Filtering related entities in entity framework 6 - entity-framework

I want to fetch the candidate and the work exp where it is not deleted. I am using repository pattern in my c# app mvc.
Kind of having trouble filtering the record and its related child entities
I have list of candidates which have collection of workexp kind of throws error saying cannot build expression from the body.
I tried putting out anonymous object but error still persist, but if I use a VM or DTO for returning the data the query works.
It's like EF doesn't like newing up of the existing entity within its current context.
var candidate = dbcontext.candidate
.where(c=>c.candiate.ID == id).include(c=>c.WorkExperience)
.select(e=>new candidate
{
WorkExperience = e.WorkExperience.where(k=>k.isdeleted==false).tolist()
});
Is there any workaround for this?

You cannot call ToList in the expression that is traslated to SQL. Alternatively, you can start you query from selecting from WorkExperience table. I'm not aware of the structure of your database, but something like this might work:
var candidate = dbcontext.WorkExperience
.Include(exp => exp.Candidate)
.Where(exp => exp.isdeleted == false && exp.Candidate.ID == id)
.GroupBy(exp => exp.Candidate)
.ToArray() //query actually gets executed and return grouped data from the DB
.Select(groped => new {
Candidate = grouped.Key,
Experience = grouped.ToArray()
});

var candidate =
from(dbcontext.candidate.Include(c=>c.WorkExperience)
where(c=>c.candiate.ID == id)
select c).ToList().Select(cand => new candidate{WorkExperience = cand.WorkExperience.where(k=>k.isdeleted==false).tolist()});

Related

EF6 can I update model/table after lambda querying?

I am lambda querying models (I make projection with other classes-GameBankVM, GameCouponBankVM) and at the end, I would like to loop throuh query result and update the model field. But I am getting The entity or complex type 'EPINMiddleWareAPI.Models.GameBankVM' cannot be constructed in a LINQ to Entities query.
Here is my sample code:
var gameBankResult = await (context.GameBanks.Where(g => g.productCode == initiate.productCode)
.Take(initiate.quantity)
.Select(g => new GameBankVM
{
quantity = g.quantity,
currency = g.currency,
initiationResultCode = g.initiationResultCode,
productCode = g.productCode,
productDescription = g.productDescription,
referenceId = g.referenceId,
responseDateTime = g.responseDateTime,
unitPrice = g.unitPrice,
totalPrice = g.totalPrice,
coupons = g.coupons.Select(c => new GameCouponBankVM
{
Pin = c.Pin,
Serial = c.Serial,
expiryDate = c.expiryDate
}).ToList()
})).ToListAsync();
if (gameBankResult.Count() != 0)
{
foreach (var item in gameBankResult)
{
item.referenceId = initiate.referenceId;
context.Entry(item).State = System.Data.Entity.EntityState.Modified;
}
await context.SaveChangesAsync();
return Ok(gameBankResult);
}
How can I update referenceId on my GameBank model/table?
In this scenario, your data won't be updated because your query is returning a List of GameBankVM and not a List of GameBank, now technically speaking, you are breaking SRP, you should either update your data or query your data not both in the same method, you may want to refactor your method like this :
1.- Create a private method for data update, in this case, you query directly GameBank iterate thru list entries, make your changes and save them to the database, this same method can return List of GameBank to avoid another database roundtrip.
2.- In the controller after you call your new method, you can run the transformation query to convert List of GameBank to List of GameBankVM and return it to the view.
There are many other ways to do this, I'm just recommending this as a less impact way to make your controller work. But if you are willing to make things better, you can create a business layer where you resolve all your business rules, or you can use patterns like CQS or CQRS.

Materialize entity framework query

