LINQ to SQL - Restrict Relationship by Condition (One to Many) (1 - M) - c#

OK, first off, I'm brand new to LINQ2SQL so excuse my ignorance.
I've done some searches, but after 1 hour, I decided to come here.
Here goes:
I'm trying to do something that I think is very simple. It could just be that I don't understand. I'm using LINQ 2 SQL, via the VS designer. I have 2 tables: Clients and Categories. A client can have multiple categories.
I have a very simple query to find clients:
Client c = db.Clients.SingleOrDefault(client => client.ID == id);
What I want to do is modify this so that the collection: c.Categories, will only contain a subset of that client's categories.
This is what I've tried:
Client c = db.Categories.Where(cat => cat.IsActive == true).Select(cat.Clients).SingleOrDefault(client => client.ID == id);
I get an error reporting that more than one client is being returned.
Am I missing something? Is this not something that LINQ is designed to do?
Am I supposed to use the first query then do another query specifically on Categories when I need that list??
c.Categories.Categories.Where(cat => cat.IsActive == true)
Thank you in advance.

SingleOrDefault() is for when there's ONLY one result. It seems like there's more than one record that has that id. Try using FirstOrDefault() instead.
Client c = db.Categories.Where(cat => cat.Name == "Name").Select(cat.Clients).FirstOrDefault(client => client.ID == id);

The problem is that the Categories.Where part in combination with the Select, is returning a collection of collections. What you may need to do is either use SelectMany, or use Single (or SingleOrDefault, or First or FirstOrDefault) instead of Where.
Examples:
Client c = db.Categories.Where(cat => cat.IsActive)
.SelectMany(cat.Clients)
.SingleOrDefault(client => client.ID == id);
or
Client c = db.Categories.Single(cat => cat.IsActive)
.Clients
.SingleOrDefault(client => client.ID == id);

Related

Filtering Related Entites with Entity Framework

According to this StackOverflow answer:
Linq to Entities - how to filter on child entities
you should be able to filter down the list of related entities in Entity Framework by utilizing a projection, like I've done here:
Company company = _context.Company
.Where(g => g.CompanyId == id)
.Select(comp => new
{
group = comp,
operators = comp.Operator,
formFamilies = comp.FormFamily.Where(ff => ff.IsActive ?? false)
}).AsEnumerable().Select(i => i.group).FirstOrDefault();
To give a quick overview of what I'm trying to obtain here, I'm trying to get a list of all of the active form families associated with this company object, however, whenever I restrict the results in any way, the result set is empty.
If the line were formFamilies = comp.FormFamily then it returns two results, one active one inactive
If the line is formFamilies = comp.FormFamily.Where(ff => true) then it returns nothing
If the line is formFamilies = comp.FormFamily.OrderBy(ff => ff.FormFamilyId) then it returns nothing.
Any sort of modification that I do to comp.FormFamily means the result set returns nothing, I've dug through the deepest sections of SA to try to find a solution, and tried every solution I've found, but nothing seems to cause this list to return anything.
Assuming that Company and FormFamily entities has one to many relationship I would suggest to use a join statement.Something like this should give you what you are looking for.
var company = from c in _context.Company
join f in _context.FormFamily
on c.Id equals f.CompanyId
where c.Id == id
select new Company()
{
Id = c.Id,
operators = c.Operator.ToList(),
formFamilies = c.FormFamily.Where(x=>x.IsActive ==
false).ToList()
} .FirstOrDefault();
Hope this helps.
I didn't quite understand what is your query is supposed to do. But it seems to me that you cannot just call Select method on another Select result method.
Anyway, you could simply use Include methods instead of projecting.
var company = _context.Company
.Where(c => c.Id == id)
.Include(c => c.FormFamily).Where(ff => ff.IsActive ?? false)
.ToList();
Did not test it. To prove it works or not be sure put an entity model in the question. Then I may produce more accurate answer.

Entity Framework (using In and Select Distinct)

