Why doesn't IEnumerable.Where() find my objects in DynamoDBContext.Scan() results? - c#

While using AWS DynamoDB "Object Persistence Model" in C#, ran into an interesting issue; parsing results of a Scan operation. In the code below, my entries in Datastore.userIndicators (which is a Dictionary with Lists of objects, indexed by usernames) are always empty lists.
var allIndicators = context.Scan<Indicator>();
Datastore.globalIndicators = allIndicators.Where(i => i.UserName == "*").ToList();
var userDefinedIndicators = allIndicators.Where(i => i.UserName != "*");
foreach (var username in userDefinedIndicators.Select(i => i.UserName).Distinct())
{
Datastore.userIndicators[username] = userDefinedIndicators.Where(i => i.DynamoDbRangeKey.StartsWith(username)).ToList();
}
So, for example, if I have entries in my table that include an attribute "UserName" with value "userA", when running this code the Dictionary "Datastore.userIndicators" will end up with an entry for key "userA" but the corresponding value will be an empty list.

After fiddling with this and following a hunch, I modified the assignment of
var allIndicators = context.Scan<Indicator>();
to
var allIndicators = context.Scan<Indicator>().ToList();
Voila!
It turns out (as confirmed by AWS SDK documentation), the return of the DynamoDBContext.Scan() method is lazy-loaded. Calling .ToList() forces enumeration and loads all the results.

Related

Error thrown while trying to update linq-to-sql object

I am using ASP.NET MVC 4 (aspx) using MSLinqToSQL as the connector to MYSQL database. I am trying to update the StatusID of an object based on certain criteria.
The criteria is simple, the current status should not be equal to specific statuses -> code below, all within a try - catch.
int ApplicationID = 20;
pleDBDataContext dp = new pleDBDataContext();
Recruit_Applicant_Application app = dp.Recruit_Applicant_Applications.FirstOrDefault(a => a.ApplicationID == ApplicationID);
var statuses = new List<string> { "New", "Rejected", "Archived", "Declined" };
if (statuses.Contains(app.Recruit_ApplicationStatuse.Status, StringComparer.OrdinalIgnoreCase))
{
app.ApplicationStatusID = dp.Recruit_ApplicationStatuses.FirstOrDefault(s => s.Status == "Evaluating").ApplicationStatusID;
}
dp.SubmitChanges(); //throws error here
However I keep getting this error:
Operation is not valid due to the current state of the object
The stacktrace points me to this.SendPropertyChanging(); event in the designer.cs.
The weird/odd thing is, if I remove the [if..statement] in the above, it works fine...
ApplicationStatusID is a foreign key. The relationships are strong - I've even rebuilt these.
I've even tried referencing a second Recruit_Applicant_Application object and updating that object, as I though I am manipulating the current object too much - that didn't work.
I've even tried the below with no success...
int newstatusis = dp.Recruit_ApplicationStatuses.FirstOrDefault(s => s.Status == "Evaluating").ApplicationStatusID;
int currentstatusid= dp.Recruit_Applicant_Applications.FirstOrDefault(a => a.ApplicationID == ApplicationID).ApplicationStatusID;
string currentstatus =app.Recruit_ApplicationStatuse.Status;
var statuses = new List<string> { "New", "Rejected", "Archived", "Declined" };
if (statuses.Contains(currentstatus , StringComparer.OrdinalIgnoreCase)) //theres no reference to the 'app' object at all
{
currentstatusid = newstatusid;
}
app.ApplicationStatusID = currentstatusid;
dp.SubmitChanges(); //throws error here
I am expecting the if statement to validate the current status, and only if it meets that criteria then update the status of the current object...nothing complex.
I am not an expert. It might be the case where, if the Application object has unsaved pending changes, the properties representing object relationships are not be considered reliable - particularly if a foreign key ID has been updated and EF has not yet performed the associated object lookup. This is only a guess. Someone more knowledgeable may be able to provide a better explanation.
As a workaround, perhaps you can check the ID instead of the object reference.
Something like:
var statuses = new List<string> { "New", "Rejected", "Archived", "Declined" };
var statusIds = dp.Recruit_ApplicationStatuses
.Where(s => statuses.Contains(s.Status))
.Select(s => s.StatusId)
.ToList();
if (statusIds.Contains(app.ApplicationStatusID))
{
app.ApplicationStatusID = dp.Recruit_ApplicationStatuses.FirstOrDefault(s => s.Status == "Evaluating").ApplicationStatusID;
}
Depending on how your code is structured, you may even be able to cache the status ID values in an in-memory dictionary. This would have database hits.
Even if you find another solution, it is worth considering the above approach, since ID (integer or GUID) comparisons are almost always be more efficient than string comparisons.

How to properly convert a list to IQueryable on a asp net core web API

