EF Core - Enforce priority in executing commands in a transaction
I want to delete 2 set of data in database, using EF Core.
All codes are hypothetical.
Data models:
class Parent
public int Id get; set;
class Child
public int Id get; set;
public int ParentId get; set;
public virtual Parent Parent get; set;
public bool Flag get; set;
Let's assume I want to delete all [Child] records with (ParentId=100) and (flag=false), after that if (child.ParentId=100).length=0 then delete the parent itself too.
So, here is the service class:
class Service
public void Command(int parentId)
Parent parent = GetParent(parentId);
List<Child> children = GetChildren(parent);
List<Child> toDelete = children.Where(x => !x.Flag).ToList();
foreach(var child in toDelete)
var entry = DbContext.Entry(child);
entry.State = EntityState.Deleted;
List<Child> remainChildren = children.Where(x => x.Flag).ToList();
if (!remainChildren.Any())
var entry = DbContext.Entry(parent );
entry.State = EntityState.Deleted;
SaveChanges();
I have multiple scenarios that call the Service.Command method.
Because I call SaveChanges()
only once, I assume that all delete operations will be executed in a single transaction, and of course they would be in this order:
- Delete child records
- Delete parent
but EF send queries to database like this:
- Delete parent
- Delete child records
Obviously it will throw an ForeignKey exception.
Is there any way to enforce EF Core to execute queries in order that I wrote the code?
c# entity-framework asp.net-core ef-core-2.1
add a comment |
I want to delete 2 set of data in database, using EF Core.
All codes are hypothetical.
Data models:
class Parent
public int Id get; set;
class Child
public int Id get; set;
public int ParentId get; set;
public virtual Parent Parent get; set;
public bool Flag get; set;
Let's assume I want to delete all [Child] records with (ParentId=100) and (flag=false), after that if (child.ParentId=100).length=0 then delete the parent itself too.
So, here is the service class:
class Service
public void Command(int parentId)
Parent parent = GetParent(parentId);
List<Child> children = GetChildren(parent);
List<Child> toDelete = children.Where(x => !x.Flag).ToList();
foreach(var child in toDelete)
var entry = DbContext.Entry(child);
entry.State = EntityState.Deleted;
List<Child> remainChildren = children.Where(x => x.Flag).ToList();
if (!remainChildren.Any())
var entry = DbContext.Entry(parent );
entry.State = EntityState.Deleted;
SaveChanges();
I have multiple scenarios that call the Service.Command method.
Because I call SaveChanges()
only once, I assume that all delete operations will be executed in a single transaction, and of course they would be in this order:
- Delete child records
- Delete parent
but EF send queries to database like this:
- Delete parent
- Delete child records
Obviously it will throw an ForeignKey exception.
Is there any way to enforce EF Core to execute queries in order that I wrote the code?
c# entity-framework asp.net-core ef-core-2.1
What do your repository and SetState methods do? Also why are you "getting" the children twice?
– Wurd
Nov 12 at 9:50
@Wurd I changed the code to be more clear. Please take a look again.
– Mohammad Azhdari
Nov 12 at 9:59
Instead of manually deleting child records use OnDelete(DeleteBehavior.Cascade). For required relations that's the default behaviour. Cascading works even with untracked entities
– Panagiotis Kanavos
Nov 12 at 10:27
BTW the question's code doesn't delete entities - there's no call toDbContext.Remove
which means EF doesn't know anything about the order. The code treats all entities as if they were detached, even thoughGetChildren
would have to load child records from the database. I suspectGetChildren
is a read-only method incorrectly used to remove entities from the database.
– Panagiotis Kanavos
Nov 12 at 10:32
add a comment |
I want to delete 2 set of data in database, using EF Core.
All codes are hypothetical.
Data models:
class Parent
public int Id get; set;
class Child
public int Id get; set;
public int ParentId get; set;
public virtual Parent Parent get; set;
public bool Flag get; set;
Let's assume I want to delete all [Child] records with (ParentId=100) and (flag=false), after that if (child.ParentId=100).length=0 then delete the parent itself too.
So, here is the service class:
class Service
public void Command(int parentId)
Parent parent = GetParent(parentId);
List<Child> children = GetChildren(parent);
List<Child> toDelete = children.Where(x => !x.Flag).ToList();
foreach(var child in toDelete)
var entry = DbContext.Entry(child);
entry.State = EntityState.Deleted;
List<Child> remainChildren = children.Where(x => x.Flag).ToList();
if (!remainChildren.Any())
var entry = DbContext.Entry(parent );
entry.State = EntityState.Deleted;
SaveChanges();
I have multiple scenarios that call the Service.Command method.
Because I call SaveChanges()
only once, I assume that all delete operations will be executed in a single transaction, and of course they would be in this order:
- Delete child records
- Delete parent
but EF send queries to database like this:
- Delete parent
- Delete child records
Obviously it will throw an ForeignKey exception.
Is there any way to enforce EF Core to execute queries in order that I wrote the code?
c# entity-framework asp.net-core ef-core-2.1
I want to delete 2 set of data in database, using EF Core.
All codes are hypothetical.
Data models:
class Parent
public int Id get; set;
class Child
public int Id get; set;
public int ParentId get; set;
public virtual Parent Parent get; set;
public bool Flag get; set;
Let's assume I want to delete all [Child] records with (ParentId=100) and (flag=false), after that if (child.ParentId=100).length=0 then delete the parent itself too.
So, here is the service class:
class Service
public void Command(int parentId)
Parent parent = GetParent(parentId);
List<Child> children = GetChildren(parent);
List<Child> toDelete = children.Where(x => !x.Flag).ToList();
foreach(var child in toDelete)
var entry = DbContext.Entry(child);
entry.State = EntityState.Deleted;
List<Child> remainChildren = children.Where(x => x.Flag).ToList();
if (!remainChildren.Any())
var entry = DbContext.Entry(parent );
entry.State = EntityState.Deleted;
SaveChanges();
I have multiple scenarios that call the Service.Command method.
Because I call SaveChanges()
only once, I assume that all delete operations will be executed in a single transaction, and of course they would be in this order:
- Delete child records
- Delete parent
but EF send queries to database like this:
- Delete parent
- Delete child records
Obviously it will throw an ForeignKey exception.
Is there any way to enforce EF Core to execute queries in order that I wrote the code?
c# entity-framework asp.net-core ef-core-2.1
c# entity-framework asp.net-core ef-core-2.1
edited Nov 12 at 9:58
asked Nov 12 at 9:46
Mohammad Azhdari
25729
25729
What do your repository and SetState methods do? Also why are you "getting" the children twice?
– Wurd
Nov 12 at 9:50
@Wurd I changed the code to be more clear. Please take a look again.
– Mohammad Azhdari
Nov 12 at 9:59
Instead of manually deleting child records use OnDelete(DeleteBehavior.Cascade). For required relations that's the default behaviour. Cascading works even with untracked entities
– Panagiotis Kanavos
Nov 12 at 10:27
BTW the question's code doesn't delete entities - there's no call toDbContext.Remove
which means EF doesn't know anything about the order. The code treats all entities as if they were detached, even thoughGetChildren
would have to load child records from the database. I suspectGetChildren
is a read-only method incorrectly used to remove entities from the database.
– Panagiotis Kanavos
Nov 12 at 10:32
add a comment |
What do your repository and SetState methods do? Also why are you "getting" the children twice?
– Wurd
Nov 12 at 9:50
@Wurd I changed the code to be more clear. Please take a look again.
– Mohammad Azhdari
Nov 12 at 9:59
Instead of manually deleting child records use OnDelete(DeleteBehavior.Cascade). For required relations that's the default behaviour. Cascading works even with untracked entities
– Panagiotis Kanavos
Nov 12 at 10:27
BTW the question's code doesn't delete entities - there's no call toDbContext.Remove
which means EF doesn't know anything about the order. The code treats all entities as if they were detached, even thoughGetChildren
would have to load child records from the database. I suspectGetChildren
is a read-only method incorrectly used to remove entities from the database.
– Panagiotis Kanavos
Nov 12 at 10:32
What do your repository and SetState methods do? Also why are you "getting" the children twice?
– Wurd
Nov 12 at 9:50
What do your repository and SetState methods do? Also why are you "getting" the children twice?
– Wurd
Nov 12 at 9:50
@Wurd I changed the code to be more clear. Please take a look again.
– Mohammad Azhdari
Nov 12 at 9:59
@Wurd I changed the code to be more clear. Please take a look again.
– Mohammad Azhdari
Nov 12 at 9:59
Instead of manually deleting child records use OnDelete(DeleteBehavior.Cascade). For required relations that's the default behaviour. Cascading works even with untracked entities
– Panagiotis Kanavos
Nov 12 at 10:27
Instead of manually deleting child records use OnDelete(DeleteBehavior.Cascade). For required relations that's the default behaviour. Cascading works even with untracked entities
– Panagiotis Kanavos
Nov 12 at 10:27
BTW the question's code doesn't delete entities - there's no call to
DbContext.Remove
which means EF doesn't know anything about the order. The code treats all entities as if they were detached, even though GetChildren
would have to load child records from the database. I suspect GetChildren
is a read-only method incorrectly used to remove entities from the database.– Panagiotis Kanavos
Nov 12 at 10:32
BTW the question's code doesn't delete entities - there's no call to
DbContext.Remove
which means EF doesn't know anything about the order. The code treats all entities as if they were detached, even though GetChildren
would have to load child records from the database. I suspect GetChildren
is a read-only method incorrectly used to remove entities from the database.– Panagiotis Kanavos
Nov 12 at 10:32
add a comment |
1 Answer
1
active
oldest
votes
Set the parent child relationship to cascade delete at the DB level.
Query the needed data in one hit...
var data = context.Parents.Where(p => p.ParentId == parentId)
.Select(p => new
Parent = p,
ChildrenToRemove = p.Children.Where(c => c.Flag).ToList(),
HasRemainingChildren = p.Children.Any(c => !c.Flag)
).Single();
Then it's just a matter of inspecting the data and acting accordingly. If there are no remaining children, delete the parent and let cascade take care of it. Otherwise, just delete the children from the context.
if(!data.HasRemainingChildren)
context.Parents.Remove(data.Parent);
else
context.Children.RemoveRange(data.ChildrenToRemove);
For big entities you can further optimize this by selecting just the IDs then associating them to new Entity instances, attach them to a fresh DbContext, and then issue the Remove/RemoveRange calls. This option is an optimization for dealing with large numbers of items, or "big" entities that would otherwise result in a lot of data across the wire.
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53259488%2fef-core-enforce-priority-in-executing-commands-in-a-transaction%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
Set the parent child relationship to cascade delete at the DB level.
Query the needed data in one hit...
var data = context.Parents.Where(p => p.ParentId == parentId)
.Select(p => new
Parent = p,
ChildrenToRemove = p.Children.Where(c => c.Flag).ToList(),
HasRemainingChildren = p.Children.Any(c => !c.Flag)
).Single();
Then it's just a matter of inspecting the data and acting accordingly. If there are no remaining children, delete the parent and let cascade take care of it. Otherwise, just delete the children from the context.
if(!data.HasRemainingChildren)
context.Parents.Remove(data.Parent);
else
context.Children.RemoveRange(data.ChildrenToRemove);
For big entities you can further optimize this by selecting just the IDs then associating them to new Entity instances, attach them to a fresh DbContext, and then issue the Remove/RemoveRange calls. This option is an optimization for dealing with large numbers of items, or "big" entities that would otherwise result in a lot of data across the wire.
add a comment |
Set the parent child relationship to cascade delete at the DB level.
Query the needed data in one hit...
var data = context.Parents.Where(p => p.ParentId == parentId)
.Select(p => new
Parent = p,
ChildrenToRemove = p.Children.Where(c => c.Flag).ToList(),
HasRemainingChildren = p.Children.Any(c => !c.Flag)
).Single();
Then it's just a matter of inspecting the data and acting accordingly. If there are no remaining children, delete the parent and let cascade take care of it. Otherwise, just delete the children from the context.
if(!data.HasRemainingChildren)
context.Parents.Remove(data.Parent);
else
context.Children.RemoveRange(data.ChildrenToRemove);
For big entities you can further optimize this by selecting just the IDs then associating them to new Entity instances, attach them to a fresh DbContext, and then issue the Remove/RemoveRange calls. This option is an optimization for dealing with large numbers of items, or "big" entities that would otherwise result in a lot of data across the wire.
add a comment |
Set the parent child relationship to cascade delete at the DB level.
Query the needed data in one hit...
var data = context.Parents.Where(p => p.ParentId == parentId)
.Select(p => new
Parent = p,
ChildrenToRemove = p.Children.Where(c => c.Flag).ToList(),
HasRemainingChildren = p.Children.Any(c => !c.Flag)
).Single();
Then it's just a matter of inspecting the data and acting accordingly. If there are no remaining children, delete the parent and let cascade take care of it. Otherwise, just delete the children from the context.
if(!data.HasRemainingChildren)
context.Parents.Remove(data.Parent);
else
context.Children.RemoveRange(data.ChildrenToRemove);
For big entities you can further optimize this by selecting just the IDs then associating them to new Entity instances, attach them to a fresh DbContext, and then issue the Remove/RemoveRange calls. This option is an optimization for dealing with large numbers of items, or "big" entities that would otherwise result in a lot of data across the wire.
Set the parent child relationship to cascade delete at the DB level.
Query the needed data in one hit...
var data = context.Parents.Where(p => p.ParentId == parentId)
.Select(p => new
Parent = p,
ChildrenToRemove = p.Children.Where(c => c.Flag).ToList(),
HasRemainingChildren = p.Children.Any(c => !c.Flag)
).Single();
Then it's just a matter of inspecting the data and acting accordingly. If there are no remaining children, delete the parent and let cascade take care of it. Otherwise, just delete the children from the context.
if(!data.HasRemainingChildren)
context.Parents.Remove(data.Parent);
else
context.Children.RemoveRange(data.ChildrenToRemove);
For big entities you can further optimize this by selecting just the IDs then associating them to new Entity instances, attach them to a fresh DbContext, and then issue the Remove/RemoveRange calls. This option is an optimization for dealing with large numbers of items, or "big" entities that would otherwise result in a lot of data across the wire.
answered Nov 12 at 10:42
Steve Py
5,19511017
5,19511017
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53259488%2fef-core-enforce-priority-in-executing-commands-in-a-transaction%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
What do your repository and SetState methods do? Also why are you "getting" the children twice?
– Wurd
Nov 12 at 9:50
@Wurd I changed the code to be more clear. Please take a look again.
– Mohammad Azhdari
Nov 12 at 9:59
Instead of manually deleting child records use OnDelete(DeleteBehavior.Cascade). For required relations that's the default behaviour. Cascading works even with untracked entities
– Panagiotis Kanavos
Nov 12 at 10:27
BTW the question's code doesn't delete entities - there's no call to
DbContext.Remove
which means EF doesn't know anything about the order. The code treats all entities as if they were detached, even thoughGetChildren
would have to load child records from the database. I suspectGetChildren
is a read-only method incorrectly used to remove entities from the database.– Panagiotis Kanavos
Nov 12 at 10:32