Entity Framework join statement in a function - c#

I'm trying to create a function to grab a List<>() of type "tableContact" which is an entity (sort of like when you do a simple select statement: query.ToList();)
But this is becoming frustrating because I kept getting "null object reference" errors. For some reason "join" statement doesn't work, so I tried an alternate linq statement.
I have an Object2Contacts table that I want to LEFT JOIN and I'm looking up my Object and trying to see all the contacts for this Object. I also can't figure out how to change an "anonymous type" back into an entity table.
Also not every object has contacts, so sometimes null List<> should be returned.
public List<tableContact> getContactsForObject(int oid)
{
if (oid > 0)
{
var query = (from s in entities.tableContacts
from o in entities.tableObject2Contacts
where s.contact_id == o.contact_id
where o.object_id == oid
select new { s });
if (query != null)
{
IEnumerable<tableContact> e = (IEnumerable<tableContact>)query.ToList();
return (List<tableContact>)e;
}
}
return new List<tableContact>();
}
I want to then loop through the object returned... e.g.:
foreach ( tableContact c in MyList){
WriteLine(c.Name);
}
EDIT
I also tried:
List<tableContacts> contacts = (from s in entities.tableContacts
join o in entities.tableObject2Contacts
on s.contact_id equals o.contact_id
where o.object_id == oid
select s).ToList();
Then I can't convert it back into a List and still "null reference".
EDIT 2
Yes the query I am running may definitely bring in an "empty" list. I don't mind empty lists, but it shouldn't give "object null reference."

I would write the join statement a little bit different:
var query = (from s in entities.tableContacts
join o in entities.tableObject2Contacts
on new { ContactID = s.contact_id, ObjectID = oid }
equals new { ContactID = o.contact_id, ObjectID = o.objectID }
into oTemp
from o in oTemp.DefaultIfEmpty()
select new { s });
This would be a proper left-join using linq.
Maybe your NullReferenceException is caused by a wrong join or something.

Nevermind, The "I also tried this" is actually correct code, but for some reason I hadn't established my entities properly so that was returning null.
Although I'm still super confused about "anonymoustypes" I wouldn't know how to make a function that returns "anonymoustype".

Related

Multiple joins with multiple on statements using Linq Lambda expressions [duplicate]

Suppose I have a list of {City, State}. It originally came from the database, and I have LocationID, but by now I loaded it into memory. Suppose I also have a table of fast food restaurants that has City and State as part of the record. I need to get a list of establishments that match city and state.
NOTE: I try to describe a simplified scenario; my business domain is completely different.
I came up with the following LINQ solution:
var establishments = from r in restaurants
from l in locations
where l.LocationId == id &&
l.City == r.City &&
l.State == r.State
select r
and I feel there must be something better. For starters, I already have City/State in memory - so to go back to the database only to have a join seems very inefficient. I am looking for some way to say {r.City, r.State} match Any(MyList) where MyList is my collection of City/State.
UPDATE
I tried to update based on suggestion below:
List<CityState> myCityStates = ...;
var establishments =
from r in restaurants
join l in myCityStates
on new { r.City, r.State } equals new { l.City, l.State } into gls
select r;
and I got the following compile error:
Error CS1941 The type of one of the expressions in the join clause is incorrect. Type inference failed in the call to 'Join'.
UPDATE 2
Compiler didn't like anonymous class in the join. I made it explicit and it stopped complaining. I'll see if it actually works in the morning...
It seems to me that you need this:
var establishments =
from r in restaurants
join l in locations.Where(x => x.LocationId == id)
on new { r.City, r.State } equals new { l.City, l.State } into gls
select r;
Well, there isn't a lot more that you can do, as long as you rely on a table lookup, the only thing you can do to speed up things is to put an index on City and State.
The linq statement has to translate into a valid SQL Statement, where "Any" would translate to something like :
SELECT * FROM Restaurants where City in ('...all cities')
I dont know if other ORM's give better performance for these types of scenarios that EF, but it might be worth investigating. EF has never had a rumor for being fast on reads.
Edit: You can also do this:
List<string> names = new List { "John", "Max", "Pete" };
bool has = customers.Any(cus => names.Contains(cus.FirstName));
this will produce the necessary IN('value1', 'value2' ...) functionality that you were looking for

