If I have a query that looks like this:
var forms = repo.GetForms().Where(f => f.SubForms.Any(sf => sf.Classes.Any(c => c.TermId == termId)));
From this you can see my schema is as follows:
SubForm has many Class which has many Term.
What I want:
All SubForms with their Classes In a particular Term.
What is happening now is that I get all the SubForm that has any Class in a particular Term. That means that SubForm comes back with ALL child Class and not just the ones related to the Term.
For eg. I have 2 terms, a subform with 2 classes in each term. This query brings back 4 classes instead of the 2 in that particular term.
Is there any Include('Expression') that I can use to say that I only want to include all classes based on a condition? Or is my query wrong?
Use this:
var subForms = repo.GetSubForms.Select(sf = new {
SubForm = sf,
Classes = sf.Classes.Where(c => c.TermId == termId)
}).ToList()
.Select(t => t.SubForm)
.ToList();
UPDATE: based on #Slauma's comment:
If you want to load SubForms that they have any Class that has a Term by termId, you can go from end to begin; like this:
var subForms = repo.Terms.Where(t => t.Id == termId).Select(t => new {
Term = t,
Class = t.Class,
SubForm = t.Class.SubForm
}).ToList()
.Select(t => t.SubForm).ToList();
OR in a easiest way, you can use Include on your Term, see:
var subForms = repo.Terms.Include("Class.SubForm").Where(t => t.Id == termId)
.Select(t => t.Class.SubForm).ToList();
NOTE: As I can understand from your question, you have a relationship like this:
SubForm has_many Class has_many Term
But, your provided code is showing a relationship like this one:
SubForm has_many Class
Term has_many Class
If you can, put your entities in question, or explain their relationship more please. Thank you.
An Include(Where Expression) does not exist. If you use eager loading with Include you will always load all the elements.
There is a way around this by using projections. The basic idea is you will select a new anonymous type with the property you want and another property with the filtered navigational items. EF will link those together and as a result you will fake a Include(Where ... )
Check this for an example.
You know sometimes I start getting lost in fancy LINQ extension methods and attempting to figure out how to eagerly load exactly what I want and resort to a very simple "join" concept.
var result =
(from f in SubForums
from c in Classes
from t in Term
where t.TermId = 1
select new { SubForum = f, Class = c, Term = t }).ToList();
This is a simple join that uses predefined navigation properties (hence you don't have to specify the join condition). You return an anonymous type with everything that you need. The beauty of this is that Entity Framework will do the auto-fixup for you, therefor you're free to return the SubForum only from your method if you wish, it will automatically contain the Class and subsequent Term references.
I don't know the exact names of the relations, but it should be something along the lines of
repo.Terms
.Include("Classes")
.Include("Classes.SubForms")
.SingleOrDefault(x => x.TermId = termId);
// or
repo.GetSubForms
.Include("Classes")
.Where(sf => sf.Classes.Where(c => c.TermId == termId));
This seems to be a common request, it was difficult to find a solution to it when I was looking earlier this year. I ended up using the solution included in the link below (I'm not sure this is the exact solution I found, but it's the same idea). Hope this helps!
Filter the "Includes" table on Entity Framework query
//Found this method to filter our child objects instead of using .include()
var Results = (from res in
(from u in DataContext.User
where u.Type.ToUpper() != "ADMIN"
&& u.StartDate <= DateTime.Now
&& (u.EndDate == null || u.EndDate >= DateTime.Now)
select new
{
User = u,
Access = u.Access.Where(a => a.StartDate <= DateTime.Now
&& (a.EndDate == null || a.EndDate >= DateTime.Now))
}
)
select res);
//The ToArray is neccesary otherwise the Access is not populated in the Users
ReturnValue = Results.ToArray().Select(x => x.User).ToList();
Related
I have a class Users with next format:
List<Subscriptions> Subscriptions {get;set;}
And class Subscriptions contains:
SubscriptionType type {get;set;}
I'd like to include all of these into User object, something like this:
var _referredUser = ctx.Users
.Include(x=>x.Subscriptions.Where(y=>y.Status==true))
.ToList()
.FirstOrDefault(y => y.Email == _all[i].Referred_email);
I can successfully include Subscriptions collection, but I'm not sure how can I go further beyond Subscription collection and include the property SubscriptionType into User object, if it's even possible?
My other question as well is whether I can only include those subscriptions that have status ==true, because user can have multiple records in subscriptions table and only one that is set to true?
I tried something like this, but it throws me an error:
.Include(x=>x.Subscriptions.Where(y=>y.Status==true))
The error is:
The Include path expression must refer to a navigation property defined on the type. Use dotted paths for reference navigation properties and the Select operator for collection navigation properties.
Can someone help me out with this LINQ ?
You can't filter in an Include, I recommend you do this:
var email=_all[i].Referred_email;
var _referredUser = ctx.Users.Include(x=>x.Subscriptions.Select(y=>y.SubscriptionType))
.FirstOrDefault(y => y.Email == email && y.Subscriptions.Any(y=>y.Status));
As the exception said, you must refer a navigation property in the Include
Something like this should work...
var email = _all[i].Referred_email;
var _referredUser = ctx.Users
.Include(x=>x.Subscriptions.Select(y=>y.SubscriptionType))
.Select(x => new { User = x, Subscriptions = x.Subscriptions.Where(subscription => subscription.Status)})
.Where(x => x.Subscriptions.Any())
.Select(x => x.User)
.FirstOrDefault(y => y.Email == email);
To include the child properties you can use Select in the include statement.
To only get the results where Subscriptions.Status is true you will need to do a separate query or do some Select() magic.
I am trying to fetch an option using the SingleOrDefault Linq to SQL method.
var po = repository.Context.AsQueryable<Option>().SingleOrDefault(o => o.Option.Id == sp.Options // sp.Options is a collection);
The problem is that inside the SingleOrDefault method I am comparing p.Option.Id == a collection. What I want is to select the option from sp.Options that matches the o.Option.Id. How can I do that?
UPDATE:
One thing I should have mentioned that the sp.Options is a different class than the Option class. sp.Options is SPOptions class so I cannot pass it inside the contains method.
Take a look at Contains.
repository.Context.AsQueryable<Option>().SingleOrDefault(o => sp.Options.Contains(o.Option.Id));
If Options is not a collection of the class of Option.Id, you can use the Any method with your comparison logic in it as follow :
repository.Context.AsQueryable<Option>().SingleOrDefault(o => sp.Options.Any(opts => opts.Something == o.Option.Id));
Search using Contains (sp.Options.Contains(o.Option.Id)) like:
var po = repository.Context.AsQueryable<Option>()
.SingleOrDefault(o => sp.Options.Contains(o.Option.Id));
If members of sp.Options are different from Id then you can do:
var po = repository.Context.AsQueryable<Option>()
.SingleOrDefault(o => sp.Options.Any(r=> r.Id == o.Option.Id));
or
var po = repository.Context.AsQueryable<Option>()
.SingleOrDefault(o => sp.Options.Select(r=> r.Id).Contains(o.Option.Id));
Assuming Id is the field in sp.Options elements that you want to compare with.
Based on your question it seems you're expecting to have a single match between those two option sets, correct ?
If so, I'd suggest you to write it as:
var po = repository.Context.AsQueryable().Where(o => sp.Options.Any(item=>item.id == o.Option.Id)).SingleOrDefault();
I have got a bit of an issue and was wondering if there is a way to have my cake and eat it.
Currently I have a Repository and Query style pattern for how I am using Linq2Sql, however I have got one issue and I cannot see a nice way to solve it. Here is an example of the problem:
var someDataMapper = new SomeDataMapper();
var someDataQuery = new GetSomeDataQuery();
var results = SomeRepository.HybridQuery(someDataQuery)
.Where(x => x.SomeColumn == 1 || x.SomeColumn == 2)
.OrderByDescending(x => x.SomeOtherColumn)
.Select(x => someDataMapper.Map(x));
return results.Where(x => x.SomeMappedColumn == "SomeType");
The main bits to pay attention to here are Mapper, Query, Repository and then the final where clause. I am doing this as part of a larger refactor, and we found that there were ALOT of similar queries which were getting slightly different result sets back but then mapping them the same way to a domain specific model. So take for example getting back a tbl_car and then mapping it to a Car object. So a mapper basically takes one type and spits out another, so exactly the same as what would normally happen in the select:
// Non mapped version
select(x => new Car
{
Id = x.Id,
Name = x.Name,
Owner = x.FirstName + x.Surname
});
// Mapped version
select(x => carMapper.Map(x));
So the car mapper is more re-usable on all areas which do similar queries returning same end results but doing different bits along the way. However I keep getting the error saying that Map is not able to be converted to SQL, which is fine as I dont want it to be, however I understand that as it is in an expression tree it would try to convert it.
{"Method 'SomeData Map(SomeTable)' has no supported translation to SQL."}
Finally the object that is returned and mapped is passed further up the stack for other objects to use, which make use of Linq to SQL's composition abilities to add additional criteria to the query then finally ToList() or itterate on the data returned, however they filter based on the mapped model, not the original table model, which I believe is perfectly fine as answered in a previous question:
Linq2Sql point of retrieving data
So to sum it up, can I use my mapping pattern as shown without it trying to convert that single part to SQL?
Yes, you can. Put AsEnumerable() before the last Select:
var results = SomeRepository.HybridQuery(someDataQuery)
.Where(x => x.SomeColumn == 1 || x.SomeColumn == 2)
.OrderByDescending(x => x.SomeOtherColumn)
.AsEnumerable()
.Select(x => someDataMapper.Map(x));
Please note, however, that the second Where - the one that operates on SomeMappedColumn - will now be executed in memory and not by the database. If this last where clause significantly reduces the result set this could be a problem.
An alternate approach would be to create a method that returns the expression tree of that mapping. Something like the following should work, as long as everything happening in the mapping is convertible to SQL.
Expression<Func<EntityType, Car>> GetCarMappingExpression()
{
return new Expression<Func<EntityType, Car>>(x => new Car
{
Id = x.Id,
Name = x.Name,
Owner = x.FirstName + x.Surname
});
}
Usage would be like this:
var results = SomeRepository.HybridQuery(someDataQuery)
.Where(x => x.SomeColumn == 1 || x.SomeColumn == 2)
.OrderByDescending(x => x.SomeOtherColumn)
.Select(GetCarMappingExpression());
Consider this line of code:
List<SIDB_TransactionInformation> transaction = SIDB.SIDB_TransactionInformations
.Where(k => k.iscurrent == true & k.objectid == SIDB.func_GetObjectID("dbo.SIDB_Module")).ToList();
List<SIDB_Module> module = SIDB.SIDB_Modules
.Where(k => k.moduleid == transaction
.Where(j => j.transactionid == k.moduleid)
.SingleOrDefault().transactionid).ToList();
I do have 2 invocation of where method in different collection. First i distinct my list via iscurrent and objectid after that I do have other invocation of where method (for SIDB_Modules) to distinct the list via moduleid where in the the values refer to the transactionid of my previous list. Now i have an error message like this Local sequence cannot be used in LINQ to SQL implementation of query operators except the Contains() operator.
sorry i'm new in lambda expression. need help badly
I think this is what you're looking for
List<SIDB_Module> module = SIDB
.SIDB_Modules
.Where(k => transaction.Any(j => j.transactionid == k.moduleid))
.ToList();
Make a list of SIDB_Modules where there is a transaction whose transactionid is equal to the moduleid. LINQ to Sql might have an issue with Any, I don't remember, if it does you can rewrite it with an extra step like this
var transactionIds = transaction.Select(j => j.transactionid);
List<SIDB_Module> module = SIDB
.SIDB_Modules
.Where(k => transactionIds.Contains(k.moduleid))
.ToList();
If performance is an issue you might consider going with the second method and putting transactionIds into something that implements ISet<T> and has a constant time lookup.
Well, it looks like you're trying to do a join between SIDB_TransactionInformations and SIDB.SIDB_Modules. If so, try
var objectID = SIDB.func_GetObjectID("dbo.SIDB_Module");
List<SIDB_Module> modules = (from module in SIDB.SIDB_Modules
join transaction in SIDB.SIDB_TransactionInformations on module.moduleid equals transaction.transactionid
where transaction.iscurrent && transaction.objectid == objectID
select module).ToList();
So I have this query in my repository (also using Unit of Work pattern) which uses eager loading to make one hit to the database:
from g in _context.Games.Include(pg => pg.PreviousGame).Include(go => go.GameObjects)
where EntityFunctions.DiffMilliseconds(DateTime.Now, g.EndDate) > 0
&& g.GameTypeId == (int)GameTypes.Lottery
&& g.GameStatusId == (int)GameStatues.Open
select new LotteryModel
{
EndDate = g.EndDate,
GameId = g.Id,
PreviousGameEndDate = g.PreviousGame.EndDate,
PreviousGameId = g.PreviousGameId.HasValue ? g.PreviousGameId.Value : 0,
PreviousGameStartDate = g.PreviousGame.StartDate,
PreviousWinningObjectCount = g.PreviousGame.GameObjects.Select(go => go.Object.Count).FirstOrDefault(),
PreviousWinningObjectExternalVideoId = g.PreviousGame.GameObjects.Select(go => go.Object.Video.ExternalVideoId).FirstOrDefault(),
PreviousWinningObjectName = g.PreviousGame.GameObjects.Select(go => go.Object.Video.Name).FirstOrDefault(),
StartDate = g.StartDate,
WinningObjectCount = g.GameObjects.Select(go => go.Object.Count).FirstOrDefault(),
WinningObjectExternalVideoId = g.GameObjects.Select(go => go.Object.Video.ExternalVideoId).FirstOrDefault(),
WinningObjectName = g.GameObjects.Select(go => go.Object.Video.Name).FirstOrDefault()
};
However I'm reluctant to use this because I now have to create a separate LotteryModel object to return up throughout my other layers.
I would like to be able to return an entity of type "Game" which has all of the navigational methods to all of my other data (PreviousGame, GameObjects, etc) and then map the needed properties to my flat view model, but when I do this it seems to only lazy load the objects and then I have the additional hits to the DB.
Or do I have this wrong and whenever I need to return heirarchical data I should return it through my LINQ query in the select portion?
My basic goal is to reduce the hits to the DB.
I don't really understand the problem. You return your Games object and you can access the properties and subobjects off it. Your use of the Include() method tells it to load what you need, and not lazy load it.
Make sure you return a single object via a .First, .FirstOrDefault, .Single, .SingleOrDefault, or similar methods.
I ended up with this query (FYI I'm using the System.Data.Objects namespace for the Include extension):
(from g in _context.Games.Include(pg => pg.PreviousGame.GameObjects.Select(o => o.Object.Video))
.Include(go => go.GameObjects.Select(o => o.Object.Video))
where EntityFunctions.DiffMilliseconds(DateTime.Now, g.EndDate) > 0
&& g.GameTypeId == (int)GameTypes.Lottery
&& g.GameStatusId == (int)GameStatues.Open
select g).FirstOrDefault();
I guess I just needed to include more of the heirarchy and didn't know I could use Select() in the Include() function!