Am I lazy loading these properties? - c#

We're using Entity Framework 4.1 for our data acces and while building up objects and we started asking questions to ourselves
about how chatty the application was going to be with the database. Now one item that we really started looking at is below:
public MasterPreAward()
{
public int ID
public int MemberID
public int CycleID
public virtual Cycle
public virtual Member
public virtual Status
public virtual ICollection<DataTracking> DataTrackings
public virtual ICollection<ReviewerAssignment> Reviewers
}
The MasterPreAward is a generated entity from the database and has the navigation properites of Cycle, Member, Status along with two collections for DataTrackings
Reviewers. What we were wondering was, how did Entity Framework load up the child objects based off of these items and bring back the data we use in the follow model?
As you can see, we're passing in MasterPreAward object and then accessing children properties which are loaded based on the MasterPreAward.
public ViewHeaderSummary(MasterPreAward masterPreAward)
{
MasterPreAwardId = masterPreAward.ID;
ClientId = masterPreAward.Cycle.Project.Program.ClientID;
ApplicationId = masterPreAward.MemberID;
ProgramId = masterPreAward.Cycle.Project.ProgramID;
ProjectId = masterPreAward.Cycle.ProjectID;
EventTypeId = masterPreAward.DataTrackings.FirstOrDefault(x=>x.Finished==true
&& x.EventTypeID==(int)FormEvents.Application).EventTypeID;
CycleId = masterPreAward.CycleID;
FormId = masterPreAward.Cycle.CycleForms.FirstOrDefault().FormID;
}
What we'd like to know, is this the best way to access these properties, or should be really be thinking doing this type of work in a different way?

I believe the default settings would be to lazy load each nested collection independently, which could cause a lot of database traffic.
The best way to verify the generated SQL is to start a SQL profiler and confirm the number of queries.
You can force EF to eagerly load related entities by calling .Include method. See here for more details.

You don't seem to query for full entities but only for a bunch of scalar values. In my opinion this would be a good candidate for a projection which collects all the needed values in a single database roundtrip:
var result = dbContext.MasterPreAwards
.Where(m => m.ID == masterPreAward.ID)
.Select(m => new
{
ClientId = m.Cycle.Project.Program.ClientID,
ProgramId = m.Cycle.Project.ProgramID,
ProjectId = m.Cycle.ProjectID,
EventTypeId = m.DataTrackings.Where(d => d.Finished
&& x.EventTypeID==(int)FormEvents.Application)
.Select(d => d.EventTypeID).FirstOrDefault(),
FormId = m.Cycle.CycleForms.Select(c => c.FormID).FirstOrDefault()
})
.Single();
MasterPreAwardId = masterPreAward.ID;
ClientId = result.ClientID;
ApplicationId = masterPreAward.MemberID;
ProgramId = result.ProgramID;
ProjectId = result.ProjectID;
EventTypeId = result.EventTypeId;
CycleId = masterPreAward.CycleID;
FormId = result.FormID;
As you can see, you need the DbContext to run such a query.
Your original way to lazily load all related entities will lead to 5 database queries as far as I can see (for Cycle, Project, Program, DataTrackings and CycleForms). Worst of all are the queries for DataTrackings.FirstOrDefault and CycleForms.FirstOrDefault which will actually load the full collections first from the database into memory and then execute FirstOrDefault in memory on the loaded collections to return only one single element from which you then only use one single property.
(Edit: Query for ApplicationId and CycleId not necessary, Code changed.)

Related

EF6 inserts duplicates to join table

