EF Core - Enforce priority in executing commands in a transaction












2














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:




  1. Delete child records

  2. Delete parent


but EF send queries to database like this:




  1. Delete parent

  2. 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?










share|improve this question
























  • What do your repository and SetState methods do? Also why are you "getting" the children twice?
    – Wurd
    Nov 12 '18 at 9:50












  • @Wurd I changed the code to be more clear. Please take a look again.
    – Mohammad Azhdari
    Nov 12 '18 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 '18 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 '18 at 10:32
















2














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:




  1. Delete child records

  2. Delete parent


but EF send queries to database like this:




  1. Delete parent

  2. 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?










share|improve this question
























  • What do your repository and SetState methods do? Also why are you "getting" the children twice?
    – Wurd
    Nov 12 '18 at 9:50












  • @Wurd I changed the code to be more clear. Please take a look again.
    – Mohammad Azhdari
    Nov 12 '18 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 '18 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 '18 at 10:32














2












2








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:




  1. Delete child records

  2. Delete parent


but EF send queries to database like this:




  1. Delete parent

  2. 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?










share|improve this question















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:




  1. Delete child records

  2. Delete parent


but EF send queries to database like this:




  1. Delete parent

  2. 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






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 12 '18 at 9:58







Mohammad Azhdari

















asked Nov 12 '18 at 9:46









Mohammad AzhdariMohammad Azhdari

25729




25729












  • What do your repository and SetState methods do? Also why are you "getting" the children twice?
    – Wurd
    Nov 12 '18 at 9:50












  • @Wurd I changed the code to be more clear. Please take a look again.
    – Mohammad Azhdari
    Nov 12 '18 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 '18 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 '18 at 10:32


















  • What do your repository and SetState methods do? Also why are you "getting" the children twice?
    – Wurd
    Nov 12 '18 at 9:50












  • @Wurd I changed the code to be more clear. Please take a look again.
    – Mohammad Azhdari
    Nov 12 '18 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 '18 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 '18 at 10:32
















What do your repository and SetState methods do? Also why are you "getting" the children twice?
– Wurd
Nov 12 '18 at 9:50






What do your repository and SetState methods do? Also why are you "getting" the children twice?
– Wurd
Nov 12 '18 at 9:50














@Wurd I changed the code to be more clear. Please take a look again.
– Mohammad Azhdari
Nov 12 '18 at 9:59






@Wurd I changed the code to be more clear. Please take a look again.
– Mohammad Azhdari
Nov 12 '18 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 '18 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 '18 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 '18 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 '18 at 10:32












1 Answer
1






active

oldest

votes


















1














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.






share|improve this answer





















    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
    });


    }
    });














    draft saved

    draft discarded


















    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









    1














    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.






    share|improve this answer


























      1














      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.






      share|improve this answer
























        1












        1








        1






        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.






        share|improve this answer












        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.







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Nov 12 '18 at 10:42









        Steve PySteve Py

        5,22511017




        5,22511017






























            draft saved

            draft discarded




















































            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.




            draft saved


            draft discarded














            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





















































            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







            Popular posts from this blog

            Full-time equivalent

            Bicuculline

            さくらももこ