im trying to deploy a simple search function that uses a simple tag system, probably there are better ways to bt this is the solution i landed on:
public async Task<ActionResult<IEnumerable<t_usuarios_pub>>> Gett_usuarios_pubByTag(string tag)
{
string[] TagList;
TagList = tag.Split(',');
List<t_usuarios_pub> results = new List<t_usuarios_pub>();
var pubs = from m in _context.t_usuarios_pub select m;
if (!String.IsNullOrEmpty(tag))
{
foreach (var Itag in TagList)
{
pubs = pubs.Where(s => (s.tag.Contains(Itag) && s.estatus<2)).OrderBy(x => x.estatus);
foreach(var x in pubs)
{
if (!results.Contains(x))
{
results.Add(x);
}
}
}
}
return await results.AsQueryable().ToListAsync();
}
problem is that the List results cant be converted to IQueryable, this is the error stack.
Any idea of how i can properly implement it ?
System.InvalidOperationException:
The source 'IQueryable' doesn't implement 'IAsyncEnumerable<UserPostAPI.Models.t_usuarios_pub>'.
Only sources that implement 'IAsyncEnumerable' can be used for Entity Framework asynchronous operations.
ยดยดยด
Since results is a local variable of List<> type, I don't believe you need to await the last line. It might just work with:
return results.AsQueryable();
In which case, your method may not even need to be an async method. Or, depending on what _context is, perhaps the await needs to be on the pubs filter call:
pubs = await pubs.Where(s => (s.tag.Contains(Itag) && s.estatus<2))
.OrderBy(x => x.estatus)
.ToListAsync();
Furthermore, since your method says it returns an IEnumerable<>, you probably don't need the .AsQueryable(), either:
return result;
There's also a lot of refactoring you could do -- here are just a few things you may want to consider:
Move the String.IsNullOrEmpty(tag) check to the beginning of the method as a guard.
If this is user input, trim the split tags in TagList to avoid any issues with extra whitespace, and then be sure to remove any resulting empty members.
You could put the estatus < 2 check in your query to reduce having to re-check it again for every item in pubs for every ITag.

Linq Queryable from OrganizationServiceProxy throws exception when comparing two fields

I've tried to reduce this example to remove the OrganizationServiceProxy/XrmServiceContext, but now I believe the issue is originating there. I'm pulling data from a Dynamics 365 instance using the code generated by CrmSvcUtil.exe from Microsoft. Here is the relevant snippet:
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
CrmServiceClient client = new CrmServiceClient(userName, CrmServiceClient.MakeSecureString(password), string.Empty, orgName, isOffice365: true);
using (OrganizationServiceProxy service = client.OrganizationServiceProxy)
{
XrmServiceContext crm = new XrmServiceContext(service);
var q = crm.CreateQuery<Account>();
List<Account> accounts = q.Where(x => x.AccountNumber != x.dynamics_integrationkey).ToList();
}
I get the following exception:
variable 'x' of type '{company-specific}.Account' referenced from scope '', but it is not defined
This is happening on the last line when the Linq is actually executed. It's only when it's created through the Microsoft.Xrm.Sdk.Client.OrganizationServiceContext that I get the error.
Things I've checked:
I get the error when comparing any two fields on the same object (not just Account), even when both are the same exact data type.
If I replace crm.CreateQuery... with a hand-populated List...AsQueryable() the Linq works fine
Most frustratingly, if I work through an intermediary list with List<Account> a = q.ToList(); to rasterize the result set first then this all works fine. It takes much longer to run because I've forfeited any lazy loading, but it works without error.
I've looked at other answers related to the referenced from scope '' but not defined Linq error, but they are all about malformed Linq. I know this Linq is well formed because it works fine if I change the underlying Queryable.
Clearly something is different between a List...ToQueryable() and the Microsoft.Xrm.Sdk.Linq.Query<> created by the XrmServiceContext (which inherits CreateQuery from Microsoft.Xrm.Sdk.Client.OrganizationServiceContext), but I don't know what it is.
The query you are trying to build (compare a field value with another field value on the same entity) is not possible in Dynamics.
The LINQ Provider still uses QueryExpression under the hood, meaning that your LINQ condition will be converted in a ConditionExpression, and that comparison is not possible.
Similar questions:
https://social.microsoft.com/Forums/en-US/d1026ed7-56fd-4f54-b382-bac2fc5e46a7/linq-and-queryexpression-compare-entity-fields-in-query?forum=crmdevelopment
Dynamics QueryExpression - Find entity records where fieldA equals fieldB
This morning I realized that my answer contained the flaw of comparing two fields against each other in a LINQ query. My bad. Thank you Guido for calling that out as impossible.
I generate my proxy classes with a 3rd party tool, so I'm not familiar with the XrmServiceContext class. But, I have confirmed that XrmServiceContext inherits from Microsoft.Xrm.Client.CrmOrganizationServiceContext, which inherits from Microsoft.Xrm.Sdk.Client.OrganizationServiceContext
I use OrganizationServiceContext for LINQ queries.
If you retrieve all the accounts first by calling ToList() on the query, you can then do the comparison of the two fields. If you have too many Accounts to load all at once, you can page through them and store the mismatched ones as you go.
And, I would probably retrieve a subset of the fields rather than the whole record (as shown).
var client = new CrmServiceClient(userName, CrmServiceClient.MakeSecureString(password), string.Empty, orgName, isOffice365: true);
using (var ctx = new OrganizationServiceContext(client))
{
var q = from a in ctx.CreateQuery<Account>()
where a.AccountNumber != null
&& a.dynamics_integrationkey != null
select new Account
{
Id = a.AccountId,
AccountId = a.AccountId,
Name = a.Name,
AccountNumber = a.AccountNumber,
dynamics_integrationkey = a.dynamics_integrationkey
};
var accounts = q.ToList();
var mismatched = accounts.Where(a => a.AccountNumber != a.dynamics_integrationkey).ToList()
}

