Best way to save a sometimes store-generated column - entity-framework

I have an entity that has one column with strange behavior. On insert sometimes it is set to a value passed by the application and sometimes it uses the newly-created identity value from another column. This is currently implemented using a stored procedure. Is there a clean way to do this with Entity Framework?
I'm using Entity Framework 6 with Code First, but I'm not doing database generation or using migrations.
I know that I can essentially use a DbContext.Database to manually call a procedure as if I'm not using EF, but then I lose all of EF's automatic updates of the entity on save, participation in a SaveChanges transaction, etc. What's the best way to mitigate this?
Edit:
I will try to describe my ideal solution in pseudo code. The question is really how close can I get to this with real code given Entity Framework 6's current functionality.
Ideally I could just configure a column to sometimes be an "identity" column. In other words, sometimes generated by the database on insert, sometimes not. Something like this:
public sealed class WidgetConfiguration : EntityTypeConfiguration<Widget>
{
public WidgetConfiguration()
{
HasKey(d => d.Id);
Property(e => e.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Property(e => e.Number)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.SometimesIdentity);
MapToStoredProcedures(s => s.Insert(i => i.HasName("InsertWidget")));
}
}
Since this is not possible, I'm wondering if there is some other way of participating in SaveChanges so that I can take advantage of the EF goodness like dependency analysis and automatic transaction management rather than:
Creating a transaction
Populating a context with new/changed entities that a Widget depends on and saving it.
Inserting the Widget.
Populating a context with new/changed entities that depend on the Widget and saving it.
Committing the transaction.
In other words, is it possible to tell EF, "Hey, I know how to save Widgets. Here's a callback when you need to save one." or something like that?

EF does not refresh the entities that may be affected by a StoredProcedure you have to do it manually by calling:
dbContext.Entry(myEntityToRefresh).Reload();
//or
dbContext.Entry(myEntityToRefresh).ReloadAsync();
Please change the title of this question. It says nothing.

Related

How does EntityFramework Core mange data internally?

I'm trying to understand how EntityFramework Core manages data internally because it influences how I call DbSets. Particularly, does it refer to in-memory data or re-query the database every time?
Example 1)
If I call _context.ToDo.Where(x => x.id == 123).First() and then in a different procedure call the same command again, will EF give me the in-memory value or re-query the DB?
Example 2)
If I call _context.ToDo.Where(x => x.id == 123).First() and then a few lines later call _context.ToDo.Find(123).Where(x => x.id == 123).Incude(x => x.Children).First(), will it use the in-memeory and then only query the DB for "Children" or does it recall the entire dataset?
I guess I'm wondering if it matters if I duplicate a call or not?
Is this affected by the AsNoTracking() switch?
What you really ask is how caching works in EF Core, not how DbContext manages data.
EF always offered 1st level caching - it kept the entities it loaded in memory, as long as the context remains alive. That's how it can track changes and save all of them when SaveChanges is called.
It doesn't cache the query itself, so it doesn't know that Where(....).First() is meant to return those specific entities. You'd have to use Find() instead. If tracking is disabled, no entities are kept around.
This is explained in Querying and Finding Entities, especially Finding entities using primary keys:
The Find method on DbSet uses the primary key value to attempt to find an entity tracked by the context. If the entity is not found in the context then a query will be sent to the database to find the entity there. Null is returned if the entity is not found in the context or in the database.
Find is different from using a query in two significant ways:
A round-trip to the database will only be made if the entity with the given key is not found in the context.
Find will return entities that are in the Added state. That is, Find will return entities that have been added to the context but have not yet been saved to the database.
In Example #2 the queries are different though. Include forces eager loading, so the results and entities returned are different. There's no need to call that a second time though, if the first entity and context are still around. You could just iterate over the Children property and EF would load the related entities one by one, using lazy loading.
EF will execute 1 query for each child item it loads. If you need to load all of them, this is slow. Slow enough to be have its own name, the N+1 selects problem. To avoid this you can load a related collection explicitly using explicit loading, eg. :
_context.Entry(todo).Collection(t=>t.Children).Load();
When you know you're going to use all children though, it's better to eagerly load all entities with Include().

How to properly use EFCore with SignalR Core (avoid caching entities)

I just found some really strange behaviour which turns out it is not so strange at all.
My select statement (query from database) worked only the first time. The second time, query from database was cached.
Inside Hub method I read something from database every 10 seconds and return result to all connected clients. But if some API change this data, Hub context does not read actual data.
In this thread I found this:
When you use EF it by default loads each entity only once per context. The first query creates entity instance and stores it internally. Any subsequent query which requires entity with the same key returns this stored instance. If values in the data store changed you still receive the entity with values from the initial query. This is called Identity map pattern. You can force the object context to reload the entity but it will reload a single shared instance.
So my question is how to properly use EFCore inside SignalR Core hub method?
I could use AsNoTracking, but I would like to use some global setting. Developer can easily forget to add AsNoTracking and this could mean serving outdated data to user.
I would like to write some code in my BaseHub class which will tell context do not track data. If I change entity properties, SaveChanges should update data. Can this be achieved? It is hard to think all the time to add AsNoTracking when querying from hub method.
I would like to write some code in my BaseHub class which will tell context do not track data.
The default query tracking behavior is controlled by the ChangeTracker.QueryTrackingBehavior property with default value of TrackAll (i.e. tracking).
You can change it to NoTracking and then use AsTracking() for queries that need tracking. It's a matter of which are more commonly needed.
If I change entity properties, SaveChanges should update data.
This is not possible if the entity is not tracked.
If you actually want tracking queries with "database wins" strategy, I'm afraid it's not possible currently in EF Core. I think EF6 object context services had an option for specifying the "client wins" vs "database wins" strategy, but EF Core currently does not provide such control and always implements "client wins" strategy.