I am using entity framework 5 for a query like this:
var query =
from i in context.Instrument
from p in i.InstrumentPerformance // 1 : n
where p.PortfolioScenarioID == 6013
select i;
I want to store a queryable respresentation of this (filtered) query in memory. Ideally, I would be able to disconnect the context and still request a specific InstrumentPerformance collection like so:
var perf = query.First(i => i.InstrumentID == 407240).InstrumentPerformance;
But this - of course - does not produce the desired result, since the "perf" object will contain an InstrumentPerformance collection that contains every 1:n joined InstrumentPerformance entity (whether its PortfolioScenarioID is 6013 or not) and it will retrieve these entities via lazy loading, with context.ContextOptions.LazyLoadingEnabled = false (or the context runnning out of scope) the query will not yield anyting.
So this is far from where I want to get: an easy to query in-memory representation from the original query. I tried to materialize into dictionaries and similar approaches, but ended up coding custom data objects for the result which I would like to avoid.
So my question is: what is the recommended method to get such in-memory view?
EDIT:
I am currently using two dictionaries to cache the data, e.g:
var instruments = (
from i in context.Instrument
from p in i.InstrumentPerformance
where p.PortfolioScenarioID == 6013
select i)
.ToDictionary (i => p.InstrumentID, i => i);
var performances = (
from i in context.Instrument
from p in i.InstrumentPerformance
where p.PortfolioScenarioID == 6013
select p)
.ToDictionary (p => p.InstrumentID, p => p);
However, this requires two roundtrips to the database where one seems sufficient and more importantly the semantics for querying the performance data (which is now performances[InstrumentID]) is inconsistent with the EF way of querying (which should be instrument.InstrumentPerformance.First() and the like).
It is possible to retrieve the objects in one take first and then create the dictionaries by:
var query =
(from i in context.Instrument
select new {
i,
ps = i.InstrumentPerformance
.Where(p.PortfolioScenarioID == 6013)
}).AsEnumerable()
.Select(x => x.i);
This materializes and selects Instrument entities and, here's the trick, their partly loaded InstrumentPerformance collections. I.e. the instruments only contain InstrumentPerformance entities that meet the condition PortfolioScenarioID == 6013. This is because EF runs a process known as relationship fixup that ties child objects to the right parent object when they are fetched from the database.
So now you can dispose the context and any time after that do
var perf = query.First(i => i.InstrumentID == 407240).InstrumentPerformance;
or build your dictionaries using from i in query in stead of from i in context.Instrument.
IMPORTANT: lazy loading should be disabled, otherwise EF will still try to load the full collections when they are addressed.
Look at EntityCollection<T> CreateSourceQuery and Attach. I think you could do this (not tested):
var instrumentQuery =
from i in context.Instrument
from p in i.InstrumentPerformance // 1 : n
where p.PortfolioScenarioID == 6013
select i;
var instruments = instrumentQuery.ToList();
foreach (var instrument in instruments) {
var performanceQuery =
instrument.InstrumentPerformance.CreateSourceQuery()
.Where(p => p.PortfolioScenarioID == 6013);
instrument.InstrumentPerformance.Attach(performanceQuery);
}
This executes everything at once (no lazy loading) and has a bit of code duplication, but it would result in a list of Instrument where each i.InstrumentPerformance returns the filtered collection, meaning any subsequent code that operates on it can treat it like any other EF collection without needing to know the details of the query.

Entity Framework Timeout

I have been trying to figure out how to optimize the following query for the past few days and just not having much luck. Right now my test db is returning about 300 records with very little nested data, but it's taking 4-5 seconds to run and the SQL being generated by LINQ is awfully long (too long to include here). Any suggestions would be very much appreciated.
To sum up this query, I'm trying to return a somewhat flattened "snapshot" of a client list with current status. A Party contains one or more Clients who have Roles (ASPNET Role Provider), Journal is returning the last 1 journal entry of all the clients in a Party, same goes for Task, and LastLoginDate, hence the OrderBy and FirstOrDefault functions.
Guid userID = 'some user ID'
var parties = Parties.Where(p => p.BrokerID == userID).Select(p => new
{
ID = p.ID,
Title = p.Title,
Goal = p.Goal,
Groups = p.Groups,
IsBuyer = p.Clients.Any(c => c.RolesInUser.Any(r => r.Role.LoweredName == "buyer")),
IsSeller = p.Clients.Any(c => c.RolesInUser.Any(r => r.Role.LoweredName == "seller")),
Journal = p.Clients.SelectMany(c => c.Journals).OrderByDescending(j => j.OccuredOn).Select(j=> new
{
ID = j.ID,
Title = j.Title,
OccurredOn = j.OccuredOn,
SubCatTitle = j.JournalSubcategory.Title
}).FirstOrDefault(),
LastLoginDate = p.Clients.OrderByDescending(c=>c.LastLoginDate).Select(c=>c.LastLoginDate).FirstOrDefault(),
MarketingPlanCount = p.Clients.SelectMany(c => c.MarketingPlans).Count(),
Task = p.Tasks.Where(t=>t.DueDate != null && t.DueDate > DateTime.Now).OrderBy(t=>t.DueDate).Select(t=> new
{
ID = t.TaskID,
DueDate = t.DueDate,
Title = t.Title
}).FirstOrDefault(),
Clients = p.Clients.Select(c => new
{
ID = c.ID,
FirstName = c.FirstName,
MiddleName = c.MiddleName,
LastName = c.LastName,
Email = c.Email,
LastLogin = c.LastLoginDate
})
}).OrderBy(p => p.Title).ToList()
I think posting the SQL could give us some clues, as small things like the order of OrderBy coming before or after the projection could make a big difference.
But regardless, try extracting the Clients in a seperate query, this will simplify your query probably. And then include other tables like Journal and Tasks before projecting and see how this affects your query:
//am not sure what the exact query would be, and project it using ToList()
var clients = GetClientsForParty();
var parties = Parties.Include("Journal").Include("Tasks")
.Where(p=>p.BrokerID == userID).Select( p => {
....
//then use the in-memory clients
IsBuyer = clients.Any(c => c.RolesInUser.Any(r => r.Role.LoweredName == "buyer")),
...
}
)
In all cases, install EF profiler and have a look at how your query is affected. EF can be quiet surprising. Something like putting OrderBy before the projection, the same for all these FirstOrDefault or SingleOrDefault, they can all have a big effect.
And go back to the basics, if you are searching on LoweredRoleName, then make sure it is indexed so that the query is fast (even though that could be useless since EF could end up not making use of the covering index since it is querying so many other columns).
Also, since this is query is to view data (you will not alter data), don't forget to turn off Entity tracking, that will give you some performance boost as well.
And last, don't forget that you could always write your SQL query directly and project to your a ViewModel rather than anonymous type (which I see as a good practice anyhow) so create a class called PartyViewModel that includes the flatten view you are after, and use it with your hand-crafted SQL
//use your optimized SQL query that you write or even call a stored procedure
db.Database.SQLQuery("select * from .... join .... on");
I am writing a blog post about these issues around EF. The post is still not finished, but all in all, just be patient, use some of these tricks and observe their effect (and measure it) and you will reach what you want.

