Dynamics CRM: Problem with Composite Key Join in LINQ - c#

I have following query in LINQ to LEFT JOIN same entity:
query = from a in orgSvcContext.CreateQuery("entity1")
join b in orgSvcContext.CreateQuery("entity1")
on new { v1 = a["field1"], v2 = a["field2"] } equals new { v1 = b["field2"], v2 = b["field1"] } into gr
from c_joined in gr.DefaultIfEmpty()
where c_joined["field0"] == null && (a["field1"].Equals(new Guid(#param)) || a["field2"].Equals(new Guid(#param)))
select a;
It complains the following error:
invalid 'join' condition. an entity member is invoking an invalid
property or method
I refer from here: https://learn.microsoft.com/en-us/dotnet/csharp/linq/join-by-using-composite-keys
Anything wrong with my JOIN? I have no problem if I just use
on a["field1"] equals b["field2"]
Thanks in advance.

The new { v1 = a["field1"], v2 = a["field2"] } construct cannot be resolved to an attribute name.
The LINQ implementation for Dynamics CRM is very limited as it attempts to convert LINQ expressions into QueryExpression queries. These queries in turn only implement a basic subset of the SQL language.
When joining two entities make shure that after the on the left and right side can be resolved to existing lookup attributes of both entities.
Still keep in mind not all QueryExpression capabilities can be used in LINQ queries.

Related

CRM LINQ Composite join "The method 'Join' is not supported" error

Im getting a "The method 'Join' is not supported" error... Funny thing is that i simply converted the 1st LINQ into the 2nd version and it doesnt work...
What i wanted to have was LINQ version #3, but it also doesnt work...
This works
var query_join9 = from s in orgSvcContext.CreateQuery(ServiceAppointment.EntityLogicalName)
join b in orgSvcContext.CreateQuery(bh_product.EntityLogicalName)
on s["bh_contract"] equals b["bh_contract"]
where ((EntityReference)s["bh_contract"]).Id == Guid.Parse("09BDD5A9-BBAF-E111-A06E-0050568B1372")
select new
{
Events = s,
Products = b
};
This doesn't
var query_join9 = from s in orgSvcContext.CreateQuery(ServiceAppointment.EntityLogicalName)
join b in orgSvcContext.CreateQuery(bh_product.EntityLogicalName)
on new { contractid = s["bh_contract"] }
equals new { contractid = b["bh_contract"] }
where ((EntityReference)s["bh_contract"]).Id == Guid.Parse("09BDD5A9-BBAF-E111-A06E-0050568B1372")
select new
{
Events = s,
Products = b
};
Also, this doesn't, which is a composite join and what i really aim for
var query_join9 = from s in orgSvcContext.CreateQuery(ServiceAppointment.EntityLogicalName)
join b in orgSvcContext.CreateQuery(bh_product.EntityLogicalName)
on new { contractid = s["bh_contract"], serviceid = s["serviceid"] }
equals new { contractid = b["bh_contract"], serviceid = s["serviceid"] }
where ((EntityReference)s["bh_contract"]).Id == Guid.Parse("09BDD5A9-BBAF-E111-A06E-0050568B1372")
select new
{
Events = s,
Products = b
};
I tried early binding and still doesnt work...
var query_join9 = from s in orgSvcContext.CreateQuery<ServiceAppointment>()
join b in orgSvcContext.CreateQuery<bh_product>()
on new { foo = s.bh_contract.Id }
equals new { foo = b.bh_Contract.Id }
where s.bh_contract.Id == Guid.Parse("09BDD5A9-BBAF-E111-A06E-0050568B1372")
select new
{
Events = s,
Products = b
};
stil not working
var query_join9 = from s in orgSvcContext.CreateQuery<ServiceAppointment>()
join b in orgSvcContext.CreateQuery<bh_product>()
on new { s.bh_contract.Id, s.ServiceId }
equals new { b.bh_Contract.Id, ServiceId = b.bh_Service }
where s.bh_contract.Id == Guid.Parse("09BDD5A9-BBAF-E111-A06E-0050568B1372")
select new
{
Events = s,
Products = b
};
But im simply trying to do the example(s) here How to do joins in LINQ on multiple fields in single join
What am i missing?
Thanks in advance
While I'm not entirely sure which CRM you're using, I think you're misunderstanding something.
In order for a LINQ query to work, there needs to be a LINQ provider for the underlying data source -- the bit of code responsible for translating chain of e.g. Join, Where, operator usage, etc, etc, into the query API of the data source. This might be SQL, some custom query language, or some chain of methods.
Two LINQ providers (such as, one for LINQ to DataSet and some custom provider you've written yourself) don't have to support the same methods and other code. The precise subset of LINQ methods (and/or other embedded statements) a LINQ provider supports is dependent on its implementation.
Looking at it like that, it's not that surprising that the LINQ provider you're using doesn't seem to comprehend the standard syntax for joins using multiple fields, or doesn't seem to comprehend the usage of anonymous types at all.
My advice is to search the documentation of the supplied LINQ provider to see which query operations it supports (perhaps there is a note about this specific mode of query not being supported). Failing that, you'll have to resort to some sort of other query -- one not involving an equijoin. Perhaps your best option is to perform the joins separately, and then intersect the two result groups. It really depends on the specifics of the case.
Have you looked at the MSDN samples. There are some multiple-column join examples there:
using (ServiceContext svcContext = new ServiceContext(_serviceProxy))
{
var list_join = (from a in svcContext.AccountSet
join c in svcContext.ContactSet
on a.PrimaryContactId.Id equals c.ContactId
where a.Name == "Contoso Ltd" && <<--- multiple join here
a.Address1_Name == "Contoso Pharmaceuticals"
select a).ToList();
foreach (var c in list_join)
{
System.Console.WriteLine("Account " + list_join[0].Name
+ " and it's primary contact "
+ list_join[0].PrimaryContactId.Id);
}
}
This other thread might be relevant

converting int to string in linq to entites

My Code is :
var currency = (from ac in db.shop
join cur in db.books
on ac.CODE equals cur.CODE.ToString() //here is the Error
// because "ac.code is type strig" & "cur.code is type long
where ac.ID == merchantId
select new Currency()
{
ShopCode = ac.CODE,
PosCode = ac.POSCODE,
}).ToList();
I found that .ToString(), SqlFunctions.StringConvert(long) are not working in the join query conditions but working in 'select' area in the query.
However Devart.Data.Oracle.Entity.OracleFunctions.ToChar((long)cur.CODE) is working fine. Since I am using entity framework it shouldn't have problems with particular DB types (i.e. oracle or sql server). It should work even I change from oracle to sql in future.
Please give me a solution for this.
Did you try casting on this.
Try : ac.CODE equals (string)cur.CODE
You can create a VIEW Currency on the database and perform the query on the view.
Here is the list of supported method for Linq to Entities, if conversion is not supported you can not execute it.
http://msdn.microsoft.com/en-us/library/bb738681.aspx
The problem is, that EF is trying to convert whole your expression into T-SQL query.
So it will look similar to this:
select ac.CODE, cur.CODE from shop ac
inner join books cur on ac.CODE = cur.CODE
Here is your problem. CODE fields have diffent types and server can't join on them.
In T-SQL you can use CAST, but since EF don't support such operation you can't do anything.
And afterall, why do you store those codes in string? If you have such a query, then in most cases there is some problem with your DB schema.
I would suggest you to look at the schema and refactor it, so CODE is always of type long. Then everything will work.
If you still really want to use different types for you columns. You can look at this question, to see how to execute CAST Convert String to Int in EF 4.0
this should solve your problem:
var currency = (from ac in db.shop
join cur in db.books
let codestr = cur.CODE.ToString()
on ac.CODE equals codestr
where ac.ID == merchantId
select new Currency()
{
ShopCode = ac.CODE,
PosCode = ac.POSCODE,
}).ToList();

join using lambda expressions and retrieving the data

I have a members table with columns
member_Id,
member_Lastname,
member_Firstname,
member_Postcode,
member_Reference,
member_CardNum,
and i have another table mshipoptions with columns
mshipoption_id
mshiptype_id
and i have another table mshiptypes
mshiptype_id
mshiptype_name
another table memtomship
memtomship_id
mshipoption_id
member_id
and my entity name is eclipse
at the form load i am filling the datagrid view by using the below method....
private void reportmembers()
{
MemberControlHelper.Fillmembershiptypes(cbGEMembershiptype);
var membersreport = from tsgentity in eclipse.members
join memtomships in eclipse.membertomships on tsgentity.member_Id equals memtomships.member_Id
join mshipoptiions in eclipse.mshipoptions on memtomships.mshipOption_Id equals mshipoptiions.mshipOption_Id
join mshiptypes in eclipse.mshiptypes on mshipoptiions.mshipType_Id equals mshiptypes.mshipType_Id
select
new {
tsgentity.member_Id,
tsgentity.member_Lastname,
tsgentity.member_Firstname,
tsgentity.member_Postcode,
tsgentity.member_Reference,
tsgentity.member_CardNum,
mshiptypes.mshipType_Name,
};
if (txtfirstname.Text != "")
{
dgvmembersrep.DataSource = membersreport.Where(t => t.member_Firstname == txtlastname.Text).ToList();
}
if (txtcardnum.Text != "")
{
dgvmembersrep.DataSource = membersreport.Where(a => a.member_CardNum == txtcardnum.Text).ToList();
}
}
that was fine,...
My problem is here , i have one comboboxsay (cbgemembershiptype)......
when ever the user select the membership type in (cbgemembershiptype) i want retrieve the details of members those who have that membership type....
(Your question is unclear in terms of whether the query you've got already does what you want. I'm assuming it does.)
It's unclear why you'd want to convert this query into one using lambda expressions. It's certainly possible, but each join would introduce a new range variable - by the end, the strictly literal translation would end up with a select involving something like
member_Id = a.b.c.member_Id
... it wouldn't be very readable at all.
There are ways of improving it if you're writing it out by hand, but it still wouldn't be as clear as the query expression.
You should definitely know both forms, and write using whichever form is clearest for the query in question - which in this case is definitely the query expression form.
For more information about how query expressions are translated, see my Edulinq post on that topic.

Why does this LINQ join query work, but this other one doesn't?

I've written two LINQ queries using the join method. Essentially, if I switch the order of the objects to be joined, the query no longer works and throws the error:
"Unable to create a constant value of type 'Domain.Entities.UsersSitesRole'. Only primitive types ('such as Int32, String, and Guid') are supported in this context."
var foo2 = //works
from p in privilegesForUser
join c in repository.Child on p.SiteId equals c.Child_SiteID
select new { ChildID = c.Child_ChildID, name = c.Child_FirstName, site = c.Child_SiteID, p.PrivilegeLevel };
var foo3 = //throws exception
from c in repository.Child
join p in privilegesForUser on c.Child_SiteID equals p.SiteId
select new { ChildID = c.Child_ChildID, name = c.Child_FirstName, site = c.Child_SiteID, p.PrivilegeLevel };
The object privilegesForUser is a List of entities derived from my Entity Framework context (UsersSiteRole), and repository.Child is an IQueryable<Child> from my EF context as well.
This is caused by the way EF parses the expression tree it gets in the extension methods.
There are many cases, where a query is logically correct and executes just fine on IEnumerable (Linq to Objects) but fails in Linq to Entities. Basicly, it's pretty much impossible to compile just any logical expression tree into proper SQL statements (SQL is not ideal and far from object oriented world) and this is the case where EF gives up. In time, you get used to understanding what works and what doesn't.

Entity Data Model, Dynamic Linq, multiple table dynamic Where clause, strongly typed return types

When writing a method for an oData service I have the below linq, for which I need to have a dynamic "where" clause to filter the results (the "new"s in the joins are for composite PKs in the Entity Data Model):
var query = from pl in CurrentDataSource.ProductListing
join pla in CurrentDataSource.ProductListingAttribute
on new {pl.ProductID, pl.WebCategoryID, pl.ParentProductID}
equals new {pla.ProductID, pla.WebCategoryID, pla.ParentProductID}
join att in CurrentDataSource.Attribute
on pla.AttributeID
equals att.AttributeID
join attItem in CurrentDataSource.AttributeItem
on pla.AttributeItemID
equals attItem.AttributeItemID
select pl;
My Linq is not very good and I'm trying to use the DynamicQueryable class to generate a "where" clause at runtime (it's built from various variables):
var returnData = query.Where(whereClause);
Since the "where" clause filters on values in the Attribute and AttributeItem entities it invariably contains things like
"((Attribute.Value='foo' AND AttributeItem.Value='bar')
OR
(Attribute.Value='sna' AND AttributeItem.Value='fu'))"
which will fail at runtime since "No property or field 'Attribute' exists in type 'ProductListing'".
I have tried to build an anonymous type in the "select" which contains all elements of ProductListing entity and those from Attribute and AttributeItem which I require to filter by, but I need a strongly typed entity of type "ProductListing" to return from the method call.
Can ANYONE please help?? Should I be using dynamic Joins instead of dynamic Wheres? Is there a way of Where-ing against entities that you're not Selecting? Should I be selecting an anonymous type /"let" and building a strongly typed entity afterwards?
Please, any help is massively appreciated.
rposbo
The solution to my particular query was to do this:
var query = from pl in CurrentDataSource.ProductListing
join pla in CurrentDataSource.ProductListingAttribute
on new {pl.ProductID, pl.WebCategoryID, pl.ParentProductID}
equals new {pla.ProductID, pla.WebCategoryID, pla.ParentProductID}
join att in CurrentDataSource.Attribute
on pla.AttributeID
equals att.AttributeID
join attItem in CurrentDataSource.AttributeItem
on pla.AttributeItemID
equals attItem.AttributeItemID
select new
{
ProductListing = pl,
att.AttributeName,
attItem.AttributeValue
};
var returnData = query.Where(whereClause).Select(o => o.ProductListing);
i.e., select an anonymous type containing a concrete type, apply the where clause to that then select only the concrete type from the result.

Categories

Resources