n:m with payload in Entity Framework 4.x











up vote
2
down vote

favorite
1












I am trying to create the following relation in EF4.x



"one Material is made of many Materials to a certain amount and each Material can be used in Materials"



ideally that would convert to Material n:m Material but the Content is a payload, so I have translated that to:



"Material 1:n MaterialUsage" and "MaterialUsage m:1 Material"



I have created two tables since I have a payload (certain amount)



'Material' and 'MaterialUsage'



in Material I have defined a relation 'IsMadeOf' which links to 'MaterialUsage.IsUsedIn' and
a relation 'IsUsedFor' which links to 'MaterialUsage.IsMadeOf'



in MaterialUsage I have aside of the two above described the filed 'Content'.



Now to my problem:



If I delete a Material I run into an error message essentially saying that within the Association 'MaterialMaterialUsage', which identifies the relation "Material.IsUsedFor <-> MaterialUsage.IsMadeOf", a relation is in status 'Deleted' and due to the multiplicity definition a corresponding 'MaterialUsage' record must be in status 'Deleted' as well, which was not found.



My intention is however to delete the material and all 'MaterialUsages' which are identified thru 'Material.IsMadeOf'. Which does not include the materials referenced but only the 'MaterialUsage' records that have a reference in 'IsUsedIN' to the Material to be deleted.



Now I am trying to find a clear way to do so. I am guessing it could work with referential integrity but I am not too familiar with that and therefor I am lost.



I can change the DB-Design no problem.