Linq to Entities Select clause with lambda

I am working on a new project and we are using Entity Framework and the dev lead would like to use lambda queries whenever possible. One thing we are having a hard time figuring out is how to select two columns specifically. Also how to select distinct. We have a table that has multiple entries for a vendor but we want to just get a list of vendors and load to a dictionary object. It fails because as written it is trying to add a key value that has already been added. Take the following query.
Dictionary<int, string> dict = new Dictionary<int, string>();
dict = GetWamVendorInfo().AsEnumerable()
.Where(x => x.vendor_name != null && x.vendor_id != null)
//.Select(x => x.vendor_id).Distinct()
.Take(2)
.ToDictionary(o => int.Parse(o.vendor_id.ToString()), o => o.vendor_name);
What I would like to do is select just vendor_id and vendor_name so we can get just the distinct records.
Any help would be greatly appreciated.
Thanks,
Rhonda
Use an anonymous type:
// earlier bit of query
.Select(x => new { VendorId = x.vendor_id, VendorName = x.vendor_name } )
.Distinct()
.ToDictionary(o => o.VendorId, o => o.VendorName);
I've removed the call to Take(2) as it wasn't clear why you'd want it - and also removed the parsing of VendorId, which I would have expected to already be an integer type.
Note that you should almost certainly remove the AsEnumerable call from your query - currently you'll be fetching all the vendors and filtering with LINQ to Objects. There's also no point creating an empty dictionary and then ignoring it entirely. I suspect your complete query should be:
var vendors = GetWamVendorInfo()
.Select(x => new { VendorId = x.vendor_id,
VendorName = x.vendor_name } )
.Distinct()
.ToDictionary(o => o.VendorId,
o => o.VendorName);
As an aside, you should ask your dev lead why he wants to use lambda expressions (presumably as opposed to query expressions) everywhere. Different situations end up with more readable code using different syntax options - it's worth being flexible on this front.
Just use an anonymous object:
var vendors = GetWamVendorInfo().AsEnumerable()
.Where(x => x.vendor_name != null && x.vendor_id != null)
.Select(new {x.vendor_id, x.vendor_name})
.Take(2)
That's it. You can now work with vendors[0].vendor_id, vendors[0].vendor_name, and so on.

Entity Framework Include with condition