I have following issue - I'm using generic repository to detach and reattach entity on update operation to prevent accessing it from two different objects.
This is how Update operation looks like:
public void Update(TEntity entityToUpdate)
{
if (this.context.Entry(entityToUpdate).State != EntityState.Detached)
{
this.context.Entry(entityToUpdate).State = EntityState.Detached;
}
this.context.Entry(entityToUpdate).State = EntityState.Modified;
}
When using two entities lets say like user and course
public class User
{
public Guid UserId { get; set; }
public string Email { get; set; }
public ICollection<Course> Courses{ get; set; }
}
}
and Course
public class Course
{
public string Name { get; set; }
public Guid CourseId { get; set; }
public virtual ICollection<User> Users { get; set; }
}
public class CourseDto
{
public string Name { get; set; }
public Guid CourseId { get; set; }
public virtual ICollection<Guid> Users { get; set; }
}
and I try to update course's users with following code:
public async Task<Course> Update(Guid existingCourseId, CourseDto courseModel)
{
var course = await course.Repository.Query(true)
.Include(c=> c.Users)
.Where(e => e.CourseId == existingCourseId )
.FirstOrDefaultAsync();
if (course == null)
{
return null;
}
course.Users = await FindUsersByIds(courseModel.Users);
course.Name = courseModel.Name;
courseRepository.Update(course);
await this.unitOfWork.SaveChangesAsync();
return course;
}
it doesn't work when I want to for example update only Name property.
If Users property doesn't change and there is at least one user it will try to insert record to the CourseUser join table violating primary key constraint instead of 'noticing' that it is already existing in database.
Edit:
Additionally when I use Entry(entityToUpdate).State = EntityState.Unchanged before changing it to modified and move repository.Update() call before overwriting entity properties it works all fine. If somebody could explain this behaviour to me I would be really grateful
In a nutshell, simply don't do this with detaching and setting state to modified. You're already doing the right thing by transporting details to update via a DTO. Let EF's change tracking do it's thing and just call SaveChanges().
I'm not sure what you're concern is warranting the Detach to "prevent accessing". All this does it tell the DbContext to treat the item as untracked, but then by setting the state to Modified, you're immediately tracking it again. The Main reasons you don't want to do this is that by setting the entity state to "Modified" you are telling EF to generate an UPDATE statement for the entire record regardless of whether any value was actually modified rather than optimizing an UPDATE statement to be run for values it detected had changed, and only if they had changed.
The issue you are likely seeing is because of this line:
course.Users = await FindUsersByIds(courseModel.Users);
I would bet money that your FindUsersByIds method is returning a non-tracked set of User entities either by AsNoTracking() or detaching those user entities before returning them. You have already eager loaded users for the given course you loaded. You might think this tells EF to remove existing references and replace them with the desired ones, but it doesn't. It just "adds" a set of Users that EF will treat as new associations where-by some of those associations already still exist in the database.
As a general rule when working with entities. Never, under any circumstances reset/overwrite a collection of related entities. Not to clear it and not to change the values associated with an entity.
If the model contains a revised list of IDs, then the proper way to update the set is to identify any users that need to be added, and any that need to be removed. Load references for the ones that need to be added to associate, and remove any from the eager loaded relationships. Also, where you expect 0 or 1 result, use SingleOrDefault rather than FirstOrdefault. First methods should only be used with an OrderBy clause where you could expect more than one entry and want a reliable, repeatable First. The exception to this would be when working with Linq against in-memory ordered sets where you can guarantee one unique find where First() will perform faster while Single() would scan the entire set. With EF generating queries First() generates a TOP(1) SQL statement where Single() generates a TOP(2) SQL statement so that performance assumption gets shot down to a bare minimum difference to enforce that expectation.
var course = await course.Repository.Query(true)
.Include(c=> c.Users)
.Where(e => e.CourseId == existingCourseId)
.SingleOrDefaultAsync();
if (course == null)
return null;
var existingUserIds = course.Users.Select(u => u.UserId);
var userIdsToAdd = courseModel.Users.Except(existingUserIds).ToList();
var userIdsToRemove = existingUserIds.Except(courseModel.Users).ToList();
if (userIdsToRemove.Any())
{
var usersToRemove = course.Users
.Where(u => userIdsToRemove.Contains(u.UserId))
.ToList();
foreach(var user in usersToRemove)
course.Users.Remove(user);
}
if (userIdsToAdd.Any())
{
var usersToAdd = FindUsersByIds(userIdsToAdd); // Important! Ensure this does NOT detach or use AsNoTracking()
foreach(var user in usersToAdd)
course.Users.Add(user);
}
course.Name = courseModel.Name;
await this.unitOfWork.SaveChangesAsync();
return course;
Basically this inspects the IDs selected to pick out the ones to add and remove, then proceeds to modify the eager loaded collection if needed. The change tracking will take care of determining what, if any SQL needs to be generated.
If the Users collection is exposed as List<User> then you can use AddRange and RemoveRange rather than the foreach loops. This won't work if they are exposed as IList<User> or IHashSet<User> or ICollection<User>.
Edit:
Based on the error mentioned, I would suggest starting by at least temporarily removing some of the variables in the equation around the particular implementation of the Repository and Unit of Work patterns and working with a scoped DbContext to begin with:
For a start try this edit:
using (var context = new AppDbContext())
{
var course = await context.Courses
.Include(c=> c.Users)
.Where(e => e.CourseId == existingCourseId)
.SingleOrDefaultAsync();
if (course == null)
return null;
var existingUserIds = course.Users.Select(u => u.UserId);
var userIdsToAdd =
courseModel.Users.Except(existingUserIds).ToList();
var userIdsToRemove = existingUserIds.Except(courseModel.Users).ToList();
if (userIdsToRemove.Any())
{
var usersToRemove = course.Users
.Where(u => userIdsToRemove.Contains(u.UserId))
.ToList();
foreach(var user in usersToRemove)
course.Users.Remove(user);
}
if (userIdsToAdd.Any())
{
var usersToAdd = FindUsersByIds(userIdsToAdd); // Important! Ensure this does NOT detach or use AsNoTracking()
foreach(var user in usersToAdd)
course.Users.Add(user);
}
course.Name = courseModel.Name;
await context.SaveChangesAsync();
context.Entry(course).State = EntityState.Detached; // Necesssary evil as this method is returning an entity.
return course;
}
This is only intended as a temporary measure to help identify if your repository or unit of work could be leading to transient DbContexts being used to load and track entities. This could still have issues depending on what happens to the Course after this method returns it. In this case since we are leaving the scope of the DbContext instance that is tracking it, we detach it. The next step would be to ensure that a DbContext, either directly or accessible through the unit of work can be injected and that it is guaranteed to be scoped to the web request (if web) or a scope suited to the unit of work this operation is part of. (A bit more work for things like WPF desktop applications)
Normally for something like a web application you would want to ensure that a DbContext has a lifetime scope of the web request (or shorter) but not Transient. We want to ensure that all operations within a unit of work reference the same DbContext instance otherwise you end up working with entities that might be tracked by multiple DbContexts, or start introducing code to mix tracked and untracked (detached) entities to get around problems when passing entity references around. Working with detached entities requires a lot of extra boiler-plate, disciplined coding to ensure DbContexts are working with the correct, single reference of entities to avoid "already tracked" type errors or duplicate data insertion / PK violation exceptions. I.e. pre-checking each and every DbSet.Local in a entity graph of related entities for any currently tracked instances with the same ID and replacing references when wanting to attach an entity graph to a DbContext that isn't already tracking that instance.