"Invalid Where condition" when I try to search for a value that is accented ignoring accents in CRM 2011 with LINQ

I 'm trying to do a search for a contact. For example value "Café " which is stored in the name field , but when I search like "cafe" does not return any record .
I tried to do the following
using (ServiceContext svcContext = new ServiceContext(_serviceProxy))
{
var query_where3 = from c in svcContext.ContactSet
join a in svcContext.AccountSet
on c.ContactId equals a.PrimaryContactId.Id
where c.FullName.Normalize(NormalizationForm.FormD).Contains("Café")
select new
{
account_name = a.Name,
contact_name = c.LastName
};
}
and appear the Exception with message saying "Invalid 'where' condition. An entity member is invoking an invalid property or method"
You can't use that functions on LinQ-CRM, the correct way to do the query is:
c.FullName == "someString" or c.FullName.equals("someString").
This is because you can't use functions or transformations on the left condition. You must use the attribute itself.
Your query will look like:
using (ServiceContext svcContext = new ServiceContext(_serviceProxy))
{
var query_where3 = from c in svcContext.ContactSet
join a in svcContext.AccountSet
on c.ContactId equals a.PrimaryContactId.Id
where c.FullName == "Café" || c.FullName == "Cafe"
select new
{
account_name = a.Name,
contact_name = c.LastName
};
}
You can't really deal with the accents with Linq to SQL in general ... and you are even more limited with what you can do with Linq to CRM. You cant modify the DB; unless you don't care about being supported. Then you could do something like : MAD suggested and to a db alter.
ALTER TABLE Name ALTER COLUMN Name [varchar](100) COLLATE SQL_Latin1_General_CP1_CI_AI
I personally would not recommend that.
The best that I can come up with is getting the data as close as you can and filtering it from there inside a list or something similar.
I have to do it all the time and it is a pain (and adds more overhead) but there is not really another workaround that I have found.
//declare a dictionary
Dictionary<string, string> someDictionary = new Dictionary<string, string> ();
using (ServiceContext svcContext = new ServiceContext(_serviceProxy))
{
var query_where3 = from c in svcContext.ContactSet
join a in svcContext.AccountSet
on c.ContactId equals a.PrimaryContactId.Id
where c.FullName.Contains("Caf")
select new
{
account_name = a.Name,
contact_name = c.LastName
};
}
//then
foreach(var q in query_where3)
{
if(string.IsNullOrEmpty(account_name)==false && string.IsNullOrEmpty(contact_name)==false)
{
someDictionary.Add(account_name, contact_name);
}
}
//then you can add the .Normalize(NormalizationForm.FormD) to your dictionary
Hope that helped.
Its all about
.Normalize(NormalizationForm.FormD)
, probably EF does not knows how to handle this method. Remove it and test just with
c.FullName.Contains("Café")
------------------------------------------------- Added in 2015-01-30 --------------------------------------------------
So man, the unique solution i can think about is list before you do the where condition. This way the you can use the normalize once this will be handled by linq 2 objects. try:
(from c in svcContext.ContactSet join a in svcContext.AccountSet
on c.ContactId equals a.PrimaryContactId.Id
select new {a=a,c=c} ).ToList()
.Where(c=>c.FullName.Normalize(NormalizationForm.FormD).Contains("Café"))
.Select( x=> select new {
account_name = x.a.Name,
contact_name = x.c.LastName
};)
But that way can cause some overhead given that linq 2 obejects runs in application server memory, not in database server.
CRM's LINQ translator cannot handle the .Equals() method.
on c.ContactId equals a.PrimaryContactId.Id
Change the above line to below line.
on c.ContactId == a.PrimaryContactId.Id

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