How to ignore Symbols in string comparison and dictionary lookup

It looks like I have a problem with the symbols encodings between string built by my program and string retrieved from another datasource.
Here is a .NET Fiddle and here is the explanation:
var context = new List<Foo>
{
new Foo { Name = "SoW.Probing.4GEPCCore.CaptSite[1].S1U" },
new Foo { Name = "SoW.Probing.4GEPCCore.CaptSite[2].S1U" },
new Foo { Name = "SoW.Probing.2G3GPSCore.CaptSite[1].GnGpU" },
new Foo { Name = "SoW.Probing.2G3GPSCore.CaptSite[2].GnGpU" }
};
var nameToCheckPresence = GetStringFromAnotherDataSource(); // the value of the string is for example: "SoW.Probing.4GEPCCore.CaptSite.S1U"
nameToCheckPresence = nameToCheckPresence.Replace("CaptSite", "CaptSite[1]");
var foo = context.FirstOrDefault(f => f.Name == nameToCheckPresence); // Should return an object since one object does have that name
My problem is that foo is null. It works if I use this code line:
var foo = context .FirstOrDefault(f => CultureInfo.CurrentCulture.CompareInfo.Compare(f.Name, nameToCheckPresence , CompareOptions.IgnoreSymbols) == 0);
So clearly, I have a problem with symbols encoding (the .? the [ ]?). My true problem is that later, I am doing the same thing with a dictionary. The hashcode of the strings are different and the dictionary lookup also failed:
var dictionary = context.ToDictionary(f => f.Name);
var foo = dictionary[nameToCheckPresence]; // Should return the object but failed and throw a KeyNotFoundException
Is there a way to change the string symbols encoding in a global manner in the application? (WPF application in my case)
As the context can be very large, it is planed to use a Dictionary also in the first place. So if you provide me a solution that only works with Dictionary, it is not a problem.
Just for the record, the datasource is a SQLite database in which is a copy of the data of a MySQL database filled by another WPF application (running on the same computer, no specific culture setup). Finally, the nameToCheckPresence is extracted from a larger string by ANTLR4CS.
This is not a satisfactory answer, but that's all I find to solve the problem. Instead of looking into the dictionary through the indexor, I am doing a linq query:
dictionary.FirstOrDefault(pair => CultureInfo.CurrentCulture.CompareInfo.Compare(pair.Key, localFactName, CompareOptions.IgnoreSymbols) == 0).Value;
But doing this, I lost all the benefit of the dictionary access complexity. If anyone has a better solution, I will take it!

Querying Complex Object using Lambda

I am querying a complex c# object that describes a Shift Roster. This object is populated from a web service and is an object from a third party software supplier.
My task is to build a gridview from data from this third party product. The object is an that contains other objects nested inside most of which are an array of objects.
I am using Lambda Expressions to navigate down the object, but I am struggling to get values out, and I am also wondering whether the expression I am writing could be better. Here is one example where I am trying to get an employee name out:
var employeeDetails = employeeRoster.PathsRosters.SelectMany(a => a.EmployeesRosters);
var empName = employeeDetails.Select(b => b.Resource.Name).ToList();
foreach (string nam in empName)
{
string var = nam;
}
employeeRoster is the object that I get back from the web service and PathRosters is an array. Then I am querying the result again and casting to a List just to get back the employee name.
Is there a better way of doing this? Unfortunately, I cannot serialize to XML as I have been told it has to be done in memory on the object.
Instead of making two calls, you can do the same with one:
var employeeNames = employeeRoster.PathsRosters.SelectMany(a => a.EmployeesRosters)
.Select(b => b.Resource.Name).ToList();
Also, your foreach loop doesn't do anything. I don't see the point in having this.
var empNames = employeeRoster.PathsRosters.SelectMany(a => a.EmployeesRosters)
.Select(b => b.Resource.Name).ToList();

Categories

Resources