EF Core 2 Fluent API OnDelete /w Cascade [duplicate] - entity-framework

I am having a few issues with EF Core at the moment. I have some data that I need to delete, and I am struggeling to see how the fluent API works, exactly in regards to the .OnDelete() function.
Considering the classic blog/post scenario from microsofts own websites, I wonder what entity, exactly the OnDelete() is 'targeting' (for the lack of a better word) In some instances it seems to be the blog, in others, the post. Can the Cascade delete be defined from both sides (that the posts are deleted when the parent Blog is) if so i imagine the code should look like this:
model.Entity<Post>().HasOne(p => p.Blog).WithMany(b => b.Posts).HasForeignKey(p => p.BlogId).OnDelete(DeleteBehavior.Cascade)
As i understand this is saying "When a Blog is deleted, first delete all posts referencing this blog" meaning the OnDelete(DeleteBehavior.Cascade)applies to blog, not to post.
But is this the same then?
model.Entity<Blog>().HasMany(b => b.Posts).WithOne(p => p.Blog).OnDelete(DeleteBehavior.Cascade)
or does OnDelete(DeleteBehavior.Cascade) apply to Post rather than blog?

Cascade delete always works in one direction - from principal entity to dependent entity, i.e. deleting the principal entity deletes the dependent entities. And for one-to- many relationships the one side is always the principal and the many side is the dependent.
Looks like you are confused by the fluent configuration. Note that each relationship consists of two ends. The fluent configuration allows you to start with one of the ends and relate it to the other end, or vice versa, but still you are configuring (defining) a single relationship. So
Entity<A>().HasOne(a => a.B).WithMany(b => b.As)
is the same as
Entity<B>().HasMany(b => b.As).WithOne(a => a.B);
and they both define one and the same relationship. Which one you choose doesn't matter, just use single configuration per relationship in order to avoid discrepancies.
With that being said,
model.Entity<Post>().HasOne(p => p.Blog).WithMany(b => b.Posts)
.HasForeignKey(p => p.BlogId)
.OnDelete(DeleteBehavior.Cascade);
and
model.Entity<Blog>().HasMany(b => b.Posts).WithOne(p => p.Blog)
.HasForeignKey(p => p.BlogId)
.OnDelete(DeleteBehavior.Cascade);
is one and the same and define single one-to-many relationship from Blog to Post. Since Blog is the one side and Post is the many side, the Blog is the principal entity and the Post is the dependent entity, hence deleting a Blog will delete the related Posts.
Reference:
Relationships - Definition of terms
Cascade Delete

Related

Cascading Deletes On Filtered Children - EF

Does anyone know what behavior should be expected by attempting to cascade deletes to child entities that are filtered via Map's Requires/HasValue? I'm curious if I can trust that EF(6) will only cascade the delete to children that make it through the filter, or whether the delete would also be pushed do children that are no longer "pending".
Below is my specific mapping configuration for said child entities.
modelBuilder.Entity<PendingReviewRecord&gt().Map(m => m.Requires("Pending").HasValue(true))
.HasRequired(t => t.Install).WithOptional(t => t.PendingReviewRecord).WillCascadeOnDelete(true);
My situation has some further complication in that I am implementing a soft-delete on the "Install" object.. but that is being accomplished via command tree interception so entity still feels it as a delete/removal and not some hacky field modification.

Entity Framework 5, Multiple Models, Same Entity

