I have some issues wrapping my head around the .include in LINQ. I'd appreciate a lot if someone could show me the query to achieve this:
I have two entities, User and Validation (which is a serie of validation codes) . They are related in EF5 as 1:*. I'm trying to get a User object and eager load the Validations collection.
At this moment, I'm doing this (cx being the Context inside a using block):
var user = cx.Users
.Where(u => u.UserGuid.Equals(UserGuid))
.FirstOrDefault();
if (user == null)
{
return 0;
}
var validation = cx.Validations
.Where(v => v.Code.Equals(Code))
.Where(v => v.UserId.Equals(user.UserId))
.FirstOrDefault();
But how do I achieve this in only one query so that then I can use
user.Validations.FirstOrDefault();
without getting an error and by only getting the Validations that satisfy the Validation.Code == Code test?
Thanks for your help (I know I'm sounding confused but that's because I am).
Did you try something like this:
var validation = cx.Validations.Where(v=>v.Code==Code &&
cx.Users.Any(u=>u.UserID==v.UserID &&
u.UserGuid==UserGuid)
).FirstOrDefault();
You might need to include the related records as well by doing:
var user = cx.Users
.Where(u => u.UserGuid.Equals(UserGuid))
.Include(u => u.Validations)
.FirstOrDefault();
but then you should be able to select the requested validaiton with
user.Validations
.FirstOrDefault(v => v.Code.Equals(Code));
Related
I am having struggles finding the best method to implement an IN statement.
Currently I am using the below code in my controller to return a list of stances specific an individual customer account.
return _context.Stances
.ToList()
.Select(Mapper.Map<Stances, StancesDto>)
.Where(c => c.AccountGUID == userAccountID.CustomerGUID);
I am in the process of creating a partner portal that would allow a Partner user to have access to many other customer accounts that each individual customer provides them access to.
I setup a partnerLink table that stores a PartnerGUID and a CustomerGUID, marrying the relationship. The struggle I have is finding a method to allow a one to many relationship using an "IN" or "contains" option.
What I am looking to do is something like this:
either load the list if it is just a customer "OR" if it is a partner account Load all customer stances in the partnerLink table.
var partners = _context.PartnerLinks
.Where(user => user.PartnerGUID == userAccountID.CustomerGUID)
.Select(user => user.AccountGUID) // extract the emails from users
.ToList();
return _context.Stances
.ToList()
.Select(Mapper.Map<Stances, StancesDto>)
.Where(c => c.AccountGUID == userAccountID.CustomerGUID || partners.Contains(c.AccountGUID));
You should not use ToList when combining LINQ queries. Everything should be IQueryable.
var partners = _context.PartnerLinks
.Where(user => user.PartnerGUID == userAccountID.CustomerGUID)
.Select(user => user.AccountGUID);
return _context.Stances
.Where(c => c.AccountGUID == userAccountID.CustomerGUID || partners.Contains(c.AccountGUID))
.AsEnumerable()
.Select(Mapper.Map<Stances, StancesDto>);
Also consider to use Automapper's ProjectTo
return _context.Stances
.Where(c => c.AccountGUID == userAccountID.CustomerGUID || partners.Contains(c.AccountGUID))
.ProjectTo<StancesDto>(configuration);
I would think you could do it with a Union.
Sometime like:
return _context.Stances.Select(Mapper.Map<Stances, StancesDto>).Where(c => c.AccountGUID == userAccountID.CustomerGUID)
.Union(_context.Stances.Select(Mapper.Map<Stances, StancesDto>).Where(c => partners.Contains(c.AccountGUID)));
I have an entity TimesheetHeader which has a User object, and start date and end date corresponding to the week of timesheet entry, and an ICollection of TimesheetEntry.
TimesheetEntry is another entity which contains a Project entity, date and hours object (each TimesheetEntry basically records the number of hours the user worked on a project on a day).
I have to generate a report to find out the various users and breakdown of hours worked on each day on a particular project.
Currently my logic is to first get a list as follows:
context.TimesheetHeader.Where(u => u.status.statusName != "deleted" && DbFunctions.TruncateTime(u.StartDate) >= dateStart && u.TimesheetEntry.Any(te => te.projectId == report.project)).ToList();
But this basically returns a TimesheetHeader and all its TimesheetEntry if there is at least one TimesheetEntry corresponding to the project.
Then I have to loop and filter the result.
Is there a better solution to this where I can directly get the result from the query itself, where I get the TimesheetHeader and only the subset of relevant TimesheetEntry corresponding to the project
Does this work? I also suggest pluralizing your collections so it's more clear which end of the relationship you're on.
context.TimesheetHeaders
.Where(u => u.status.statusName != "deleted")
.Where(u => DbFunctions.TruncateTime(u.StartDate) >= dateStart)
.Where(u => u.TimesheetEntries.Any(te => te.projectId == report.project))
.Select(u => new {
TimeSheetHeader = u,
TimeSheetHeaderEntries = u.TimesheetEntries.Where(te => te.projectId == report.project)
})
As Gert Arnold answered here
you can try as below by adding where clause to your include :
context.TimesheetHeader.Where(u => u.status.statusName != "deleted" &&
DbFunctions.TruncateTime(u.StartDate) >= dateStart).Select(t => { t, TimesheetEntries = t.TimesheetEntry.Where(te => te.projectId == report.project)).AsEnumerable()
.Select(x => x.t)
.ToList();
Or look at EntityFramework-Plus. It might be useful.
I am trying to filter out the second part of the tables (UserRoles.IsDeleted==false). Is there any advice how i can do that?
var Users = context.Users.Where(r => r.IsDeleted == IsDeleted).ToList<User>();
Users = context.Users.Include(x => x.UserRoles.Select(y=>y.IsDeleted==false)).ToList();
Thank you
You can do the following to filter using the second part:
var Users = context.Users.Where(r => r.IsDeleted == IsDeleted).ToList<User>();
if(condition)
{
Users = Users.where(y => y.IsDeleted == false)).ToList();
}
There are two options to filter related entities
Doing a projection.
Unfortunately, when you use Include method, you can't filter the related entities as you intend to do. You need to project your query to a DTO object or a anonymous object, as the below example.
var query=context.Users.Include(x => x.UserRoles)
.Where(r => r.IsDeleted == IsDeleted)
.Select(u=> new{ ...,
Roles=x => x.UserRoles.Where(y=>!y.IsDeleted)})
A second option could be using Explicitly Loading. But this is in case you can load the related entities of one specific entity,eg,.
var user=context.Users.FirstOrDefault(r.IsDeleted == IsDeleted);//Getting a user
context.Entry(user)
.Collection(b => b.UserRoles)
.Query()
.Where(y=>!y.IsDeleted)
.Load();
You can do this inside of a foreach per each entity you get from the first query,
var query=context.Users.Where(r => r.IsDeleted == IsDeleted);
foreach(var u in query)
{
context.Entry(u)
.Collection(b => b.UserRoles)
.Query()
.Where(y=>!y.IsDeleted)
.Load();
}
but it's going to be really inefficient because you are going to do a roundtrip to your DB per each entity. My advice is use the first option, projecting the query.
I am currently building a custom MVC widget for a Sitefinity (v9.2) project.
As part of this widget there is a query to the database to retrieve a collection of a dynamic module type (articles). I am trying to get article types that contain all the labels in my Labels.PublicLabels guid list.
My current query is:
var collection = dynamicModuleManager.GetDataItems(articleType)
.Where(a => a.Status == Telerik.Sitefinity.GenericContent.Model.ContentLifecycleStatus.Live
&& a.Visible == true
&& Labels.PublicLabels.All(l => a.GetValue<IList<Guid>>("Public").Contains(l)));
At runtime I get an exception mentioning that 'server side not implemented'.
Could this be a limitation of OpenAccess?
I have tried a wide range of LINQ to SQL query combinations which have been successful, I am struggling to understand the problem here.
Any ideas would be greatly appreciated!
UPDATE:
I have tried a few variations on the same query such as:
var collection = dynamicModuleManager.GetDataItems(articleType)
.Where(a => a.Status == Telerik.Sitefinity.GenericContent.Model.ContentLifecycleStatus.Live
&& a.Visible == true
&& Labels.PublicLabels.Any(l => a.GetValue<IList<Guid>>("Public").Contains(l)));
No result, I still recieve the following exception message:
Execution of 'System.Linq.Enumerable:Any(IEnumerable1,Func2)' on the database server side currently not implemented.
Any further advice would be greatly appreciated, for now I will assume this is to do with OpenAccess limitations around LINQ to SQL.
Managed to solve this issue. Definitely down to Open Access LINQ to SQL limitations. What I did was add an additional where clause after filtering the collection down as much as I needed to. Here is the complete query:
var collection = dynamicModuleManager.GetDataItems(articleType).Where(a => a.Status == ContentLifecycleStatus.Live && a.Visible == true)
.OrderByDescending(a => a.PublicationDate)
.Distinct()
.Where(a => new HashSet<Guid>(a.GetValue<IList<Guid>>("Public")).IsSupersetOf(Labels.PublicLabels));
I will be reporting this to Sitefinity and if they manage to put a proper fix in place I will report back.
Thank you everyone for your support!
I had the same problem before, this way worked for me
var listIds = Labels.PublicLabels;
var collection = dynamicModuleManager.GetDataItems(articleType)
.Where(a => a.Status == ContentLifecycleStatus.Live && a.Visible == true)
.Where(a => a.GetValue<TrackedList<Guid>>("Public").Where(al => listIds.Contains(al)).Any());
I didn't compile that, let me know if it will not work for you
I can get 1..0 data by RIA Services fine.
var sFiltered = this.ObjectContext.Sequences.Include("Schedules").Include("Events")
.Include("Events.EventFrames")
.Include("Events.EventRules")
.Include("Events.EventFrames.EventFramePlugins")
.Include("Events.EventFrames.EventFramePlugins.EventFramePluginParameters")
.Include("Events.EventFrames.EventFramePlugins.EventFramePluginContentItems")
.Where(s => s.ID == schedule.SequenceID).FirstOrDefault();
So the code above works great.
The problem is that I want to get data by ClientContentItemID [EventFramePlugins] from [ClientContentItemElements]
Please have a look at the image below. But what I don't like to do is to use 1 extra request from the WPF client to get this data. So the idea is use 1 request to get ALL data I need.
Thank you!!!
I think the following query would return the desired result. it finally selects an anonymous type (ClientContentItemElementId, Sequence) which you can change to get the appropriate result. However, I did not test the generated sql to see if this approach is acceptable at all.
sequences.Include.....Where(s => s.ID == schedule.SequenceID).SelectMany(s => s.Events).SelectMany(e => e.EventFrames).SelectMany(ef => ef.EventFramePlugins)
.SelectMany(efp => efp.EventFramePluginContents).SelectMany(efpc => efpc.ClientContentItems).
SelectMany(cci => cci.ClientContentItemElemts).Where(ccie => ccie.ClientContentItemElementId == myValue).
Select(
ccie =>
new
{
ccie,
ccie.ClientContentItem.EventFramePluginContentItem.EventFramePlugin.EventFrame.Event.
Sequence
});