I am relatively new to Entity Framework 6.0 and I have come across a situation where I want to execute a query in my C# app that would be similar to this SQL Query:
select * from periods where id in (select distinct periodid from ratedetails where rateid = 3)
Is it actually possible to execute a query like this in EF or would I need to break it into smaller steps?
Assuming that you have in your Context class:
DbSet<Period> Periods...
DbSet<RateDetail> RateDetails...
You could use some Linq like this:
var distincts = dbContext.RateDetails
.Where(i => i.rateId == 3)
.Select(i => i.PeriodId)
.Distinct();
var result = dbContext.Periods
.Where(i => i.Id)
.Any(j => distincts.Contains(j.Id));
Edit: Depending on your entities, you will probably need a custom Comparer for Distinct(). You can find a tutorial here, and also here
or use some more Linq magic to split the results.
Yes, this can be done but you should really provide a better example for your query. You are already providing a bad starting point there. Lets use this one:
SELECT value1, value2, commonValue
FROM table1
WHERE EXISTS (
SELECT 1
FROM table2
WHERE table1.commonValue = table2.commonValue
// include some more filters here on table2
)
First, its almost always better to use EXISTS instead of IN.
Now to turn this into a Lambda would be something like this, again you provided no objects or object graph so I will just make something up.
DbContext myContext = this.getContext();
var myResults = myContext.DbSet<Type1>().Where(x => myContext.DbSet<Type2>().Any(y => y.commonValue == x.commonValue)).Select(x => x);
EDIT - updated after you provided the new sql statement
Using your example objects this would produce the best result. Again, this is more efficient than a Contains which translates to an IN clause.
Sql you really want:
SELECT *
FROM periods
WHERE EXISTS (SELECT 1 FROM ratedetails WHERE rateid = 3 AND periods.id = ratedetails.periodid)
The Lamda statement you are after
DbContext myContext = this.getContext();
var myResults = myContext.DbSet<Periods>()
.Where(x => myContext.DbSet<RateDetails>().Any(y => y.periodid == x.id && y.rateid == 3))
.Select(x => x);
Here is a good starting point for learning about lamda's and how to use them.
Lambda Expressions (C# Programming Guide).
this is your second where clause in your query
var priodidList=ratedetails.where(x=>x.rateid ==3).DistinctBy(x=>x.rateid);
now for first part of query
var selected = periods.Where(p => p.id
.Any(a => priodidList.Contains(a.periodid ))
.ToList();

LINQ - "Unable to create constant value of type [type]"?

Here's my error
Unable to create a constant value of type 'Courses.Model.Track'. Only primitive types or enumeration types are supported in this context.
and here's the code that causes it:
model.Courses = db.Courses
.Where(c => model.Tracks.Any(t => t.Certification.ID == certificate))
.ToList();
Also, model.Courses is IEnumerable<Courses>, and I'm not sure if that has anything to do with my problem.
Could anybody shed some light on this for me? Many thanks.
db.Courses is an IQueryable. While the syntax is virtually identical to the LINQ methods of IEnumerable, under the hood they're completely different.
The IQueryable code isn't actually exectued anywhere at all. It just creates a bunch of Expression objects that different query providers are able to use to do...whatever they want (in this case query a database). Those query providers need to specifically have code to handle any given type of input. There are some things that they either can't sensibly transform into a SQL query, or things that the programmers simply didn't think of or choose to handle (even if it might have a sensible SQL translation).
In sort, the query provider just doesn't know how to translate model.Tracks.Any(t => t.Certification.ID == certificate) into SQL.
You simply need to know what types of code is and isn't supported by the query provider that you're using and try to manipulate the code you have into something that it can handle. Something like this should work:
var certificates = model.Tracks.Select(t => t.Certification.ID).ToList();
model.Courses = db.Courses
.Where(c => certificates.Contains(c))
.ToList();
If this were C# code executing all of this in LINQ to objects the two queries would be identical, but to a query provider they're not. They simply have special support for knowing when they see List.Contains to map it to a SQL IN clause. They didn't add specific support for what you did in your first query.
Try using LINQ 'Where In', this should solve your problem:
var names = new string[] { "Alex", "Colin", "Danny", "Diego" };
var matches = from person in people
where names.Contains(person.Firstname)
select person;
OR
var matches = from person in people
where person.Firstname == "Alex" ||
person.Firstname == "Colin" ||
person.Firstname == "Danny" ||
person.Firstname == "Diego"
select person;
http://blogs.msdn.com/b/alexj/archive/2009/03/26/tip-8-writing-where-in-style-queries-using-linq-to-entities.aspx
My apologies for the almost unnecessary question, although #klugerama did help me figure it out.
There was a problem with this:
model.Courses = db.Courses
.Where(c => model.Tracks.Any(t => t.Certification.ID == certificate))
.ToList();
Until I changed it to this:
model.Courses = db.Courses
.Where(c => c.Track.Certification.ID == certificate)
.ToList();
Again, my apologies. Thanks to everyone for their input.

Calling functions from within linq statement

Just wondering if this is the most efficient method of doing this? is there a way of having all the linq within one statement instead of calling a method, like a subselect or something?
newEmployee = (from emp
in db.employees
select new
{
a.EmployeeID,
a.Username,
Status = emp.GetEmployeeCurrentStatus(a.Username)
}).ToList();
This is the GetEmployeeCurrentStatus which returns the status of the employee:
public string GetEmployeeCurrentStatus(string username)
{
using (Entities db = new Entities())
{
var times = (from d in db.TimeTables
where d.DateTime == DateTime.Today &&
d.Employee.Username == username
select d)
.OrderByDescending(d => d.TimeID).FirstOrDefault();
return (x.ClockOut == null ? "IN" : "OUT");
}
}
how about:
newEmployee = (db.employees.Select(emp => new
{
emp.EmployeeID,
emp.Username,
Status = db.TimeTables
.Where(d => d.Employee.Username == emp.Username
&& d.DateTime == DateTime.Today)
.Select(x => x.ClockOut == null ? "IN" : "OUT")
.FirstOrDefault()
})).ToList();
Your attempt may appear cleaner and is functionally ok. However, it is firing up a secondary db call. This will be bad for scalability and performance. The version i've posted uses the same initial db connection and will make the join 1-1. This will result in tighter, faster queries as well as lower resource usage.
You cannot really call a custom method inside a query (or a part of a query that will be executed using the databse). You have essentially two options:
Call ToList before performing the select that needs to call the method (this way, the method will be called on in-memory data)
Compose the query such that it can all run on the SQL server if it is possible. This can be done using AsExpandable extension in predicate builder. For more information on how this works, see also my blog post.
its fine for small data (employees count) but since each GetEmployeeCurrentStatus requires an sql new connection so its not that best practice.
I personally will get all employees (one trip to database) and then get all employees status (one trip to database) so i cashed them all, now i'll join them locally
Hope this helped
Regardless of efficiency, having GetEmployeeCurrentStatus(...) as a method makes the code clearer and more reusable.
Assuming you are using LINQ to SQL or EF, I would refactor your query to use a Join. That way, you will execute a single efficient SQL query on the database, instead of two separate queries.

How do I write a Func<T, bool> to return the entity with the most children?

I have a repository accessing my Entity Framework. I have a method that looks like this:
public TEntity FindOne(Expression<Func<TEntity, bool>> criteria)
{
var query = _queryAll.Where(criteria);
return query.FirstOrDefault();
}
I have 2 Entities that have a one to many relation. Lets call them Courses and Students. A Course can have multiple Students. I'd like to write a query that returns the Course that has the most students.
Courses.OrderByDescending(x=>x.Students.Count()).FirstOrDefault();
But how would I write that as a Func<T, bool> ?
I hope it's not
(x=>x.OrderBy(y=>y.Students.Count()).FirstOrDefault().id == x.id)
Because adding another criteria looks like it wouldn't work:
(x=>x.OrderBy(y=>y.Students.Count())
.FirstOrDefault().id == x.id
&& x.CourseName == "CS101")
It's not at all clear why you'd put the && x.Course == "CS101" at the end. What is that even meant to mean? I strongly suspect the problem is in your design of trying to cram everything into a single Where clause in your FindOne method. Quite simply, not everything can easily be expressed that way.
Also, you should be using OrderByDescending so that the first result has the most students.
Other than that, using ordering seems fairly reasonable to me, in terms of the overall query - but it won't fit neatly into your FindOne scheme.
EDIT: This is a bigger problem than just requiring two conditions. You could write:
x => x.CourseId == "CS101" &&
x.Students.Count() == Courses.Where(x => x.CourseId == "CS101"))
.Max(x => x.Students.Count())
... but the problem is that at that point you've got to have access to Courses anyway, which I'd imagine you're trying to get away from. Aside from that, it should work but it's grotesque.
If you can get access to courses, you might as well ignore FindOne and write the query yourself:
var course = courses.Where(course => course.CourseId == "CS101")
.OrderByDescending(course => course.Students.Count())
.FirstOrDefault();
That's clearly simpler than the first version.

Categories