EF Core Distinct + OrderBy Throws Translation Error - c#

I have been dealing with a really frustrating EF Core (newest version) error. I'm not sure at this point if I am doing something wrong or if it's a bug. Any help the community can provide would be appreciated.
The error is in regards to Entity Framework Core and translating a LINQ expression to SQL. The below code translates to SQL properly. The query variable below could potentially have a variety of Where expressions and Includes applied to it with no issue.
// This works fine
query.Select(price => new Customer {
Name = price.Payer.Name,
Code = price.Payer.Code,
City = price.Payer.City,
ParentCode = price.Payer.ParentCode,
ParentLevel = CustomerLevel.Corporate,
CustomerLevel = CustomerLevel.Payer
}).Distinct().ToListAsync();
As soon as I add a call to OrderBy, it will not evaluate. If I remove the call to Distinct, it once again works, but I can't have both. I've tried several different ways to build the expression and several workarounds that I've found around the interwebz, and nothing seems to resolve it.
// This throws error
// query is of type IQueryable<Price>
query.Select(price => new Customer {
Name = price.Payer.Name,
Code = price.Payer.Code,
City = price.Payer.City,
ParentCode = price.Payer.ParentCode,
ParentLevel = CustomerLevel.Corporate,
CustomerLevel = CustomerLevel.Payer
}).Distinct().OrderBy(cust => cust.Name).ToListAsync();
Also, placement of the OrderBy does not seem to matter. Based on what I've read, the call to Distinct removes all prior ordering, so this one is not too surprising.
// This also throws error
// query is of type IQueryable<Price>
query
.OrderBy(price => price.payer.Name)
.Select(price => new Customer {
Name = price.Payer.Name,
Code = price.Payer.Code,
City = price.Payer.City,
ParentCode = price.Payer.ParentCode,
ParentLevel = CustomerLevel.Corporate,
CustomerLevel = CustomerLevel.Payer
}).Distinct().ToListAsync();

I actually found the cause of the issue today. The ParentCode property (used at line ParentCode = price.Payer.ParentCode) is actually a property on the base class that is not mapped to the table, so EF apparently did not know what to do with it. The very strange part of this is that it worked without the OrderBy. Changing that line to ParentCode = price.Payer.CorporateCode resolved the issue.
Thanks for anyone that took a look at this.

Related

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()
}

LinqPad Entity Framework and the NullExceptionReference