I need to filter a dealer based on id and the uncomplete checkins
Initially, it returned the dealer based only on id:
// TODO: limit checkins to those that are not complete
return this.ObjectContext.Dealers
.Include("Groups")
.Include("Groups.Items")
.Include("Groups.Items.Observations")
.Include("Groups.Items.Recommendations")
.Include("Checkins")
.Include("Checkins.Inspections")
.Include("Checkins.Inspections.InspectionItems")
.Where(d => d.DealerId == id)
.FirstOrDefault();
As you can see the requirement is to limit the checkins.
Here's what I did:
var query = from d in this.ObjectContext.Dealers
.Include("Groups")
.Include("Groups.Items")
.Include("Groups.Items.Observations")
.Include("Groups.Items.Recommendations")
.Include("Checkins.Inspections")
.Include("Checkins.Inspections.InspectionItems")
.Where(d => d.DealerId == id)
select new
{
Dealer = d,
Groups = from g in d.Groups
select new
{
Items = from i in g.Items
select new
{
Group = i.Group,
Observations = i.Observations,
Recommendations = i.Recommendations
}
},
Checkins = from c in d.Checkins
where c.Complete == true
select new
{
Inspections = from i in c.Inspections
select new
{
InspectionItems = i.InspectionItems
}
}
};
var dealer = query.ToArray().Select(o => o.Dealer).First();
return dealer;
It works.
However, I am not convinced I am doing the right thing.
What is the best way to accomplish what I did? A stored procedure maybe?
I am not sure I even have to use Include clause anymore
Thank you.
If you want to load filtered relations with single query you indeed have to execute such projection but you don't need those calls to Include. Once you are building projections includes are not use - you have returned data under your control.
Stored procedure will help you only if you fall back to plain ADO.NET because stored procedures executed through Entity framework are not able to fill related entities (only flattened structures).
Automatic fixupu mentioned by #Andreas requires multiple database queries and as I know it works only if lazy loading is disabled because proxied object somehow doesn't have information about fixup and it still has its internal flags for each relation as not loaded so when you access them for the first time they still execute additional query.
Maybe you can make use of the relation fixup mechanism in the EF ObjectContexts. When you do multiple queries in the same context for entities, that are related by associations, these are resolved.
Assuming your association between Dealers and Checkins is 1:n with navigation properties on each side, you could do like:
var dealer = yourContext.Dealers
.Where(p => p.DealerId == id)
.FirstOrDefault();
if(dealer != null)
{
yourContext.Checkins
.Where(c => c.Complete && c.DealerId == dealer.DealerId)
.ToList();
I have not tested this by now, but since EF recognises that the Checkins, it inserts into the context by the second query belong to the dealer from the first query, corresponding references are created.
#Andreas H:
Awesome, thank you a lot.
I had to adjust your suggestion like this and it worked:
var dealer = this.ObjectContext.Dealers
.Include("Groups")
.Include("Groups.Items")
.Include("Groups.Items.Observations")
.Include("Groups.Items.Recommendations")
.Where(p => p.DealerId == id).
FirstOrDefault();
if (dealer != null)
{
this.ObjectContext.Checkins
.Include("Inspections")
.Include("Inspections.InspectionItems")
.Where(c => !c.Complete && c.Dealer.DealerId == dealer.DealerId)
.ToList();
}
return dealer;
I still have to use the Include otherwise it won't return the referenced entities.
Note also that Dealer.Groups are unrelated to the Dealer.Checkins.
So if there's no checkins satisfying the condition, Groups still need to be returned.
It's interesting to note that at first, I put the two include for checkins to the dealer
var dealer = this.ObjectContext.Dealers
.Include("Groups")
.Include("Groups.Items")
.Include("Groups.Items.Observations")
.Include("Groups.Items.Recommendations")
.Include("Checkins.Inspections")
.Include("Checkins.Inspections.InspectionItems")
.Where(p => p.DealerId == id).
FirstOrDefault();
if (dealer != null)
{
this.ObjectContext.Checkins
.Where(c => c.Complete && c.DealerId == id)
.ToList();
}
return dealer;
but it returned all the Checkins including those which are not complete.
I don't understand exactly why the latter doesn't work but the former does, how are the entities are resolved. I somehow can intuit that the former returns all data.
Your accepted solution will generate multiple database queries. As Ladislav Mrnka said a projection is the only way to pull your result with one query. The maintance of your code indeed hard. Maybe you could use an IQueryable-Extension that builds the projection dynamically and keep your code clean:
var query = this.ObjectContext.Dealers.SelectIncluding( new List<Expression<Func<T,object>>>>(){
x => x.Groups,
x => x.Groups.Select(y => y.Items),
x => x.Groups.Select(y => y.Items.Select(z => z.Observations)),
x => x.Groups.Select(y => y.Items.Select(z => z.Recommendations)),
x => x.Checkins.Where(y => y.Complete==true),
x => x.Checkins.Select(y => y.Inspections),
x => x.Checkins.Select(y => y.Inspections.Select(z => z.InspectionItems))
});
var dealer = query.First();
return dealer;
You can find the extension at thiscode/DynamicSelectExtensions on github