Entity Framework "Basic" Doubts - c#

Just started to experiment EF, and some doubts came across:
Suppose I've got a DB table with 10 000 rows. When the EF entity is instantiated in my source, do I have these 10 000 rows in memory?
Does the lazy instantiation mechanism has some kind of effect on the above?
It's been my practice to include some audit fields on my DB tables, such as CreateOn and ChangedOn. These are DateTime and I use to set its values to GETDATE() (SQL Server time). EF allows changes to entity properties to occur either on the client machine (desktop apps) or IIS. Is there any way to set these values on the SQL Server?
Thanks.

1) Only the entities that match your query are loaded into memory, and that only occurs as the list is iterated. (thanks to yield, unless you do a .ToList())
2) Lazy instantiation means that the complex properties of your objects are only fetched from the database as you use them (unless you have an .Include()).
3) You can use triggers, for UpdatedOn fields, or Defaults for CreatedOn fields in your SQL Server database. Don't include those properties in your Code First entities, and you'll be fine (unless you want to use them for any sort of querying in your C#).

When you instantiate entity framework, no data is obtained. There is only set connection via ADO.NET (connection is added to pool, etc.).
Lazy loading allow entities to be loaded when needed, it means before that moment, they aren't obtained from database. For example (http://msdn.microsoft.com/en-us/library/vstudio/dd456846(v=vs.100).aspx):
using (AdventureWorksEntities context =new AdventureWorksEntities())
{
// You do not have to set context.ContextOptions.LazyLoadingEnabled to true
// if you used the Entity Framework to generate the object layer.
// The generated object context type sets lazy loading to true
// in the constructor.
context.ContextOptions.LazyLoadingEnabled = true;
// Display ten contacts and select a contact
var contacts = context.Contacts.Take(10);
foreach (var c in contacts)
Console.WriteLine(c.ContactID);
Console.WriteLine("Select a customer:");
Int32 contactID = Convert.ToInt32(Console.ReadLine());
// Get a specified customer by contact ID.
var contact = context.Contacts.Where(c => c.ContactID == contactID).FirstOrDefault();
// If lazy loading was not enabled no SalesOrderHeaders would be loaded for the contact.
foreach (SalesOrderHeader order in contact.SalesOrderHeaders)
{
Console.WriteLine("SalesOrderID: {0} Order Date: {1} ",
order.SalesOrderID, order.OrderDate);
}
}
Answer is here: https://stackoverflow.com/a/5965620/1617002

If your database is huge and has 10,000 tables then you should avoid having a single context as this will be initialized and loaded with each initialization to the context and will be in memory, so you should avoid this by using bounded contexts where you divide your tables into logical areas for example financial context, security context, etc and each context will define only the tables that context is dealing with which leads to lighter initialization and faster processing. See this link
http://msdn.microsoft.com/en-us/magazine/jj883952.aspx
About created date, I do the same and I define an Interfaces called ITrackable that has the createddate property and also modifieddate and implement this interface by all the entities that have these properties and this allows me to set those field before I do ef saving process by looping through all the tracked entities of type ITrackable and set the created date and modified date properties.
Hope that helps.

Related

EF manually load / populate navigation property

I have the below code that fetches the data from service and attaches to the objects that was loaded from local database by EntityFramework 6!
We had the UserJob details before in our local database, However we decided to move it to the service later and migrated all the UserJobs to new micro service.
We are in the middle of this move and now we are saving the User jobs in both local database and in the new service!
When we read from micro service and update the local database as well, the code treats its a new job, since the JobId is no longer available in new service, but we have same creation date and user id to find the JobId available in local database! I set it now correctly.
private void IncludeUserJobOnUser(UserJob userJob, MyDBContext db)
{
var aspnetUser = new EFDataReader().GetUser(userJob.userId, db);
if (userJob != null && aspnetUser != null)
{
userJob.JobId = new EFDataReader().GetUserJobId(userJob.userId, userJob.CreationDate, db);
DbEntityEntry entry = db.Entry(userJob);
entry.State = EntityState.Unchanged;
if (aspnetUser.UserJobs.Any(x => x.JobId != userJob.JobId)) // Here is the problem
{
aspnetUser.UserJobs.Add(userJob);
}
userJob.AspNetUser = aspnetUser;
}
}
PROBLEM:
I am trying to set the navigational property with the objects retrieved from the microservice. However, EF6 always hits the database and populate them from local database instead adding the entity came from microservice.
Any idea how to solve the above?
If you mean that by accessing "aspnetUser.UserJobs" you are seeing a query kicked off to load the UserJobs as part of the Any() check, that would be Lazy Loading. If the UserJobs were already eager loaded when the data reader loaded the user then this wouldn't happen. If the user hasn't loaded the jobs, how would the code know if the user had that job or not?
Code like this can be dangerous at runtime if not careful because you are passing entity references around to be associated with other entities where the scope of the DbContext that may, or may not still be tracking these entities is not clearly defined. Provided that "db" DbContext instance is the single overarching DbContext for retrieving all entities (including UserJob) are loaded from or associated to then you should be ok, but when I read things like "objects retrieved from the microservice" that sends up a bit of a red flag as Microservices would be scoping their own DbContext so returning entities from one would not be known by the current DbContext whose entities you are associating such results with.

How do I compare an entity in my local context with the one in the database in EF Core?

EF Core 6 and .NET 6.
Suppose all my entities have a LastUpdateAt property, which is a DateTime that gets updated every time an entity is added or modified.
I get an entity from the context and show it to the user (web page, WPF window, whatever). At some point, the user clicks a Save button.
Before I save, I want to check if the entity has been updated by someone else since I got my copy. However, I'm struggling to see how to do this.
If I query the context, it just gives me back the entity I already have (including any changes my user has made).
If I refresh the entity, it overwrites the one in my context, losing my user's changes.
How do I check if the database version has a newer time stamp than the one in my context?
Thanks
Moving the discussion here since I need to paste longer text. In this article it's said, during SaveChanges(), if the DATABASE version was modified in the mean time it will throw DbUpdateConcurrencyException. In that exception you have all 3 values and YOU can decide on how to resolve the conflict:
Resolving a concurrency conflict involves merging the pending changes from the current DbContext with the values in the database. What values get merged will vary based on the application and may be directed by user input.
There are three sets of values available to help resolve a concurrency conflict:
Current values are the values that the application was attempting to write to the database.
Original values are the values that were originally retrieved from the database, before any edits were made.
Database values are the values currently stored in the database.
If you are loading an entity, keeping a DbContext instance open, updating that entity, then saving to the same DbContext instance then by default you are relying on EF to manage concurrency. This follows a "last in wins". You can let EF manage the concurrency by adding a [ConcurrencyCheck] on the LastUpdateAt property or using a Row Version via [Timestamp]. This will cause EF to fail updating if the underlying data has been updated. From there you have to decide how you want to handle it.
If you want to perform the concurrency check yourself then there are a couple of options.
Structure your code to shorten the lifespan of the DbContext using either detached entities or projected View Models. This will generally have flow-on benefits to your code performance as the original longer-lived DbContext can easily find ways to cause bloat, or accumulate "poisoned" entities if alive too long. Automapper is a great tool to assist here where you can use ProjectTo to get the view models, then Map(source, destination) to copy the values across afterward. In this way you load the data including the last modified at value, make your changes, then when saving, you load the data, validate the modified at etc. then copy the values across and save.
Scope a DbContext instance to check the data before saving.
.
private DateTime getFooLastUpdateAt(int fooId)
{
using(var context = new AppDbContext())
{
var lastUpdateAt = context.Foos
.Where(x => x.FooId == fooId)
.Select(x => x.LastUpdateAt)
.Single();
return lastUpdateAt;
}
}
This could use an injected DbContext factory or such to create the DbContext instance..

EF Core duplicate keys: The instance of entity type '' cannot be tracked because another instance with the key value '' is already being tracked

I'm working on a form using EF Core in Blazor Server. I had a number of issues with entity tracking so I set all of my queries to AsNoTracking and designed my service to create a new instance of dbcontext for each query. I think this is appropriate as none of the returned values will be edited - only the form data that users enter and the id references to the queried fields, such as employee numbers, will be stored. For inserting data, I use this:
using var context = Factory.CreateDbContext();
context.SetupForm.Attach(model);
context.Entry(model).State = EntityState.Added;
await context.SaveChangesAsync();
I am attaching the data rather than adding it and then setting the form object state to added. This ensures EF Core doesn't attempt to insert the existing employee objects when it inserts the form data.
The trouble starts in a section of the form that can have as many items as the user wants. The select a couple of employees and type in relevant data. When they submit the form, they may have selected the same employee in multiple items. As these employees were selected from separate contexts, they are two separate instances with the same ID. Of course, EF Core doesn't like this and throws errors like this one:
The instance of entity type 'Principal' cannot be tracked because another instance with the key value '{EmployeeID: 1234}' is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached.
I understand why this error is occurring but I need to be able to attach multiple entities in this way. How can I work around this issue?
One thing I could do is assign the foreign keys manually but that would be rigid and require updates whenever the model changes.
just try this
using var context = Factory.CreateDbContext();
context.Set<Principal>().Add(model);
//or maybe context.Principals.Add(model);
await context.SaveChangesAsync();
This seems to do the trick! What it does is mark any entity that lacks a key as added. Otherwise, the entity is ignored entirely.
using var context = Factory.CreateDbContext();
context.ChangeTracker.TrackGraph(model, node =>
{
if (!node.Entry.IsKeySet)
{
node.Entry.State = EntityState.Added;
}
});
await context.SaveChangesAsync();
None of the items that have a key will need to be inserted. Treating them as untracked then solves any issues with duplicates and only inserts the rows that need it.
More information: https://learn.microsoft.com/en-us/ef/core/change-tracking/identity-resolution#resolve-duplicates

EF4 update a value for all rows in a table without doing a select

I need to reset a boolean field in a specific table before I run an update.
The table could have 1 million or so records and I'd prefer not to have to have to do a select before update as its taking too much time.
Basically what I need in code is to produce the following in TSQL
update tablename
set flag = false
where flag = true
I have some thing close to what I need here http://www.aneyfamily.com/terryandann/post/2008/04/Batch-Updates-and-Deletes-with-LINQ-to-SQL.aspx
but have yet to implement it but was wondering if there is a more standard way.
To keep within the restrictions we have for this project, we cant use SPROCs or directly write TSQL in an ExecuteStoreCommand parameter on the context which I believe you can do.
I'm aware that what I need to do may not be directly supported in EF4 and we may need to look at a SPROC for the job [in the total absence of any other way] but I just need to explore fully all possibilities first.
In an EF ideal world the call above to update the flag would be possible or alternatively it would be possible to get the entity with the id and the boolean flag only minus the associated entities and loop through the entity and set the flag and do a single SaveChanges call, but that may not be the way it works.
Any ideas,
Thanks in advance.
Liam
I would go to stakeholder who introduced restirctions about not using SQL or SProc directly and present him these facts:
Updates in ORM (like entity framework) work this way: you load object you perform modification you save object. That is the only valid way.
Obviously in you case it would mean load 1M entities and execute 1M updates separately (EF has no command batching - each command runs in its own roundtrip to DB) - usually absolutely useless solution.
The example you provided looks very interesting but it is for Linq-To-Sql. Not for Entity framework. Unless you implement it you can't be sure that it will work for EF, because infrastructure in EF is much more complex. So you can spent several man days by doing this without any result - this should be approved by stakeholder.
Solution with SProc or direct SQL will take you few minutes and it will simply work.
In both solution you will have to deal with another problem. If you already have materialized entities and you will run such command (via mentioned extension or via SQL) these changes will not be mirrored in already loaded entities - you will have to iterate them and set the flag.
Both scenarios break unit of work because some data changes are executed before unit of work is completed.
It is all about using the right tool for the right requirement.
Btw. loading of realted tables can be avoided. It is just about the query you run. Do not use Include and do not access navigation properties (in case of lazy loading) and you will not load relation.
It is possible to select only Id (via projection), create dummy entity (set only id and and flag to true) and execute only updates of flag but it will still execute up to 1M updates.
using(var myContext = new MyContext(connectionString))
{
var query = from o in myContext.MyEntities
where o.Flag == false
select o.Id;
foreach (var id in query)
{
var entity = new MyEntity
{
Id = id,
Flag = true
};
myContext.Attach(entity);
myContext.ObjectStateManager.GetObjectStateEntry(entity).SetModifiedProperty("Flag");
}
myContext.SaveChanges();
}
Moreover it will only work in empty object context (or at least no entity from updated table can be attached to context). So in some scenarios running this before other updates will require two ObjectContext instances = manually sharing DbConnection or two database connections and in case of transactions = distributed transaction and another performance hit.
Make a new EF model, and only add the one Table you need to make the update on. This way, all of the joins don't occur. This will greatly speed up your processing.
ObjectContext.ExecuteStoreCommand ( _
commandText As String, _
ParamArray parameters As Object() _
) As Integer
http://msdn.microsoft.com/en-us/library/system.data.objects.objectcontext.executestorecommand.aspx
Edit
Sorry, did not read the post all the way.

Dynamically loading SQL tables in Entity Framework

I need to dynamically access some SQL tables hopefully using the Entity Framework. Here's some pseudo code:
var Account = DB.Accounts.SingleOrDefault(x => x.ID == 12345);
which will return me an Account object and this contains some fields called "PREFIX", "CAMPAIGN ID" and further information about the accounts are stored in separate SQL tables with the naming convention of PREFIX_CAMPAIGNID_MAIN.
The tables all have the same fields so I was thinking of creating a new Entity that isn't mapped anywhere and then dynamically loading it, like so:
var STA01_MAIN = new MyAccount(); // my "un-mapped" entity
DB.LoadTable('STA01_MAIN').LoadInto(STA01_MAIN);
I can now get anything about the STA01_MAIN account: STA01_MAIN.AccountId.
So my question is: how do I access these tables using the Entity Framework?
I don't think EF has a LoadTable and LoadInto method, but ObjectOntext.ExecuteStoreQuery might be what you're looking for:
http://msdn.microsoft.com/en-us/library/dd487208.aspx
This should let you execute an arbitrary query against your database, and then map the results to an arbitrary type that you specify (even if it's not otherwise mapped in EF).
It goes without saying that you would be responsible for putting together a query that supplied the necessary columns for mapping into the destination type, and also adjusting said query when this type changes.
Here's some further discussion concerning its usage
http://social.msdn.microsoft.com/Forums/en-US/adonetefx/thread/44cf5582-63f8-4f81-8029-7b43469c028d/
Have you considered mapping all of these tables (with the identical columns) into an inheritance relationship in EF, and then querying them as
db.BaseTypes.OfType<SpecificType>().Where(/*.....*/);

Categories