this is my first post here and I'm also new to C# / OO and LinqPAD. (translate to: I may mis-use concepts or terms due to being in 'learning mode')
I have an application that I've narrowed my bug down to the following query. I decided to try out LinqPAD to solve the issue in a constrained environment.
We are using Entity Framework for our database interactions. I am using LinqPAD's "C# Program" selection.
I found a great tutorial on how to use entities in LinqPAD and this seems to be working. (Proof: 'aEntities' does not return an error like it did when the entities were not connected properly)
So let's get to the issue.
At the 'var' I get: "NullReferenceException: Object reference not set to an instance of an object."
The reason I'm here asking this is every single example I see out there uses this 'var' approach. It does not work for some reason. I've tried declaring 'qryDC', creating a new instance of it and then assigning it. (I'm feeling weak knowledge wise here, but am reading and learning.)
I have read John Saunders epic write up about this topic. (Wow man! Thanks!)
What is a NullReferenceException, and how do I fix it?
I however was not able to translate that knowledge to address my issue (learning curve).
Here is the code:
void Main(AEntities aEntities)
{
decimal fmProjectUID = 1123;
var qryDC =
from pnp in aEntities.PNonPRs
from p in aEntities.Projects
from pt in aEntities.PurchaseTypes
from wbs in aEntities.P32
from pc in aEntities.PPhases
where pnp.ProjectUID == p.ProjectUID
where p.ProjectUID == fmProjectUID
where pnp.PurchaseTypeUID == pt.PurchaseTypeUID
where pnp.P32UID == wbs.P32UID
where pt.IsNonPR == 1
orderby pt.PurchaseTypeID
select new
{
PNonPrUID = pnp.PNonPrUID
,PurchaseTypeID = pt.PurchaseTypeID
,PhaseCode = pnp.ProjectPhase.PhaseCode
,DANbr = pnp.AType.DANbr
,PreAmt = pnp.PreAmt
,POStDate = pnp.POStDate
,POFDate = pnp.POFDate
,BaseAmt = pnp.BaseAmt
,Notes = pnp.Notes
,ChangedByName = pnp.ChangedByName
};
}
Let me know if you need more info.
And thank you for your time, effort and thoughts.
You can't just arbitrarily assign arguments to the Main method and expect them to be filled in. Your aEntities variable is null when you run this, thus the null reference exception. You need to instantiate your context.
void Main()
{
decimal fmProjectUID = 1123;
var aEntities = new AEntities();
var qryDC = from pnp in aEntities.PNonPRs
from p in aEntities.Projects
from pt in aEntities.PurchaseTypes
// etc...
}
Alternatively, if you defined your connection in LINQPad and then selected said connection for the query you can omit the creation of the AEntities and just use the following.
void Main()
{
decimal fmProjectUID = 1123;
var qryDC = from pnp in PNonPRs
from p in Projects
from pt in PurchaseTypes
// etc...
}
I would also suggest you familiarize yourself with the debugger in Visual Studio and LINQPad. Had you set a breakpoint and examined the variables you would have seen that aEntities was null.

LINQ query help please, for each works?

I have a problem with some code that simply can’t work in LINQ, but it does work as a simple for..each. Any explanation and solution would be appreciated.
I have 3 classes, Users, User , UserPermissions composed as follows:
Class Users
ObservableCollection<User> GetList
Class User
Public int id {get;set;}
Public string UserName {get;set;}
Public UserPermissions Permissions {get;set;}
Class UserPermissions
Public Int ID {get;set;}
Public int ApplicationID {get;set;}
This works and returns the correct user:
Users users = new Users();
foreach (User u in users.GetList() )
{
if (u.UserName==username && u.Permissions.ApplicationID == applicationId)
{
usr = u;
break;
}
}
The linq below 'should' do the same thing, but it doesn’t. There are no errors returned or raised in the output window, and the musers variable simply doesn't exist after stepping over it. I have tried being more specific in my casts and using AsQueryable. I even tried let p=u.Permissions, using two from commands, but nothing seems to fix it.
My worry is that my other classes will suffer from this and cause issues later on as more complex queries are used.
var musers = from Users.User u in UsersList
where (u.UserName==userName)
&& (u.Permissions.ApplicationID == ApplicationId)
select u.ID;
One more bit of information the following errors too?
var t1 = UsersList.SelectMany( u => u.Permissions);
Error 1 The type arguments for method 'System.Linq.Enumerable.SelectMany(System.Collections.Generic.IEnumerable, System.Func>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
var usr = users.GetList()
.FirstOrDefault(
p => p.UserName == username
&& p.Permissions.ApplicationID == applicationId);
Should actually do it for you. FirstOrDefault can return null, if no user has been found...
Adam was spot on thank you Adam.
I have seen in the debugger that 'var found=from...' showed that found is created and contains a value at the point where the linq statement is run. However, as Adam correctly states that linq enumeration is deferred until the point you enumerate the query. The reason it all looked fine to me was that directly under the code that worked was a for loop which triggers the enumeration for THAT linq query. The others above it had no such enumeration so just looked as if they had failed silently!
I think the compiler was optimizing the function so that I could debug the linq query at the point it is in the code rather than where the enumeration occurs, nice trick but it completely wrong footed me! lol. It made me think that the query itself is evaluated to some degree even if the results aren't available until you use ToList(), Count() or other enumeration function.
for example in the code below only f3 will contain anything at all, the others are just, well, nothing because they are never enumerated!
var f1 = from .....;
var f2 = from ....;
var f3 = from ....;
do lots of other stuff, even call a function
int count = f3.Count();
What is interesting is that f1, f2 are nothing even after the line runs but f3 has a value immediately after and before the enumeration (Count) takes place, so i think compiler optimization/debug is playing a part here.
Hope this helps someone else :)

"Member access 'DataType MemberName' of 'Namespace.ClassName' not legal on type 'System.Collections.Generic.IEnumerable`1[Namespace.ClassName]."