Entity Framework Core - storing/querying multilingual records in the database efficiently

I'm building an application that must support more than one language.
Therefore, some of the records in my database need to have multiple versions for each language.
I will first explain how I currently achieve this: consider an entity called Region which represents a geographical location and simply has a name.
I would design my entities like this:
public class Region
{
public int Id { get; set; }
public List<RegionLanguage> Languages { get;set; }
}
public class RegionLanguage
{
public Region Region { get;set; } // Parent record this language applies to
public string CultureCode { get; set; } // Will store culture code such as en-US or fr-CA
// This column/property will be in the language specified by Culturecode
[StringLength(255)]
public string Name { get;set; }
}
From a database perspective, this works great because its infinitely scalable to any number of records. However, due to the way Entity Framework Core works, it becomes less scalable.
Using the above structure, I can query a Region and generate a view model based on specific culture information:
var region = _context.Regions.Where(e => e.Id == 34)
.Include(e => e.Languages)
.FirstOrDefault();
var viewModel = new RegionViewModel
{
Name = region.Languages.FirstOrDefault(e => e.CultureCode == "en-US")?.Name // en-US would be dynamic based on the user's current language preference
}
You can see this becomes inefficient since I have to include ALL language records for the entity I'm fetching, when I actually only need one and then search for the correct language in memory. Of course this becomes even worse when I need to fetch a list of Regions which then has to return a large amount of unnecessary data.
Of course, this is possible using SQL directly simply by adding an extra clause on the join statement:
select *
from Regions
left join RegionLanguage on (RegionLanguage.Region = Regions.Id and RegionLanguage.CultureCode = 'en-US')
However, to my understanding, this is not possible to do natively from Entity Framework Core without using a RawQuery (EF: Include with where clause)
So that begs the question: is there a better way to achieve multilingual records in the database using EF Core? Or should I just continue with my approach and hope that EF Core implements Include filtering by the time my application actually needs it (I'll admit I might be optimizing slightly prematurely, but I'm genuinely curious if there is a better way to achieve this).
You can use projection.
var languageRegion = await _context.Regions
.Select(p => new Region
{
Languages = p.Languages.FirstOrDefault(e => e.CultureCode == "en-US")
}.FirstOrDefaultAsync(e => e.Id == 34);
If regions and languages are not changing frequently you can use caching.
You could use a Global Query Filter
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<RegionLanguage>(builder =>
{
builder.HasQueryFilter(rl => rl.CultureCode == "en-US");
});
}