LINQ to Sql is empty and throws Object reference not set to an instance of an object. error

I use the following query to get a result set
var overlaps = from s in db.signups
join u in db.users on new { userid = s.userid } equals new { userid = u.studentid }
join a in db.activities on new { activityid = s.activityid } equals new { activityid = a.id }
where
s.userid != Convert.ToInt32(Request.Cookies["studentid"].Value) &&
(from signups in db.signups
where
signups.userid == Convert.ToInt32(Request.Cookies["studentid"].Value)
select new
{
signups.activityid
}).Contains(new { s.activityid })
orderby
u.studentid
select new
{
a.name,
u.firstname,
u.lastname,
u.studentid,
u.email
};
I'm pretty new to LINQ so I actually wrote the Sql and then used Linqer to generate the LINQ, so if this can be done more efficiently then please let me know. Having said that, this is not the problem.
The problem is that when I do
foreach(var overlap in overlaps)
{
//do something
}
it throws the object reference not set error. This is being run in an MVC 3 application.
However, when this is run in a Console application, it runs without issue; it just returns no results. I've tried using DefaultIfEmpty but just can't find anything that addresses how to use this with anonymous types.
So
... is my approach correct?
If not, what should I do differently?
Thanks, in advance.
I don't know if this is your problem, but your join syntax is really weird.
You don't have to build anonymous types here, just compare directly.
join u in db.users on s.userid equals u.studentid
join a in db.activities on s.activityid equals a.id
Same with this:
select new
{
signups.activityid
}).Contains(new { s.activityid })
Can be just:
select signups.activityid).Contains(s.activityid)
And why in the world do you want to redo all the work to convert the cookie parameter to an int over and over?
var studentId = Convert.ToInt32(Request.Cookies["studentid"].Value);
//use this instead now in the query, dont repeat yourself
To your first question, you are appropriately worried about how messy the linq is... we often will take messy linq and just do a dataContext.ExecuteQuery or .ExecuteCommand because one of linq's major short falls is their ability to optimize complex queries as well as you could.
To get an idea of how badly linq has botched your query there, run it through the query analyzer and compare it to what you started with... My guess is that it will be comical!
ICBW, but I would try casting overlaps, something like:
foreach(OverlapType overlap in overlaps as IEnumerable<OverlapType>)
{
//stuff
}
This of course means you will need to make a model of the object you are getting from the database. But really, you should have one anyway, that is the whole premise behind MVC (Model View Controller)
Well, first off, I'm pretty sure you can simplify the first few lines down to :
from s in db.signups
join u in db.users on s.userid equals u.studentid
join a in db.activities on s.activityid equals a.id
in fact, if you've defined foreign keys on those properties, you don't need the joins at all -- LINQ will handle them for you automatically: Write s.User.firstname instead of u.firstname etc.
As for your main problem, check all the component of that query, mainly "db" and "Request" (and how exactly does Request.Cookies work in a console application?)

linq simple query with 2 tables

I am trying to get a simple join query from 2 tables , they have no relationship , so far i got this code but there is an error that i dont get it , please help
var query =
from p in db.Productos
from s in db.Stocks
where p.Nombre == "Suaje" && p.TipoProducto == tipo
where p.RecID == s.IDProducto
select new { s.Largo, s.Ancho };
Your query is well formed. I believe the error comes from how you're using the query object.
Are you returning it? What is the return type of the method?
Good practice dictates that you do not return anonymous types (or generic types with anonymous type parameters) from any method.
If all else fails, remove the var keyword and work from there.
Are you missing the 'and'?
and p.RecID == s.IDProducto
Consider writing your query like this:
var results = from p in db.Productos
join s in db.Stocks
on p.RecID equals s.IDProducto
where p.Nombre == "Suaje" && p.TipoProducto == tipo
select new { s.Largo, s.Ancho };

Categories

Resources