I would love a solution to my current problem, but I would love EVEN MORE if I can understand what that error actually means.
I have LINQ to SQL classes for two tables in my DB: Providers and Assignments. I added the following member to the Provider class:
public IEnumerable<Assignment> Assignments
{
get
{
return (new linqDataContext())
.Assignments
.Where(a => a.ProviderID == this.ProviderID);
}
}
Then, I bind a GridView using a query that pulls from the parent Provider and uses the child member Assignments, like this:
protected void PopulateProviders()
{
linqDataContext context = new linqDataContext();
var list = from p in context.Providers
where (p.Assignments.Count(a => a.Category == ddlCategory.SelectedValue) > 0)
select p;
lvProviders.DataSource = list;
lvProviders.DataBind();
}
At .DataBind(), when it actually runs the query, it throws the following error:
Member access 'System.String Category' of 'Namespace.Assignment' not legal on type 'System.Collections.Generic.IEnumerable`1[Namespace.Assignment].
I've tried checking for nulls (a => a != null && a.Category ...) but that hasn't worked. I'm not sure what to try next. I think that, if I knew what the error is trying to tell me, I could find the solution. As it stands, I don't know why member access would be illegal.
That Assignments property is all wrong. First of all, property getters should not have side-effects, and more importantly, entity classes should never have reverse dependencies on the DataContext. Linq to SQL has no way to decipher this query; it's relying on a property that does all sorts of crazy stuff that Linq to SQL can't hope to understand.
Get rid of that Assignments property now. Instead of doing that, you need to write this query as a join:
int category = (int)ddlCategory.SelectedValue;
var providers =
from p in context.Providers
join a in context.Assignments
on p.ProviderID equals a.ProviderID
into g
where g.Count(ga => ga.Category == category) > 0
select p;
That should do what you're trying to do if I understood the intent of your code correctly.
One last side note: You never dispose properly of the DataContext in any of your methods. You should wrap it like so:
using (var context = new linqDataContext())
{
// Get the data here
}
I think somewhere it doesn't know that type that is in the IEnumerable. You are trying to call a method that is not part of the IEnumerable inteface.
Why don't you just move the query from the property out to the PopulateProviders() method?
Remove your custom-defined Assignments property. In the your Linq-To-SQL dbml file, create an association between Providers and Assignments, with Providers as the parent property, and ProviderID as the Participating Property for both entities. LINQ will generate a property "IEnumerable Assignments" based on matches between ProviderID using a consistent DataContext.

linq to sql throwing an exception row not found or changed

Hi I am linq to sql and i am getting the error of row not found or changed.
I am updating my table with the help of linq query then sometime it is showing this error.
I am unable to figure out this problem because sometimes it's working or sometime not.
But i am not getting any permanent solution to fix this problem.
twtmob_campainincomedetails_tb incomedetails = dataContext.twtmob_campainincomedetails_tbs.Single(twtincome => twtincome.incomeid == tempincomes);
decimal temppayout = decimal.Parse(lblpertweet.Text);
decimal temptotal = temppayout + tempmoneyearned;
incomedetails.moneyearned = Convert.ToString(temptotal);
incomedetails.tweet = temptweet + 1;
incomedetails.bonus = lblbonus.Text;
incomedetails.budurl = tempbudurl;
dataContext.SubmitChanges();
twtmob_user_tb twtuserdetails = dataContext.twtmob_user_tbs.Single(twtdetail => twtdetail.twtmobuserid == tempUserId);
{
float temppayout = float.Parse(lblpertweet.Text);
float tempoutstandingtotal = temppayout+tempoutstanding;
twtuserdetails.outstandingbalances = tempoutstandingtotal;
dataContext.SubmitChanges();
}
This is a concurrency conflict exception error message.
You can avoid it by setting to none the UpdateCheck property of all your Entity's properties. But this is equivalent to Last-In-Wins, which you may not want.
Or you can use a timestamp on the DB to check for this concurrency, it will become a property on your object and it will be used during an update to find the record (in addition to the Primary Key). If the record is not found to perform an update, a ChangeConflictExcpetion is thrown. Remember to store it somewhere if you work in disonnected mode, like in ASP.NET.
You have to take some time to get your head around that concept...
How to: Manage Change Conflicts (LINQ to SQL)

Categories

Resources