Get entity navigation properties after insert

I have the following 2 classes:
public class Reward
{
public int Id { get; set; }
public int CampaignId { get; set;
public virtual Campaign Campaign { get; set; }
}
public class Campaign
{
public int Id { get; set; }
public virtual ICollection<Reward> Rewards { get; set; }
}
With this I have all the obvious necessary stuff like a DbContext and mappings.
Now let's say I create a Reward entity and insert it like this:
var reward = new Reward { CampaignId = 1 };
context.Set<Reward>().Add(reward);
context.SaveChanges();
reward = context.Set<Reward>().SingleOrDefault(a => a.Id == reward.Id);
//reward.Campaign is null
I obviously have a campaign with Id 1 so the FK constraint is happy. After this insert, my reward entity has it's new Identity Id set. Now the problem is that reward is still just the Reward entity I created. And with this, the reward.Campaign property is null. It seems like EF is keeping the inserted entities in memory, and when I then do a .SingleOrDefault(a => a.Id == reward.Id) it simply returns the entity in memory, and not a new proxy. This is probably a good thing.
So the question is: How does one access or load the navigation properties after an insert or get a new proxy that has the navigation properties as proxies as well.
Am I perhaps inserting in the wrong way?
If I understand you correctly, you're trying to eagerly load a complex property after establishing a relationship via a foreign key property.
SaveChanges() does not do anything in the way of loading complex properties. At most, it is going to set your primary key property if you're adding new objects.
Your line reward = context.Set<Reward>().SingleOrDefault(a => a.Id == reward.Id);
also does nothing in the way of loading Campaign because your reward object is not attached to the context. You need to explicitly tell EF to load that complex object or attach it then let lazy loading work its magic.
So, after you context.SaveChanges(); you have three options for loading reward.Campaign:
Attach() reward to the context so that Campaign can be lazily loaded (loaded when accessed)
context.Rewards.Attach(reward);
Note: You will only be able to lazy load reward.Campaign within the context's scope so if you're not going to access any properties within the context lifespan, use option 2 or 3.
Manually Load() the Campaign property
context.Entry(reward).Reference(c => c.Campaign).Load();
Or if Campaign was a collection, for example Campaigns:
context.Entry(reward).Collection(c => c.Campaigns).Load();
Manually Include() the Campaign property
reward = context.Rewards.Include("Campaigns")
.SingleOrDefault(r => r.Id == reward.Id);
Although, I'd suggest Load since you already have reward in memory.
Check out the Loading Related Objects Section on this msdn doc for more information.
As you are creating your reward object as new Reward(), EF doesn't have a proxy. Instead, create it using DbSet.Create like this:
var reward = context.Set<Reward>().Create();
reward.CampaignId = 5;
context.SaveChanges();
Next attach it to your DbSet:
context.Rewards.Attach(reward);
Finally, you can now use lazy loading to get related entities:
var campaign = reward.Campaign;
I have a simple Solution around the problem.
instead of adding the CampaignID to the reward, add the campaign Object.. so:
var _campaign = context.Campaign.First(c=>c.Id == 1);//how ever you get the '1'
var reward = new Reward { Campaign = _campaign };
context.Set<Reward>().Add(reward);
context.SaveChanges();
//reward.Campaign is not null
Entity framework does all the heavy lifting here.
You're probably thinking that it's a waste to load the entire Campaign object but if you are going to be using it (from what it looks like, seems you are) then I don't see why not.
You can even use the include statement when fetching it above if you need to access navigation properties from the Campaign object...
var _campaign = context.Campaign.include(/*what ever you may require*/).First(c=>c.Id = 1);
In addition to Carrie Kendall and DavidG (in VB.NET):
Dim db As New MyEntities
Dim r As Reward = = db.Set(Of Reward)().Create()
r.CampaignId = 5
db.Reward.Add(r) ' Here was my problem, I was not adding to database and child did not load
db.SaveChanges()
Then, property r.Campaign is available
Did you try using Include()? Something like this:
reward = context.Set<Reward>().Include("Campaigns").SingleOrDefault(a => a.Id == reward.Id);
if you have more than one navigation properties or want to add more than one records it may be hard to do it in these ways.
so i suggest if the memory doesn't matter create a new context object after adding your records , and use it instead

C#, Linq2SQL - tricks to fetch a ViewModel object with relation data?

I don't know Linq2Sql so well yet and I was wondering if there is a trick for this probably common MVVM scenario. I have Linq2Sql data context containing Domain models, but I am fetching data for my customized ViewModel object from it.
var query = from ord in ctx.Table_Orders
select new OrderViewModel()
{
OrderId = ord.OrderId,
OrderSum = ord.OrderSum,
OrderCurrencyId = ord.OrderCurrencyId,
OrderCurrencyView = ord.Currency.CurrencyText
};
So i want my ViewModel to inculde both CurrencyId from domain object and the CurrencyText from related table to show it nicely in the View.
This code works great. It generates one DB call with join to fetch the CurrencyText. But the model is simplified, real one has many more fields. I want to make the code reusable because I have many different queries, that returns the same ViewModel. Now every minor change to OrderViewModel requires lots of maintainance.
So I moved the code to OrderViewModel itself as a constructor.
public OrderViewModel(Table_Order ord)
{
OrderId = ord.OrderId,
OrderSum = ord.OrderSum,
OrderCurrencyId = ord.OrderCurrencyId,
OrderCurrencyView = ord.Currency.CurrencyText
}
And call it like this.
var query = from ord in ctx.Table_Orders
select new OrderViewModel(ord);
The Problem: The join is gone DB query is no more optimised. Now I get 1+N calls to database to fetch CurrencyText for every line.
Any comments are welcome. Maybe I have missed different great approach.
This is how far i could get on my own, to get the code reusability. I created a function that does the job and has multiple parameters. Then I need to explicitly pass it everything that has crossed the line of entity.
var query = ctx.Table_Orders.Select(m =>
newOrderViewModel(m, m.Currency.CurrencyText));
The DB call is again optimized. But it still does not feel like I am there yet! What tricks do You know for this case?
EDIT : The final solution
Thanks to a hint by #Muhammad Adeel Zahid I arrived at this solution.
I created an extension for IQueryable
public static class Mappers
{
public static IEnumerable<OrderViewModel> OrderViewModels(this IQueryable<Table_Order> q)
{
return from ord in q
select new OrderViewModel()
{
OrderId = ord.OrderId,
OrderSum = ord.OrderSum,
OrderCurrencyId = ord.OrderCurrencyId,
OrderCurrencyView = ord.Currency.CurrencyText
};
}
}
Now i can do this to get all list
var orders = ctx.Table_Order.OrderViewModels().ToList();
or this to get a single item, or anything in between with Where(x => ..)
var order = ctx.Table_Order
.Where(x => x.OrderId == id).OrderViewModels().SingleOrDefault();
And that completely solves this question. The SQL generated is perfect and the code to translate objects is reusable. Approach like this should work with both LINQ to SQL and LINQ to Entities. (Not tested with the latter) Thank You again #Muhammad Adeel Zahid
Whenever we query the database, we mostly require either enumeration of objects (more than one records in db) or we want a single entity (one record in db). you can write your mapping code in method that returns enumeration for whole table like
public IEnumerable<OrderViewModel> GetAllOrders()
{
return from ord in ctx.Table_Orders
select new OrderViewModel()
{
OrderId = ord.OrderId,
OrderSum = ord.OrderSum,
OrderCurrencyId = ord.OrderCurrencyId,
OrderCurrencyView = ord.Currency.CurrencyText
};
}
Now you may want to filter these records and return another enumeration for example on currencyID
public IEnumerable<OrderViewModel> GetOrdersByCurrency(int CurrencyID)
{
return GetAllOrders().Where(x=>x.CurrencyId == CurrencyID);
}
Now you may also want to find single record out of all these view models
public OrderViewModel GetOrder(int OrderID)
{
return GetAllOrders().SingleOrDefault(x=>x.OrderId == OrderID);
}
The beauty of IEnumerable is that it keeps adding conditions to query and does not execute it until it is needed. so your whole table will not be loaded unless you really want it and you have kept your code in single place. Now if there are any changes in ViewModel Mapping or in query itself, it has to be done in GetAllOrders() method, rest of code will stay unchanged
You can avoid the N+1 queries problem by having Linq2SQL eagerly load the referenced entites you need to construct your viewmodels. This way you can build one list of objects (and some referenced objects) and use it to construct everything. Have a look at this blog post.
One word of warning though: This technique (setting LoadOptions for the Linq2SQL data context) can only be done once per data context. If you need to perform a second query with a different eager loading configuration, you must re-initalize your data context. I automated this with a simple wrapper class around my context.

Entity Framework 4.0 - Returning List of Model objects + Count of Children Per Object

I'm running into a common need in my project to return collections of my model objects, plus a count of certain types of children within each, but I don't know if it is possible or how to model a "TotalCount" property in a Model class and populate it as part of on single Entity Framework query, preferably using LINQ queries. Is it possible to do this whilst being able to use the Entity Framework .Include("Object") and .Skip() and .Take()? I'm new to the Entity Framework so I may be missing tons of obvious stuff that can allow this...
I would like to be able to paginate on the dynamically constructed count properties as well. I'm thinking that the most scalable approach would be to store the counts as separate database properties and then simply query the count properties. But for cases where there are small row counts that I'm dealing with, I'd rather do the counts dynamically.
In a model like this:
Table: Class
Table: Professor
Table: Attendee
Table: ClassComment
I'd like to return a list of Class objects in the form of List, but I would also like the counts of Attendees and Class comments to be determined in a single query (LINQ preferred) and set in two Class properties called AttendeeCount and ClassCommentCount.
I have this thus far:
var query = from u in context.Classes
orderby tl.Name
select u;
List<Class> topics = ((ObjectQuery<Class>)query)
.Include("ClassComments")
.Skip(startRecord).Take(recordsToReturn).ToList();
Any suggestions or alternative query approaches that can still allow the use of .Include() and pagination would be much much appreciated, in order to produce a single database query, if at all possible. Thank you for any suggestions!
Try this:
public class ClassViewModel {
public Class Class { get; set; }
public int AttendeeCount { get; set; }
public int ClassCommentCount { get; set; }
}
var viewModel = context.Classes.Select(clas =>
new ClassViewModel {
Class = clas,
AttendeeCount = clas.ClassAttendes.Count,
ClassCommentCount = clas.ClassComments.Count}
).OrderBy(model => model.ClassCommentCount).Skip(startRecord).Take(recordsToReturn).ToList();
You don't have to include comments to get count.
It will not work this way. The easiest approach is to use projection into anonymous (or custom) non entity type. I would try something like this:
var query = context.Classes
.Include("ClassComments") // Only add this if you want eager loading of all realted comments
.OrderBy(c => c.Name)
.Skip(startRecord)
.Take(recordsToReturn)
.Select(c => new
{
Class = c,
AttendeeCount = c.Attendees.Count(),
ClassCommentCount = c.ClassComments.Count() // Not needed because you are loading all Class comments so you can call Count on loaded collection
});
The problem in your requirement are AttendeeCount and ClassCommentCount properties. You can't easily add them to your model because there is no corresponding column in database (unless you define one and in such case you don't need to manually count records). You can define them in partial Class implementation but in such case you can't use them in Linq-to-entities query.
The only way to map this in EF is to use DB view and create special read only entity to represent it in your applicaiton or to use DefiningQuery which is custom SQL command defined in SSDL instead of DB table or view.

Categories