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()
}
Related
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.
After a quite complicated rework of code I stumbled over a small part of code which is not giving me the expected result. The following Linq query should query the data contained in the main object by selecting only those containing a particular string.
The main list contains about 5500 entries. after execution of the Linq query, the secondary object contains still these 5500 entries. Am I blind or already mentally deranged? The CompanyName parameter contains a company which effectively exists.
UPDATE: If the data is taken from the cache the list contains all relevant entries, just the query seems to have no effect.
public List<Account> GetAccountsByValue(string CompanyName)
{
string CacheKey = "Accounts";
ObjectCache dataCache = MemoryCache.Default;
if (dataCache.Contains(CacheKey))
{
var ResultCached = (IEnumerable<Account>)dataCache.Get(CacheKey);
var ResultCached2 = from c in ResultCached
where c.Name.Contains(CompanyName)
select new
{
c.Name,
c.Street,
c.City,
c.ID
};
var temp = ResultCached2.ToList();
return temp;
}
else
{
IList<CrmAccount> Accounts = CrmServiceAgent.GetAccounts();
var ResultNoCache = from CrmAccount f in Accounts
orderby f.DisplayName
select new Account(f);
// put the data in the cache
CacheItemPolicy cacheItemPolicy = new CacheItemPolicy();
cacheItemPolicy.AbsoluteExpiration = DateTime.Now.AddHours(4.0);
dataCache.Add(CacheKey, ResultNoCache, cacheItemPolicy);
return ResultNoCache.ToList();
}
}
The List of data which is queried looks like this (in WCF Test Client)
Everything works fine, just only the query part is not having effect which means that again all entries are returned instead of only thos containing the query parameter.
UPDATE:
Actually I get an Null Reference Exception, probably because the Name-Attribute of the first few entries is null....
This is throwing now the error:
var ResultCached3 = ResultCached.Where(c => c.Name.Contains(CompanyName)).ToList();
I have now fixed the whole thing. The data queried from the database now does not contain any null values anymore. The problem was, that mandatory fields where checked only in the frontend and not on database level. so data which usually is mandatory was imported as null values
so now in the List I have only "valid" data and now the linq query works as it should.
Thank you all for your hints ans suggestions!
kind regards
Sandro
var ResultCached2 = (from c in ResultCached
where c.Name.Contains(CompanyName)
select new
{
c.Name,
c.Street,
c.City,
c.ID
}).ToList();
I am working against a particular SDK in the .NET Framework (CRM SDK, however not critical to this question; just background) that allows me to write an expression against a data repository and return me results. The key here is that the query actually executed relies on which fields I ask for when the query is executed.
So if I run an expression that has 6000 items it might take 5 minutes to return all 120 columns and all 6000 records. However, if I only ask for 5-6 fields/columns, it might only take 5-10 seconds to return all 6000 items. The critical key here is which fields I ask for all in the object initializer affects the query run by the SDK.
To this end, I'm having trouble accessing a property of type EntityReference in my object initializer and immediately access a property on it in 1 call. Some code to explain - the problematic code below. It's also Very fast because query executed only asks for 3 properties, so data returned is small:
var results = allAccountsQuery.Select(account => new Account
{
AccountNo = account.GetAttributeValue<string>("AccountNumber"),
//AccountID NOT working!
AccountID = new EntityReference() { Id = account.GetAttributeValue<EntityReference>("AccountID").Id }.Id,
Name = account.GetAttributeValue<string>("AccountName")
}).ToList();
The offending line is as follows:
AccountID = new EntityReference() { Id = account.GetAttributeValue<EntityReference>("AccountID").Id }.Id
As you can see, I'm trying to instantiate an instance of EntityReference, initialize it, and then immediately access it's property on the back end. It fails with Object reference not set to an instance of an object
The following code works, but is super inefficient, because it brings back everything. The SDK is preforming some under the covers magic to only evaluate the query based on the fields asked for in an object initiazlizer. In this case the query is being evaluating and in the 1st iteration of the loop it brings back everything, but it works:
List<Account> allAccounts = new List<Account>();
//Super slow - framework actually gets back ALL accounts in 1st iteration
foreach (var account in allAccountsQuery)
{
Account newAccount = new Account
{
AccountNo = account.GetAttributeValue<string>("AccountNumber"),
Name = account.GetAttributeValue<string>("AccountName")
};
//WORKS: This separate instantiation and then accessing of the property works!!
//Note it is done in 2 steps outside the object initializer
//Need to see if I can do this in an object initializer instead of separately
EntityReference ef = account.GetAttributeValue<EntityReference>("AccountID");
newAccount.AccountID = ef.Id;
allAccounts.Add(newAccount);
}
So the question is how can I dictate an EntityReference instance and extract it's property Id in an object initializer so that my resulting evaluating query is small?
If I've understood you correctly you want to achieve a request comparable to the following SQL statement
SELECT AccountNumber, Id, Name FROM Accounts
A comparable request with the CRM SDK would be
AccountSet.Select(account => new Account
{
AccountNumber = account.AccountNumber,
Id = account.Id,
Name = account.Name
})
Hint: The Id or AccountId is only a Guid. It is not an EntityReference.
Therefore your call to account.GetAttributeValue<EntityReference>("AccountID") will return null. Therefore you get a NullReference when accesing the Id Property.
This results in the following QueryExpression
<?xml version="1.0" encoding="utf-8"?>
<QueryExpression xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/xrm/2011/Contracts">
<ColumnSet>
<AllColumns>false</AllColumns>
<Columns xmlns:d3p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
<d3p1:string>accountnumber</d3p1:string>
<d3p1:string>name</d3p1:string>
</Columns>
</ColumnSet>
<Criteria>
<Conditions />
<FilterOperator>And</FilterOperator>
<Filters />
</Criteria>
<Distinct>false</Distinct>
<EntityName>account</EntityName>
<LinkEntities />
<Orders />
<PageInfo>
<Count>0</Count>
<PageNumber>1</PageNumber>
<PagingCookie i:nil="true" />
<ReturnTotalRecordCount>false</ReturnTotalRecordCount>
</PageInfo>
<NoLock>false</NoLock>
</QueryExpression>
So only these three attributes are requested and included in the result
I'm not sure what is wrong with your solution but I'm pretty sure that you really want to be limiting your columns on the query expression and then using the .ToEntity<> method to cast your collection to Accounts.
QueryExpression query = new QueryExpression("account");
query.ColumnSet = new ColumnSet(new string[] { "AccountId", "AccountNumber", "AccountName" });
List<Account> allAccounts = service.RetrieveMultiple(query).Entities.Select(account => account.ToEntity<Account>()).ToList<Account>();
Thus far the best working solution I have found is to load the initial data into an anonymous type collection, and then load the actual concrete type collection based on it:
//Anonymous type collection
var accounts = allAccountsQuery.Select(account => new
{
AccountNo = account.GetAttributeValue<string>("AccountNumber"),
AccountID = account.GetAttributeValue<EntityReference>("AccountID"),
Name = account.GetAttributeValue<string>("AccountName")
}).ToList();
Now that I did the dirty work to get the EntityReference instance defined, I can access it's .Id property when populating the concrete type:
var results = accounts.Select(account => new Account
{
AccountNo = account.AccountNo,
AccountID = account.AccountID.Id,
Name = account.Name
}).ToList();
This is tested and working. the results collection has the realized .Id property defined. However as you see I still had to do it in 2 steps. It does however solve my problem because the query runs quickly. I'm still open to being able to do this in 1 object initializer because this solution can get lengthy if I have to create a sort of temporary holding tank just to then access and map it's property later.
I'd still prefer an answer that does this loading in a single object initializer.
I'm having very strange problem in this simple linq query
return (from doc in db.umDocumentActions
where doc.sysDocument.ModuleID == modid
join roleaction in db.umRoleActions on doc.DocumentActionID equals roleaction.DocumentActionID
where roleaction.RoleID == roleID
select new { doc.DocumentID, roleaction.RoleID }).Count() > 0;
When this query is called it gives invalid operation exception telling me that sequence contains no elements. It happens when there is fair amount of traffic on the site. I am using following static method to get instance of datacontext.
public static EvoletDataContext Get()
{
var connection = ProfiledDbConnection.Get(new SqlConnection(ConfigurationManager.ConnectionStrings["cnstring"].ToString()));
return new EvoletDataContext(connection);
//return DataContextUtils.CreateDataContext<EvoletDataContext>(connection);
}
I'm afraid that this method is creating problem as static methods are not thread safe. Any views?
My best guess would be that sysDocument is actually a seperate table with a reference to DocumentID. This would normally mean that there would be a related collection of sysDocuments in the document class, but I'm guessing that you've changed the cardinality to "one to one" in the Linq to SQL designer.
When using a "one to one" cardinality, Linq to SQL uses the Single() method behind the scenes to get the sysDocument. This means that if there are no related sysDocuments you will get an invalidoperation exception.
You can fix this by wither changing the cardinality in you Linq model from "one to one" to "one to many" and use the SingleOrDefault() method to get the related sysDocument from the sysDocuments collection.
If that doesn't sound appealing you can look in the database to find which document doesn't have a related sysDocument and fix it manually.
UPDATE:
Instead of basing the query off the documentActions, try basing it off the sysDocument table instead. I've had to guess at what the table will be called so this might not compile, but hopefully you get the idea:
var query = from sysDocument in db.sysDocuments
where sysDocument.ModuleID == modid
let doc = sysDocument.umDocumentAction
join roleaction in db.umRoleActions on doc.DocumentActionID equals roleaction.DocumentActionID
where roleaction.RoleID == roleID
select new { doc.DocumentID, roleaction.RoleID };
//return true if there are any results (this is more efficient than Count() > 0)
return query.Any();
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.