Ok, so I am new to entity framework...
I have an existing SQL database with some 500 tables in it, and we are in the process of considering a move from Linq->SQL to Entity Framework as our main data access layer. We also want to consider more of a domain driven design approach with separate data contexts managing key areas of the application ( i.e. Sales, Marketing, Jobs, Shipping etc. etc. ).
If we take a common entity such as "Customer", this appears in more than one model. I have two models in my sample app so far. Entity Framework is clever enough to create only one customer class ( we are using the default Poco T4 templates for class generation ), however when I try and run the project I get the following error "Multiple types with the name 'Customer' exist in the EdmItemCollection in different namespaces. Convention based mapping requires unique names without regard to namespace in the EdmItemCollection".
So am I right in thinking that Entity Framework does not allow "Customer" to exist in more than one model ? If I really want customer appearing in more than one model, do I have to start creating different versions of the customer class to deal with it ?
Apologies in advance if this is a dumb question, but I am relatively new to EF.
Thanks...
You said that you are creating DDD with bounded context. In bounded context, you create more than one context with one or more related entities in it. Why do you want to create more than one model with the same name?
Check the Julie Lerman's link for reference:
http://msdn.microsoft.com/en-us/magazine/jj883952.aspx
Sorry if I am out of context. But, in my experience in such a scenario, we have to create two different context such as "MarketingModelContext" and SalesModelContext. MarketingModelContext will have all the dbsets related to marketingmodel along with customer entity. In the same way, SalesModelContext will have all the dbsets related to SalesModel along with customer entity. In this way, you will be creating only one customer entity or POCO which can be used by two contexts independently. This is known as bounded contexts as Julie Lerman calls it. It will help you in separation of context, concerns and helps you with better performance as only required context(fewer entities) can be loaded. The above article will help you with this.
Hope I have answered your query.

EF5 - removing related enities

I'm using EF5 POCO entities (database first not code first, if that makes a difference). Say I have the following (hypothetical) entity types:-
Store
Customer (with a FK of StoreId)
Product (with a FK of StoreId)
Order (with FKs of CustomerId & ProductId)
A Store has many Customers and Products, and an Order is basically a M-M join between Customer and Product.
A typical scenario of mine involves retrieving a Store entity plus all of its related Customers, Products and Orders. With this hierarchy now in memory the user may choose to delete a Customer via the UI. As part of this action, I also want the Customer's related Orders to be deleted.
Is it sufficient to do:-
store.Customers.Remove(customerToDelete);
Or is it also necessary to "break the link" between the customer's Orders and their Products, e.g.:-
foreach (var order in customerToDelete.Orders)
{
order.Product.Orders.Remove(order);
}
Note that the changes won't necessarily be persisted back to the DB straight away. The user may continue to work with this in-memory Store hierarchy, and only persist the changes upon clicking a "Save" button later. Therefore, if they continue to "browse" this hierarchy of entities via the UI, and drill down into a particular product, I don't want them to see orders relating to the removed customer. Hence I suspect I have to do the above foreach to wipe all trace of that customer's orders?
Not sure if it's related to my question, but can someone explain what the "End1 OnDelete" EDMX properties do? I understand how cascade deletes work in SQL, but not sure where this EDMX cascade option fits in?
The context will not automatically remove the orders owned by the Customer entity. Generally you will have a read only context for search results shown in your UI:
DbSet.Where(x => x == y).AsNoTracking()
So there will be no automatic refresh when you delete a customer from your data source as you will be using a fresh context for updates. This pattern is generally accepted now as you won't want to deal with the inevitable exceptions caused by updating your data source from a context containing stale entities. It's best to create a fresh context for a unit of work/scoped set of operations.
You can however set up a cascade delete using the Fluent API
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Course>()
.HasRequired(t => t.Department)
.WithMany(t => t.Courses)
.HasForeignKey(d => d.DepartmentID)
.WillCascadeOnDelete(false);
}
This will do the job of removing the orders from the data source, but your orders visible through the UI will need to be refreshed/removed somehow. If your orders data is quite large then consider lazy loading, as you can defer loading your related entities until they are actually accessed which is great so long as you know what you are doing and don't accidently round trip when iterating through collections for example. Your approach of removing the entities manually after a delete is also fine, however, don't forget to delete the relationship/link separately, as this will hang around and possibly cause an issue.

How can I optimize my EF Code First query?