share|improve this question




























    up vote
    2
    down vote

    favorite
    1












    I am trying to create the following relation in EF4.x



    "one Material is made of many Materials to a certain amount and each Material can be used in Materials"



    ideally that would convert to Material n:m Material but the Content is a payload, so I have translated that to:



    "Material 1:n MaterialUsage" and "MaterialUsage m:1 Material"



    I have created two tables since I have a payload (certain amount)



    'Material' and 'MaterialUsage'



    in Material I have defined a relation 'IsMadeOf' which links to 'MaterialUsage.IsUsedIn' and
    a relation 'IsUsedFor' which links to 'MaterialUsage.IsMadeOf'



    in MaterialUsage I have aside of the two above described the filed 'Content'.



    Now to my problem:



    If I delete a Material I run into an error message essentially saying that within the Association 'MaterialMaterialUsage', which identifies the relation "Material.IsUsedFor <-> MaterialUsage.IsMadeOf", a relation is in status 'Deleted' and due to the multiplicity definition a corresponding 'MaterialUsage' record must be in status 'Deleted' as well, which was not found.



    My intention is however to delete the material and all 'MaterialUsages' which are identified thru 'Material.IsMadeOf'. Which does not include the materials referenced but only the 'MaterialUsage' records that have a reference in 'IsUsedIN' to the Material to be deleted.



    Now I am trying to find a clear way to do so. I am guessing it could work with referential integrity but I am not too familiar with that and therefor I am lost.



    I can change the DB-Design no problem.










    share|improve this question


























      up vote
      2
      down vote

      favorite
      1









      up vote
      2
      down vote

      favorite
      1






      1





      I am trying to create the following relation in EF4.x



      "one Material is made of many Materials to a certain amount and each Material can be used in Materials"



      ideally that would convert to Material n:m Material but the Content is a payload, so I have translated that to:



      "Material 1:n MaterialUsage" and "MaterialUsage m:1 Material"



      I have created two tables since I have a payload (certain amount)



      'Material' and 'MaterialUsage'



      in Material I have defined a relation 'IsMadeOf' which links to 'MaterialUsage.IsUsedIn' and
      a relation 'IsUsedFor' which links to 'MaterialUsage.IsMadeOf'



      in MaterialUsage I have aside of the two above described the filed 'Content'.



      Now to my problem:



      If I delete a Material I run into an error message essentially saying that within the Association 'MaterialMaterialUsage', which identifies the relation "Material.IsUsedFor <-> MaterialUsage.IsMadeOf", a relation is in status 'Deleted' and due to the multiplicity definition a corresponding 'MaterialUsage' record must be in status 'Deleted' as well, which was not found.



      My intention is however to delete the material and all 'MaterialUsages' which are identified thru 'Material.IsMadeOf'. Which does not include the materials referenced but only the 'MaterialUsage' records that have a reference in 'IsUsedIN' to the Material to be deleted.



      Now I am trying to find a clear way to do so. I am guessing it could work with referential integrity but I am not too familiar with that and therefor I am lost.



      I can change the DB-Design no problem.










      share|improve this question















      I am trying to create the following relation in EF4.x



      "one Material is made of many Materials to a certain amount and each Material can be used in Materials"



      ideally that would convert to Material n:m Material but the Content is a payload, so I have translated that to:



      "Material 1:n MaterialUsage" and "MaterialUsage m:1 Material"



      I have created two tables since I have a payload (certain amount)



      'Material' and 'MaterialUsage'



      in Material I have defined a relation 'IsMadeOf' which links to 'MaterialUsage.IsUsedIn' and
      a relation 'IsUsedFor' which links to 'MaterialUsage.IsMadeOf'



      in MaterialUsage I have aside of the two above described the filed 'Content'.



      Now to my problem:



      If I delete a Material I run into an error message essentially saying that within the Association 'MaterialMaterialUsage', which identifies the relation "Material.IsUsedFor <-> MaterialUsage.IsMadeOf", a relation is in status 'Deleted' and due to the multiplicity definition a corresponding 'MaterialUsage' record must be in status 'Deleted' as well, which was not found.



      My intention is however to delete the material and all 'MaterialUsages' which are identified thru 'Material.IsMadeOf'. Which does not include the materials referenced but only the 'MaterialUsage' records that have a reference in 'IsUsedIN' to the Material to be deleted.



      Now I am trying to find a clear way to do so. I am guessing it could work with referential integrity but I am not too familiar with that and therefor I am lost.



      I can change the DB-Design no problem.







      entity-framework database-design c#-4.0






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Nov 19 at 16:30









      Cœur

      17.2k9102141




      17.2k9102141










      asked Jul 21 '11 at 10:44









      mph

      396




      396
























          1 Answer
          1






          active

          oldest

          votes

















          up vote
          1
          down vote



          accepted










          I'll try a halfbaked mix of answer and question. In EF 4.1 with DbContext API I would create the following model classes (I hope I understood your description correctly):



          public class Material
          {
          public int Id { get; set; }
          public string Name { get; set; }
          public ICollection<MaterialUsage> IsMadeOf { get; set; }
          public ICollection<MaterialUsage> IsUsedFor { get; set; }
          }

          public class MaterialUsage
          {
          public int Id { get; set; }
          public int Content { get; set; }
          public Material IsUsedIn { get; set; }
          public Material IsMadeOf { get; set; }
          }


          And this derived context and mapping:



          public class MyContext : DbContext
          {
          public DbSet<Material> Materials { get; set; }
          public DbSet<MaterialUsage> MaterialUsages { get; set; }

          protected override void OnModelCreating(DbModelBuilder modelBuilder)
          {
          modelBuilder.Entity<Material>()
          .HasMany(m => m.IsMadeOf)
          .WithRequired(m => m.IsUsedIn)
          .WillCascadeOnDelete(false);

          modelBuilder.Entity<Material>()
          .HasMany(m => m.IsUsedFor)
          .WithRequired(m => m.IsMadeOf)
          .WillCascadeOnDelete(false);
          }
          }


          I've set the navigation properties in MaterialUsage to Required because I think that a MaterialUsage cannot exist without a reference to the materials. Is that right? As far as I can see it is necessary to turn off cascading delete, otherwise EF will complain about multipe possible cascading delete paths which are not allowed.



          Now, to create materials and their relationships something like this would work:



          using (var context = new MyContext())
          {
          var copper = new Material { Name = "Copper" };
          context.Materials.Add(copper);

          var zinc = new Material { Name = "Zinc" };
          context.Materials.Add(zinc);

          var brass = new Material
          {
          Name = "Brass",
          IsMadeOf = new List<MaterialUsage>
          {
          new MaterialUsage { Content = 10, IsMadeOf = copper },
          new MaterialUsage { Content = 20, IsMadeOf = zinc }
          }
          };
          context.Materials.Add(brass);

          context.SaveChanges();
          }


          The result in the database is:



          Table Materials          Table MaterialUsages

          Id Name Id Content IsUsedIn_Id IsMadeOf_Id
          --------- -------------------------------------------
          1 Brass 1 10 1 2
          2 Copper 2 20 1 3
          3 Zinc


          Now, deleting is difficult because Material appears in both relationships. Especially I don't know how you could accomplish this:




          My intention is however to delete the material and all
          'MaterialUsages' which are identified thru 'Material.IsMadeOf'




          If I understand this correctly you would like to do something like this to delete zinc:



          var zinc = context.Materials
          .Include(m => m.IsMadeOf)
          .Where(m => m.Name == "Zinc")
          .Single();
          foreach (var usage in zinc.IsMadeOf.ToList())
          context.MaterialUsages.Remove(usage);
          context.Materials.Remove(zinc);
          context.SaveChanges();


          This doesn't work because Zinc is made of nothing (the IsMadeOf collection empty, so the loop above does nothing). But if you remove zinc now you violate a constraint namely that zinc is used for brass. (Id = 2 in the MaterialUsages table cannot exist without zinc.)



          I my opinion you must also delete the MaterialUsages which are identified by Material.IsUsedFor:



          var zinc = context.Materials
          .Include(m => m.IsMadeOf)
          .Include(m => m.IsUsedFor)
          .Where(m => m.Name == "Zinc")
          .Single();
          foreach (var usage in zinc.IsMadeOf.ToList())
          context.MaterialUsages.Remove(usage);
          foreach (var usage in zinc.IsUsedFor.ToList())
          context.MaterialUsages.Remove(usage);
          context.Materials.Remove(zinc);
          context.SaveChanges();


          This would delete Id = 3 in the Materials table and also Id = 2 in the MaterialsUsages table, fullfilling the referential constraints now.



          Not sure if this is what you want.



          Edit



          I believe I see now: You actually want to have the exception which is thrown due to the violated constraint when you would delete zinc. Because: It should not be allowed to delete a material as long as it is used in another material (zinc is used in brass, therefore it's forbidden to delete zinc as long as brass is in the database). OK, then replacing zinc by brass in the example would work indeed:



          var brass = context.Materials
          .Include(m => m.IsMadeOf)
          .Where(m => m.Name == "Brass")
          .Single();
          foreach (var usage in brass.IsMadeOf.ToList())
          context.MaterialUsages.Remove(usage);
          context.Materials.Remove(brass);
          context.SaveChanges();


          It just deletes both rows in the MaterialUsages table and brass in the Material table.



          Edit 2



          If you want to check if the material to delete is used for any other material you could test this before you actually try to delete:



          if (context.Materials
          .Where(m => m.Name == "Brass")
          .Select(m => !m.IsUsedFor.Any())
          .Single())
          {
          // the code snippet above
          }
          else
          {
          // "Brass" cannot be deleted since it is used for other materials...
          }





          share|improve this answer























          • Thank you for the detailed answer.
            – mph
            Jul 25 '11 at 7:52










          • Sorry - got tricked by the system. Thank you for the detailed answer. I am very happy that you confirm that the cascading delete is violating EF rules. And I have also been working on a delete that complies to the referential constrains. And the idea of using an Exception for detecting a violation is actually appealing. Since I am not allowed to post any pictures I couldn't post the EF designer representation of the model which you have correctly setup. However I wonder if the include in your edit is actually needed. Doesn't the codeforeachcodewith the code.ToList()code force the load?
            – mph
            Jul 25 '11 at 8:04










          • @mph: The Include isn't needed if you use lazy loading (your navigation properties had to be virtual to enable lazy loading). However you can still leverage the Include also with lazy loading: You would only have one roundtrip to the DB, with lazy loading you have two roundtrips. Instead of catching and evaluating the exception you could also check if the material you want to delete is used for any other material before you try to delete it (I would actually prefer this way). I've added an example in my answer how to check this.
            – Slauma
            Jul 25 '11 at 11:22













          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',
          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%2f6774794%2fnm-with-payload-in-entity-framework-4-x%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








          up vote
          1
          down vote



          accepted










          I'll try a halfbaked mix of answer and question. In EF 4.1 with DbContext API I would create the following model classes (I hope I understood your description correctly):



          public class Material
          {
          public int Id { get; set; }
          public string Name { get; set; }
          public ICollection<MaterialUsage> IsMadeOf { get; set; }
          public ICollection<MaterialUsage> IsUsedFor { get; set; }
          }

          public class MaterialUsage
          {
          public int Id { get; set; }
          public int Content { get; set; }
          public Material IsUsedIn { get; set; }
          public Material IsMadeOf { get; set; }
          }


          And this derived context and mapping:



          public class MyContext : DbContext
          {
          public DbSet<Material> Materials { get; set; }
          public DbSet<MaterialUsage> MaterialUsages { get; set; }

          protected override void OnModelCreating(DbModelBuilder modelBuilder)
          {
          modelBuilder.Entity<Material>()
          .HasMany(m => m.IsMadeOf)
          .WithRequired(m => m.IsUsedIn)
          .WillCascadeOnDelete(false);

          modelBuilder.Entity<Material>()
          .HasMany(m => m.IsUsedFor)
          .WithRequired(m => m.IsMadeOf)
          .WillCascadeOnDelete(false);
          }
          }


          I've set the navigation properties in MaterialUsage to Required because I think that a MaterialUsage cannot exist without a reference to the materials. Is that right? As far as I can see it is necessary to turn off cascading delete, otherwise EF will complain about multipe possible cascading delete paths which are not allowed.



          Now, to create materials and their relationships something like this would work:



          using (var context = new MyContext())
          {
          var copper = new Material { Name = "Copper" };
          context.Materials.Add(copper);

          var zinc = new Material { Name = "Zinc" };
          context.Materials.Add(zinc);

          var brass = new Material
          {
          Name = "Brass",
          IsMadeOf = new List<MaterialUsage>
          {
          new MaterialUsage { Content = 10, IsMadeOf = copper },
          new MaterialUsage { Content = 20, IsMadeOf = zinc }
          }
          };
          context.Materials.Add(brass);

          context.SaveChanges();
          }


          The result in the database is:



          Table Materials          Table MaterialUsages

          Id Name Id Content IsUsedIn_Id IsMadeOf_Id
          --------- -------------------------------------------
          1 Brass 1 10 1 2
          2 Copper 2 20 1 3
          3 Zinc


          Now, deleting is difficult because Material appears in both relationships. Especially I don't know how you could accomplish this:




          My intention is however to delete the material and all
          'MaterialUsages' which are identified thru 'Material.IsMadeOf'




          If I understand this correctly you would like to do something like this to delete zinc:



          var zinc = context.Materials
          .Include(m => m.IsMadeOf)
          .Where(m => m.Name == "Zinc")
          .Single();
          foreach (var usage in zinc.IsMadeOf.ToList())
          context.MaterialUsages.Remove(usage);
          context.Materials.Remove(zinc);
          context.SaveChanges();


          This doesn't work because Zinc is made of nothing (the IsMadeOf collection empty, so the loop above does nothing). But if you remove zinc now you violate a constraint namely that zinc is used for brass. (Id = 2 in the MaterialUsages table cannot exist without zinc.)



          I my opinion you must also delete the MaterialUsages which are identified by Material.IsUsedFor:



          var zinc = context.Materials
          .Include(m => m.IsMadeOf)
          .Include(m => m.IsUsedFor)
          .Where(m => m.Name == "Zinc")
          .Single();
          foreach (var usage in zinc.IsMadeOf.ToList())
          context.MaterialUsages.Remove(usage);
          foreach (var usage in zinc.IsUsedFor.ToList())
          context.MaterialUsages.Remove(usage);
          context.Materials.Remove(zinc);
          context.SaveChanges();


          This would delete Id = 3 in the Materials table and also Id = 2 in the MaterialsUsages table, fullfilling the referential constraints now.



          Not sure if this is what you want.



          Edit



          I believe I see now: You actually want to have the exception which is thrown due to the violated constraint when you would delete zinc. Because: It should not be allowed to delete a material as long as it is used in another material (zinc is used in brass, therefore it's forbidden to delete zinc as long as brass is in the database). OK, then replacing zinc by brass in the example would work indeed:



          var brass = context.Materials
          .Include(m => m.IsMadeOf)
          .Where(m => m.Name == "Brass")
          .Single();
          foreach (var usage in brass.IsMadeOf.ToList())
          context.MaterialUsages.Remove(usage);
          context.Materials.Remove(brass);
          context.SaveChanges();


          It just deletes both rows in the MaterialUsages table and brass in the Material table.



          Edit 2



          If you want to check if the material to delete is used for any other material you could test this before you actually try to delete:



          if (context.Materials
          .Where(m => m.Name == "Brass")
          .Select(m => !m.IsUsedFor.Any())
          .Single())
          {
          // the code snippet above
          }
          else
          {
          // "Brass" cannot be deleted since it is used for other materials...
          }





          share|improve this answer























          • Thank you for the detailed answer.
            – mph
            Jul 25 '11 at 7:52










          • Sorry - got tricked by the system. Thank you for the detailed answer. I am very happy that you confirm that the cascading delete is violating EF rules. And I have also been working on a delete that complies to the referential constrains. And the idea of using an Exception for detecting a violation is actually appealing. Since I am not allowed to post any pictures I couldn't post the EF designer representation of the model which you have correctly setup. However I wonder if the include in your edit is actually needed. Doesn't the codeforeachcodewith the code.ToList()code force the load?
            – mph
            Jul 25 '11 at 8:04










          • @mph: The Include isn't needed if you use lazy loading (your navigation properties had to be virtual to enable lazy loading). However you can still leverage the Include also with lazy loading: You would only have one roundtrip to the DB, with lazy loading you have two roundtrips. Instead of catching and evaluating the exception you could also check if the material you want to delete is used for any other material before you try to delete it (I would actually prefer this way). I've added an example in my answer how to check this.
            – Slauma
            Jul 25 '11 at 11:22

















          up vote
          1
          down vote



          accepted










          I'll try a halfbaked mix of answer and question. In EF 4.1 with DbContext API I would create the following model classes (I hope I understood your description correctly):



          public class Material
          {
          public int Id { get; set; }
          public string Name { get; set; }
          public ICollection<MaterialUsage> IsMadeOf { get; set; }
          public ICollection<MaterialUsage> IsUsedFor { get; set; }
          }

          public class MaterialUsage
          {
          public int Id { get; set; }
          public int Content { get; set; }
          public Material IsUsedIn { get; set; }
          public Material IsMadeOf { get; set; }
          }


          And this derived context and mapping:



          public class MyContext : DbContext
          {
          public DbSet<Material> Materials { get; set; }
          public DbSet<MaterialUsage> MaterialUsages { get; set; }

          protected override void OnModelCreating(DbModelBuilder modelBuilder)
          {
          modelBuilder.Entity<Material>()
          .HasMany(m => m.IsMadeOf)
          .WithRequired(m => m.IsUsedIn)
          .WillCascadeOnDelete(false);

          modelBuilder.Entity<Material>()
          .HasMany(m => m.IsUsedFor)
          .WithRequired(m => m.IsMadeOf)
          .WillCascadeOnDelete(false);
          }
          }


          I've set the navigation properties in MaterialUsage to Required because I think that a MaterialUsage cannot exist without a reference to the materials. Is that right? As far as I can see it is necessary to turn off cascading delete, otherwise EF will complain about multipe possible cascading delete paths which are not allowed.



          Now, to create materials and their relationships something like this would work:



          using (var context = new MyContext())
          {
          var copper = new Material { Name = "Copper" };
          context.Materials.Add(copper);

          var zinc = new Material { Name = "Zinc" };
          context.Materials.Add(zinc);

          var brass = new Material
          {
          Name = "Brass",
          IsMadeOf = new List<MaterialUsage>
          {
          new MaterialUsage { Content = 10, IsMadeOf = copper },
          new MaterialUsage { Content = 20, IsMadeOf = zinc }
          }
          };
          context.Materials.Add(brass);

          context.SaveChanges();
          }


          The result in the database is:



          Table Materials          Table MaterialUsages

          Id Name Id Content IsUsedIn_Id IsMadeOf_Id
          --------- -------------------------------------------
          1 Brass 1 10 1 2
          2 Copper 2 20 1 3
          3 Zinc


          Now, deleting is difficult because Material appears in both relationships. Especially I don't know how you could accomplish this:




          My intention is however to delete the material and all
          'MaterialUsages' which are identified thru 'Material.IsMadeOf'




          If I understand this correctly you would like to do something like this to delete zinc:



          var zinc = context.Materials
          .Include(m => m.IsMadeOf)
          .Where(m => m.Name == "Zinc")
          .Single();
          foreach (var usage in zinc.IsMadeOf.ToList())
          context.MaterialUsages.Remove(usage);
          context.Materials.Remove(zinc);
          context.SaveChanges();


          This doesn't work because Zinc is made of nothing (the IsMadeOf collection empty, so the loop above does nothing). But if you remove zinc now you violate a constraint namely that zinc is used for brass. (Id = 2 in the MaterialUsages table cannot exist without zinc.)



          I my opinion you must also delete the MaterialUsages which are identified by Material.IsUsedFor:



          var zinc = context.Materials
          .Include(m => m.IsMadeOf)
          .Include(m => m.IsUsedFor)
          .Where(m => m.Name == "Zinc")
          .Single();
          foreach (var usage in zinc.IsMadeOf.ToList())
          context.MaterialUsages.Remove(usage);
          foreach (var usage in zinc.IsUsedFor.ToList())
          context.MaterialUsages.Remove(usage);
          context.Materials.Remove(zinc);
          context.SaveChanges();


          This would delete Id = 3 in the Materials table and also Id = 2 in the MaterialsUsages table, fullfilling the referential constraints now.



          Not sure if this is what you want.



          Edit



          I believe I see now: You actually want to have the exception which is thrown due to the violated constraint when you would delete zinc. Because: It should not be allowed to delete a material as long as it is used in another material (zinc is used in brass, therefore it's forbidden to delete zinc as long as brass is in the database). OK, then replacing zinc by brass in the example would work indeed:



          var brass = context.Materials
          .Include(m => m.IsMadeOf)
          .Where(m => m.Name == "Brass")
          .Single();
          foreach (var usage in brass.IsMadeOf.ToList())
          context.MaterialUsages.Remove(usage);
          context.Materials.Remove(brass);
          context.SaveChanges();


          It just deletes both rows in the MaterialUsages table and brass in the Material table.



          Edit 2



          If you want to check if the material to delete is used for any other material you could test this before you actually try to delete:



          if (context.Materials
          .Where(m => m.Name == "Brass")
          .Select(m => !m.IsUsedFor.Any())
          .Single())
          {
          // the code snippet above
          }
          else
          {
          // "Brass" cannot be deleted since it is used for other materials...
          }





          share|improve this answer























          • Thank you for the detailed answer.
            – mph
            Jul 25 '11 at 7:52










          • Sorry - got tricked by the system. Thank you for the detailed answer. I am very happy that you confirm that the cascading delete is violating EF rules. And I have also been working on a delete that complies to the referential constrains. And the idea of using an Exception for detecting a violation is actually appealing. Since I am not allowed to post any pictures I couldn't post the EF designer representation of the model which you have correctly setup. However I wonder if the include in your edit is actually needed. Doesn't the codeforeachcodewith the code.ToList()code force the load?
            – mph
            Jul 25 '11 at 8:04










          • @mph: The Include isn't needed if you use lazy loading (your navigation properties had to be virtual to enable lazy loading). However you can still leverage the Include also with lazy loading: You would only have one roundtrip to the DB, with lazy loading you have two roundtrips. Instead of catching and evaluating the exception you could also check if the material you want to delete is used for any other material before you try to delete it (I would actually prefer this way). I've added an example in my answer how to check this.
            – Slauma
            Jul 25 '11 at 11:22















          up vote
          1
          down vote



          accepted







          up vote
          1
          down vote



          accepted






          I'll try a halfbaked mix of answer and question. In EF 4.1 with DbContext API I would create the following model classes (I hope I understood your description correctly):



          public class Material
          {
          public int Id { get; set; }
          public string Name { get; set; }
          public ICollection<MaterialUsage> IsMadeOf { get; set; }
          public ICollection<MaterialUsage> IsUsedFor { get; set; }
          }

          public class MaterialUsage
          {
          public int Id { get; set; }
          public int Content { get; set; }
          public Material IsUsedIn { get; set; }
          public Material IsMadeOf { get; set; }
          }


          And this derived context and mapping:



          public class MyContext : DbContext
          {
          public DbSet<Material> Materials { get; set; }
          public DbSet<MaterialUsage> MaterialUsages { get; set; }

          protected override void OnModelCreating(DbModelBuilder modelBuilder)
          {
          modelBuilder.Entity<Material>()
          .HasMany(m => m.IsMadeOf)
          .WithRequired(m => m.IsUsedIn)
          .WillCascadeOnDelete(false);

          modelBuilder.Entity<Material>()
          .HasMany(m => m.IsUsedFor)
          .WithRequired(m => m.IsMadeOf)
          .WillCascadeOnDelete(false);
          }
          }


          I've set the navigation properties in MaterialUsage to Required because I think that a MaterialUsage cannot exist without a reference to the materials. Is that right? As far as I can see it is necessary to turn off cascading delete, otherwise EF will complain about multipe possible cascading delete paths which are not allowed.



          Now, to create materials and their relationships something like this would work:



          using (var context = new MyContext())
          {
          var copper = new Material { Name = "Copper" };
          context.Materials.Add(copper);

          var zinc = new Material { Name = "Zinc" };
          context.Materials.Add(zinc);

          var brass = new Material
          {
          Name = "Brass",
          IsMadeOf = new List<MaterialUsage>
          {
          new MaterialUsage { Content = 10, IsMadeOf = copper },
          new MaterialUsage { Content = 20, IsMadeOf = zinc }
          }
          };
          context.Materials.Add(brass);

          context.SaveChanges();
          }


          The result in the database is:



          Table Materials          Table MaterialUsages

          Id Name Id Content IsUsedIn_Id IsMadeOf_Id
          --------- -------------------------------------------
          1 Brass 1 10 1 2
          2 Copper 2 20 1 3
          3 Zinc


          Now, deleting is difficult because Material appears in both relationships. Especially I don't know how you could accomplish this:




          My intention is however to delete the material and all
          'MaterialUsages' which are identified thru 'Material.IsMadeOf'




          If I understand this correctly you would like to do something like this to delete zinc:



          var zinc = context.Materials
          .Include(m => m.IsMadeOf)
          .Where(m => m.Name == "Zinc")
          .Single();
          foreach (var usage in zinc.IsMadeOf.ToList())
          context.MaterialUsages.Remove(usage);
          context.Materials.Remove(zinc);
          context.SaveChanges();


          This doesn't work because Zinc is made of nothing (the IsMadeOf collection empty, so the loop above does nothing). But if you remove zinc now you violate a constraint namely that zinc is used for brass. (Id = 2 in the MaterialUsages table cannot exist without zinc.)



          I my opinion you must also delete the MaterialUsages which are identified by Material.IsUsedFor:



          var zinc = context.Materials
          .Include(m => m.IsMadeOf)
          .Include(m => m.IsUsedFor)
          .Where(m => m.Name == "Zinc")
          .Single();
          foreach (var usage in zinc.IsMadeOf.ToList())
          context.MaterialUsages.Remove(usage);
          foreach (var usage in zinc.IsUsedFor.ToList())
          context.MaterialUsages.Remove(usage);
          context.Materials.Remove(zinc);
          context.SaveChanges();


          This would delete Id = 3 in the Materials table and also Id = 2 in the MaterialsUsages table, fullfilling the referential constraints now.



          Not sure if this is what you want.



          Edit



          I believe I see now: You actually want to have the exception which is thrown due to the violated constraint when you would delete zinc. Because: It should not be allowed to delete a material as long as it is used in another material (zinc is used in brass, therefore it's forbidden to delete zinc as long as brass is in the database). OK, then replacing zinc by brass in the example would work indeed:



          var brass = context.Materials
          .Include(m => m.IsMadeOf)
          .Where(m => m.Name == "Brass")
          .Single();
          foreach (var usage in brass.IsMadeOf.ToList())
          context.MaterialUsages.Remove(usage);
          context.Materials.Remove(brass);
          context.SaveChanges();


          It just deletes both rows in the MaterialUsages table and brass in the Material table.



          Edit 2



          If you want to check if the material to delete is used for any other material you could test this before you actually try to delete:



          if (context.Materials
          .Where(m => m.Name == "Brass")
          .Select(m => !m.IsUsedFor.Any())
          .Single())
          {
          // the code snippet above
          }
          else
          {
          // "Brass" cannot be deleted since it is used for other materials...
          }





          share|improve this answer














          I'll try a halfbaked mix of answer and question. In EF 4.1 with DbContext API I would create the following model classes (I hope I understood your description correctly):



          public class Material
          {
          public int Id { get; set; }
          public string Name { get; set; }
          public ICollection<MaterialUsage> IsMadeOf { get; set; }
          public ICollection<MaterialUsage> IsUsedFor { get; set; }
          }

          public class MaterialUsage
          {
          public int Id { get; set; }
          public int Content { get; set; }
          public Material IsUsedIn { get; set; }
          public Material IsMadeOf { get; set; }
          }


          And this derived context and mapping:



          public class MyContext : DbContext
          {
          public DbSet<Material> Materials { get; set; }
          public DbSet<MaterialUsage> MaterialUsages { get; set; }

          protected override void OnModelCreating(DbModelBuilder modelBuilder)
          {
          modelBuilder.Entity<Material>()
          .HasMany(m => m.IsMadeOf)
          .WithRequired(m => m.IsUsedIn)
          .WillCascadeOnDelete(false);

          modelBuilder.Entity<Material>()
          .HasMany(m => m.IsUsedFor)
          .WithRequired(m => m.IsMadeOf)
          .WillCascadeOnDelete(false);
          }
          }


          I've set the navigation properties in MaterialUsage to Required because I think that a MaterialUsage cannot exist without a reference to the materials. Is that right? As far as I can see it is necessary to turn off cascading delete, otherwise EF will complain about multipe possible cascading delete paths which are not allowed.



          Now, to create materials and their relationships something like this would work:



          using (var context = new MyContext())
          {
          var copper = new Material { Name = "Copper" };
          context.Materials.Add(copper);

          var zinc = new Material { Name = "Zinc" };
          context.Materials.Add(zinc);

          var brass = new Material
          {
          Name = "Brass",
          IsMadeOf = new List<MaterialUsage>
          {
          new MaterialUsage { Content = 10, IsMadeOf = copper },
          new MaterialUsage { Content = 20, IsMadeOf = zinc }
          }
          };
          context.Materials.Add(brass);

          context.SaveChanges();
          }


          The result in the database is:



          Table Materials          Table MaterialUsages

          Id Name Id Content IsUsedIn_Id IsMadeOf_Id
          --------- -------------------------------------------
          1 Brass 1 10 1 2
          2 Copper 2 20 1 3
          3 Zinc


          Now, deleting is difficult because Material appears in both relationships. Especially I don't know how you could accomplish this:




          My intention is however to delete the material and all
          'MaterialUsages' which are identified thru 'Material.IsMadeOf'




          If I understand this correctly you would like to do something like this to delete zinc:



          var zinc = context.Materials
          .Include(m => m.IsMadeOf)
          .Where(m => m.Name == "Zinc")
          .Single();
          foreach (var usage in zinc.IsMadeOf.ToList())
          context.MaterialUsages.Remove(usage);
          context.Materials.Remove(zinc);
          context.SaveChanges();


          This doesn't work because Zinc is made of nothing (the IsMadeOf collection empty, so the loop above does nothing). But if you remove zinc now you violate a constraint namely that zinc is used for brass. (Id = 2 in the MaterialUsages table cannot exist without zinc.)



          I my opinion you must also delete the MaterialUsages which are identified by Material.IsUsedFor:



          var zinc = context.Materials
          .Include(m => m.IsMadeOf)
          .Include(m => m.IsUsedFor)
          .Where(m => m.Name == "Zinc")
          .Single();
          foreach (var usage in zinc.IsMadeOf.ToList())
          context.MaterialUsages.Remove(usage);
          foreach (var usage in zinc.IsUsedFor.ToList())
          context.MaterialUsages.Remove(usage);
          context.Materials.Remove(zinc);
          context.SaveChanges();


          This would delete Id = 3 in the Materials table and also Id = 2 in the MaterialsUsages table, fullfilling the referential constraints now.



          Not sure if this is what you want.



          Edit



          I believe I see now: You actually want to have the exception which is thrown due to the violated constraint when you would delete zinc. Because: It should not be allowed to delete a material as long as it is used in another material (zinc is used in brass, therefore it's forbidden to delete zinc as long as brass is in the database). OK, then replacing zinc by brass in the example would work indeed:



          var brass = context.Materials
          .Include(m => m.IsMadeOf)
          .Where(m => m.Name == "Brass")
          .Single();
          foreach (var usage in brass.IsMadeOf.ToList())
          context.MaterialUsages.Remove(usage);
          context.Materials.Remove(brass);
          context.SaveChanges();


          It just deletes both rows in the MaterialUsages table and brass in the Material table.



          Edit 2



          If you want to check if the material to delete is used for any other material you could test this before you actually try to delete:



          if (context.Materials
          .Where(m => m.Name == "Brass")
          .Select(m => !m.IsUsedFor.Any())
          .Single())
          {
          // the code snippet above
          }
          else
          {
          // "Brass" cannot be deleted since it is used for other materials...
          }






          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Jul 25 '11 at 11:21

























          answered Jul 21 '11 at 12:34









          Slauma

          146k51355383




          146k51355383












          • Thank you for the detailed answer.
            – mph
            Jul 25 '11 at 7:52










          • Sorry - got tricked by the system. Thank you for the detailed answer. I am very happy that you confirm that the cascading delete is violating EF rules. And I have also been working on a delete that complies to the referential constrains. And the idea of using an Exception for detecting a violation is actually appealing. Since I am not allowed to post any pictures I couldn't post the EF designer representation of the model which you have correctly setup. However I wonder if the include in your edit is actually needed. Doesn't the codeforeachcodewith the code.ToList()code force the load?
            – mph
            Jul 25 '11 at 8:04










          • @mph: The Include isn't needed if you use lazy loading (your navigation properties had to be virtual to enable lazy loading). However you can still leverage the Include also with lazy loading: You would only have one roundtrip to the DB, with lazy loading you have two roundtrips. Instead of catching and evaluating the exception you could also check if the material you want to delete is used for any other material before you try to delete it (I would actually prefer this way). I've added an example in my answer how to check this.
            – Slauma
            Jul 25 '11 at 11:22




















          • Thank you for the detailed answer.
            – mph
            Jul 25 '11 at 7:52










          • Sorry - got tricked by the system. Thank you for the detailed answer. I am very happy that you confirm that the cascading delete is violating EF rules. And I have also been working on a delete that complies to the referential constrains. And the idea of using an Exception for detecting a violation is actually appealing. Since I am not allowed to post any pictures I couldn't post the EF designer representation of the model which you have correctly setup. However I wonder if the include in your edit is actually needed. Doesn't the codeforeachcodewith the code.ToList()code force the load?
            – mph
            Jul 25 '11 at 8:04










          • @mph: The Include isn't needed if you use lazy loading (your navigation properties had to be virtual to enable lazy loading). However you can still leverage the Include also with lazy loading: You would only have one roundtrip to the DB, with lazy loading you have two roundtrips. Instead of catching and evaluating the exception you could also check if the material you want to delete is used for any other material before you try to delete it (I would actually prefer this way). I've added an example in my answer how to check this.
            – Slauma
            Jul 25 '11 at 11:22


















          Thank you for the detailed answer.
          – mph
          Jul 25 '11 at 7:52




          Thank you for the detailed answer.
          – mph
          Jul 25 '11 at 7:52












          Sorry - got tricked by the system. Thank you for the detailed answer. I am very happy that you confirm that the cascading delete is violating EF rules. And I have also been working on a delete that complies to the referential constrains. And the idea of using an Exception for detecting a violation is actually appealing. Since I am not allowed to post any pictures I couldn't post the EF designer representation of the model which you have correctly setup. However I wonder if the include in your edit is actually needed. Doesn't the codeforeachcodewith the code.ToList()code force the load?
          – mph
          Jul 25 '11 at 8:04




          Sorry - got tricked by the system. Thank you for the detailed answer. I am very happy that you confirm that the cascading delete is violating EF rules. And I have also been working on a delete that complies to the referential constrains. And the idea of using an Exception for detecting a violation is actually appealing. Since I am not allowed to post any pictures I couldn't post the EF designer representation of the model which you have correctly setup. However I wonder if the include in your edit is actually needed. Doesn't the codeforeachcodewith the code.ToList()code force the load?
          – mph
          Jul 25 '11 at 8:04












          @mph: The Include isn't needed if you use lazy loading (your navigation properties had to be virtual to enable lazy loading). However you can still leverage the Include also with lazy loading: You would only have one roundtrip to the DB, with lazy loading you have two roundtrips. Instead of catching and evaluating the exception you could also check if the material you want to delete is used for any other material before you try to delete it (I would actually prefer this way). I've added an example in my answer how to check this.
          – Slauma
          Jul 25 '11 at 11:22






          @mph: The Include isn't needed if you use lazy loading (your navigation properties had to be virtual to enable lazy loading). However you can still leverage the Include also with lazy loading: You would only have one roundtrip to the DB, with lazy loading you have two roundtrips. Instead of catching and evaluating the exception you could also check if the material you want to delete is used for any other material before you try to delete it (I would actually prefer this way). I've added an example in my answer how to check this.
          – Slauma
          Jul 25 '11 at 11:22




















          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%2f6774794%2fnm-with-payload-in-entity-framework-4-x%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

          Costa Masnaga

          Fotorealismo

          Sidney Franklin