Why is ON DELETE SET NULL still not implemented in the Entity Framework 6? Is there a snag?

It still is not possible to configure a relation with the ON DELETE SET NULL rule using Entity Framework code first. As a workaround you have to load all the related entities in memory and then on deletion of the parent entity EF will issue SQL commands to set their foreign keys to Null.
This, while it is trivial to implement this yourself using something like:
protected override void Seed(Context context)
{
context.Database.ExecuteSqlCommand("ALTER TABLE dbo.Guests DROP CONSTRAINT Guest_PreferredLanguage");
context.Database.ExecuteSqlCommand("ALTER TABLE dbo.Guests ADD CONSTRAINT Guest_PreferredLanguage FOREIGN KEY (LanguageID) REFERENCES dbo.Languages(LanguageID) ON UPDATE NO ACTION ON DELETE SET NULL");
}
(Example take from this post.)
I can see no problems with this approach: Loaded child entities will remain in sync with the database because EF will update (set to null) their foreign keys and Reference properties, and that other records in the database are affected does no harm as they have not been loaded anyway.
So, why is this feature still missing then? Is there some hidden snag?
The feature is probably not implemented because normally changes only affect the objects which are actually in the unit of work. Cascades are not scalable.
And I also think soft deletes are better in most cases. Maybe thats something for you?
You might also want to look into Domain Driven design. That also covers the correct use of units of work (with aggregates).
Btw your solution edits the database in the seed method. It might be better to do that a Up() method of a migration.
This feature is available in Microsoft.EntityFrameworkCore Version=3.1.10.0 onwards.
modelBuilder.Entity<Guests>()
.HasOne<Languages>(g => g.Language)
.WithMany(l => l.Guests)
.HasForeignKey(g => g.LanguageID)
.IsRequired(false)
.OnDelete(DeleteBehavior.SetNull);
Note, DeleteBehavior.SetNull

How can I update a single field from an entity with Entity Framework?

I need to load an object from the database, modify some of its fields and relations, and then I want to store a new value for only one field, without modifying the rest.
It would look something like this:
var thing = db.Things.First();
thing.Field1 = "asdas";
thing.Field2 = 23;
thing.OtherThings.Add(new OtherThing());
thing.FieldToUpdate = doSomething(thing);
db.SaveChanges();
But that would save all the changes, what I want is to only save FieldToUpdate...
I've looked around and all I've found is to use stored procedures, which seems like too much for something that looks so simple, besides I would have to make a different stored procedure for each time I need to do something like this...
My current solution is to open another context, load the thing again, update the FieldToUpdate and SaveChanges, but that's both inefficient and ugly.
If you want to do this with attached entity you have to update FieldToUpdate FIRST and call SaveChanges. Than you can update other fields and call SaveChanges again if needed. No other way with attached entity.
Other way you can try is to detach entity, modify what you want to (it will not track changes). Then attach entity back to context and call:
// I suppose that db is ObjectContext or inherited type
db.ObjectStateManager.GetObjectStateEntry(thing).SetModifiedProperty("FieldToUpdate");
Now only FieldToUpdate is tracked as changed.
The Entity Framework is smart enough to figure out what has changed and what hasn't and optimizes the SQL statement is uses accordingly. If you only change FieldToUpdate, then the SQL statement will only be an update on the single field, not on everything.
However, if you do change Field1 and Field2 from what they were originally, they will be persisted too, but ONLY if they changed. Otherwise, there's no need to tell the DB to change it to what it already is.
Entity framework does it this way because that's exactly what the developer wants 99.9% of the time. If you are going to use an entity object as an object that you want to move around and manipulate in ways other than treating it as a model of the database (like it should be), then you may want to consider creating another new wrapper class that lets you mess with all the data fields that you want (and have others that aren't in there), and then have the save method of it do the proper entity framework persistance, to keep things separate and clean.

Entity Framework and Temporary Objects

I'm new to the Entity Framework and am currently experimenting with it. I created a simple database, set up the model in VS2008, and have got the code going to query the database using the EF as well as inserting new data.
There's one thing that has me a little confused though. I have an entity (set up in my model) called Customer, and as part of the logic of my application I want to be able to create a temporary Customer object for some intermediate processing. This particular object should never actually be stored in the database. However, I noticed that as soon as I call SaveChanges() the customer is saved to the database. This isn't what I want to happen. I'd be quite happy to call AddCustomer() on the objects I do want to include - I just want to have the option to create a temporary instance for my own use.
I did discover I could call Detach() and pass in my temporary instance, which would stop it from being persisted. However I'm not sure this is the best way to do this since the temporary Customer object will have related objects, and unless I go through and detach them all I might end up in hot water.
It's possible I'm misunderstanding something about how the EF is supposed to work, or that I'm missing something obvious - I'm hoping someone can set me straight!
Thanks
John
If you want to have a temporary instance of an entity that'll never be connected to the EF again, use this Entity Cloner for cloning the entity
If you are trying to disconnect an entity, send it over the wire some where (let us say pass it over to the client over a service, to modify it, and then again get it back), and again merge back the changes to the EF - right now this is not directly supported. How ever, you can try these solutions
Entity Bag:
EFContrib (you need PostSharp4EF)
Why not have another Customer class with the same fields?
Just ran into this problem myself with a service using EF4 - there's a simpler solution - after you create the new entity instance, call
objectContext.Detach(newEntity);