[Updated - see update at bottom]
I am using EF code-first, and am generally happy with it. However, one simple (and common) operation is causing EF to generate ridiculously complex SQL, which is slowing my application down.
I am simply fetching a set of entities using a list of (integer) IDs, but because I need details of lots of sub-entities, I'm using .Include() to get those sub-entities loaded at the same time, as follows:
db.MyEntities
.Where(x => x.ClientId == clientId)
.Where(x => ids.Contains(x.Id))
.Where(x => x.SubEntity1 != null)
.Include(x => x.SubEntity1)
.Include(x => x.SubEntity1.SubSubEntity1)
.Include(x => x.SubEntity1.SubSubEntity2)
.Include(x => x.SubEntity1.SubSubEntity3)
.Include(x => x.SubEntity1.SubSubEntity4)
.Include(x => x.SubEntity2)
.Include(x => x.SubEntity2.SubSubEntity1)
.Include(x => x.SubEntity2.SubSubEntity2)
.Include(x => x.SubEntity2.SubSubEntity3)
.Include(x => x.SubEntity2.SubSubEntity4)
.Include(x => x.SubEntity3)
As you can see, it's not a particularly complex query, with the exception of all those Includes.
The SQL that EF generates for this is huge - around 74Kb of SQL. It doesn't take very long to execute (since normally the number of items in the list of IDs is small), but it takes EF more than a second just to construct the query - i.e. before the query is even sent to the database.
If I remove the Includes, then the query is much smaller, and the whole thing takes much less time - but the various related entities are then loaded one-at-a-time, which doesn't scale well.
EF seems to give me two options for loading the data:
Load all the sub-entities at once during the initial query (using Include as above), or
Load the sub-entities one-at-a-time (using lazy loading, or explicitly using Load/LoadProperty).
Option 1 would be my preferred option if it worked, but since that doesn't work in this case, my only remaining option is 2 - and I don't think that's acceptable: there would be too many database queries where the input list of IDs (i.e. the number of entities) is large.
It seems to me that there is another option that EF doesn't seem to address: having fetched the main entities, fetch all the relevant SubEntity1 entities, then all the relevant SubEntity2 entities, etc. That way, the number of queries is related to the number of types of entity to be fetched, rather the number of entities. This would scale much better.
I can't see a way to do that in EF: in other words, to say "load this property for all these entities (in a single query)".
Will I just have to give up on EF and write my own SQL?
UPDATE
I have noticed that even if I remove the Includes, the SQL generated is more complex than I think it should be, and I think this all stems from the fact that EF does not 'like' my table structure. I struggled for days to get EF to create the database structure I was looking for via Code First (and the Fluent API), and even when I had got to (nearly) where I wanted to be, I had to accept some compromises.
I think I'm now paying a further penalty for daring to do something that EF didn't want me to do. It looks like a simple query is more complex than it should be, and a slightly-more-complex query is massively more complex.
This is incredibly depressing - I thought I'd left all those EF hassles behind, and the system is now in production with dozens of users - which would make it very difficult for me to start over.
It seems I'm going to have to spend the eternity fighting EF tooth and nail at every turn. How I wish I'd never used it in the first place!
Anyway, back to my original question: if I have a bunch of entities of type A for which I want to load the related sub-entities of type B in one query, is there a way to do that?
How about loading the data using stored procedures? Yes, it is a bit dirty, but this is what I do when I hit performance issues with EF. I hope I'm not missing something in your question.
http://msdn.microsoft.com/en-US/data/jj691402

How to create 1-to-1 relationship in EF code first using a map/link table with fluent configuration

I have a situation where I would like to create a 1-to-1 relationship in Entity Framework (4.2) using code first fluent configuration API, the twist is that I need to use a link/map table to do it.
Here is a diagram of my table layout.
This is what I am after ...
HasOptional(x => x.FieldPerson)
.WithOptionalPrincipal()
.Map(map =>
{
map.ToTable("user_account__field_person");
map.MapLeftKey("user_account_id");
map.MapRightKey("field_person_id");
});
... but there doesn't seem to be a way to do it.
The alternative is to create my 3 entities and manage them myself using joins where appropriate.
Is this even possible or is it a scenario that EF fluent API doesn't handle?
I'm going to answer my own question again!
I don't think EF code first deals with this situation because the whole concept of a link/map table suggests a many-to-many relationship.
For my particular case this is a database-first situation and I "have" to deal with these tables as I cannot change the database as this stage.
What I've done is use a Many-to-Many code first configuration ...
HasMany(t=>t.FieldPersons)
.WithMany(t=>t.UserAccounts)
.Map(map =>
{
map.ToTable("user_account__field_person");
map.MapLeftKey("user_account_id");
map.MapRightKey("field_person_id");
});
... as there is nothing in my database schema to prevent me from creating a many to many relationship. If I did want that behaviour then I would simply need to add a unique constraint on each field (which EF code first doesn't support anyway)