Error deleting a table row splitted on multiple entities

前端 未结 6 960
耶瑟儿~
耶瑟儿~ 2021-01-06 05:18

I want to delete a table row that is split on two entities.

If I try to delete the main entity, I get an error if before I don\'t load the related other entity using

相关标签:
6条回答
  • 2021-01-06 05:30

    Replace your override method with this code and check

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
           modelBuilder.Entity<Product>() // one-to-one
               .ToTable("Products")
               .HasKey(e => e.Id)
               .HasRequired(e => e.Info)
               .WithRequiredDependent(e => e.Product);
    
           modelBuilder.Entity<ProductInfo>() // map to the same table Products
               .ToTable("Products")
               .HasKey(e => e.Id);
    
           //add this code
           modelBuilder.Entity<Product>()
                .HasRequired(p => p.Photo) // "Photo"  defined in Product class for ProductPhoto class's object name
                .WithRequiredPrincipal(c => c.Product);// "Product"  defined in ProductPhoto class for Product's class object name
    
    
                base.OnModelCreating(modelBuilder);
    }
    
    0 讨论(0)
  • 2021-01-06 05:31

    Possibly load the product like so:

    var product = context.Products.Include(x => x.Photo).First();
    

    Saves a line but will still load the photo from the db.

    0 讨论(0)
  • 2021-01-06 05:45

    Hi the path to solve as Gert Arnold suggest is to have in memory stubs one for the entity and another for related sub entities I had pasted a new code with that works (see comments)

    I posted the solution with the idea suggested by Gert Arnold, perhaps the code could be optimized, I tried to make it as much generic that I can.

    If your entity contains any concurrency tokens, these properties are also used to construct the DELETE statement. You can still use the stub entity approach, but you will need to set values for the concurrency token properties as well.

    Quoted from: “Programming Entity Framework: DbContext by Julia Lerman and Rowan Miller (O’Reilly). Copyright 2012 Julia Lerman and Rowan Miller, 978-1-449-31296-1.”

    using System;
    using System.Data.Entity;
    using System.Linq;
    using System.Reflection;
    
    namespace Split
    {
        class Program
        {
            static void Main()
            {
                Database.SetInitializer(new DropCreateDatabaseAlways<DataContext>());
    
                const int id = 1;
                const string split = "Info"; // contract: if the entity being delete has an Info property then the row has been splitted
    
                using (var context = new DataContext()) // Add
                {
                    var product = new Product 
                    {
                        Name = "my Article 1",
                        Info = new ProductInfo { PhotoUrl = "http://myphoto.jpg" } // when adding an entity the subEntity MUST BE included on the graph
                    };
    
                    context.Products.Add(product);
                    context.SaveChanges();
                }
    
                using (var context = new DataContext())
                {
                    var product = context.Products.Find(id);
                    context.Entry(product).Reference(e => e.Info).Load(); // when adding an entity the subEntity COULD BE OR NOT included on the graph, no need to include it if we are not going to modify it
    
                    product.Name = "MY ARTICULE 1";
                    product.Info.PhotoUrl = "HTTP://MYPHOTO.JPG";
                    context.Entry(product).State = EntityState.Modified;
                    context.SaveChanges();
                }
    
                using (var context = new DataContext())
                {
                    PropertyInfo propertyInfo;
    
                    context.Products.Find(id); // uncoment bring it to memory and test with entity in memory
    
                    var entity = context.Products.Local.FirstOrDefault(e => e.Id == id);
    
                    context.Entry(entity).Reference(e => e.Info).Load();
    
                    if (entity != null)                                      // there is a entity already yet in memory
                    {
                        propertyInfo = entity.GetType().GetProperty(split);  // contract
    
                        if (propertyInfo != null)
                        {
                            var subEntity = propertyInfo.GetValue(entity);         // get subEntity from entity Info property
                            context.Entry(subEntity).State = EntityState.Detached; // remove sub entity from ChangeTracker API
                            propertyInfo.SetValue(entity, null);                   // remove subEntity and relationship
                        }
    
                        context.Entry(entity).State = EntityState.Detached;  // remove entity from ChangeTracker API
                    }
    
                    entity = new Product { Id = id };                        // new entity stub
                    propertyInfo = entity.GetType().GetProperty(split);      // contract:
                    if (propertyInfo != null)
                    {
                        propertyInfo.SetValue(entity, null);                 // remove subEntity and and relationship
    
                        var subEntity = Activator.CreateInstance(propertyInfo.PropertyType); // create a new subEntity stub
                        subEntity.GetType().GetProperty("Id").SetValue(subEntity, id);       // set the foreinkey relation
                        context.Entry(subEntity).State = EntityState.Deleted;                // mark as deleted on context
                    }
    
                    context.Entry(entity).State = EntityState.Deleted;       // delete the entity
                    context.SaveChanges();
                }
            }
        }
    
        class Product
        {
            public virtual int Id { get; set; }
            public virtual string Name { get; set; }
    
            public virtual ProductInfo Info { get; set; }
        }
    
        class ProductInfo
        {
            public virtual int Id { get; set; }
            public virtual string PhotoUrl { get; set; }
            public virtual Product Product { get; set; }
        }
    
        class DataContext : DbContext
        {
            public DataContext()
                : base("name=DefaultConnection")
            {
                Configuration.ProxyCreationEnabled = false;
                Configuration.LazyLoadingEnabled = false;
    
    
            }
            public DbSet<Product> Products { get; set; }
            public DbSet<ProductInfo> ProductInfos { get; set; }
    
            protected override void OnModelCreating(DbModelBuilder modelBuilder)
            {
                modelBuilder.Entity<Product>() // one-to-one
                    .ToTable("Products")
                    .HasKey(e => e.Id)
                    .HasRequired(e => e.Info)
                    .WithRequiredDependent(e => e.Product);
    
                modelBuilder.Entity<ProductInfo>() // map to the same table Products
                    .ToTable("Products")
                    .HasKey(e => e.Id);
    
                base.OnModelCreating(modelBuilder);
            }
    
        }
    }
    
    0 讨论(0)
  • 2021-01-06 05:45

    Try to add Cascade Delete rule to the model. You MUST have a corresponding DELETE rule in the database to avoid loading of the dependents into memory as noted here.

    0 讨论(0)
  • 2021-01-06 05:50

    I know this isn't the "correct" answer, but with all the hassle of getting table-splitting working and getting errors on deleting, I just went with a good old SQL command:

    DbContext.Database.ExecuteSqlCommand($"DELETE FROM Products WHERE Id = {id}");
    

    Always an option when you're spending hours trying to get a small feature working!

    0 讨论(0)
  • 2021-01-06 05:53

    The best way to do this is by using a stub entity: an entity object that's only got an Id value:

    var product = context.Products.First();
    var photo = new ProductPhoto { ProductId = product.ProductId }; // Stub
    context.Entry(photo).State = System.Data.Entity.EntityState.Deleted;
    context.Products.Remove(product);
    context.SaveChanges();
    

    If you know a Product's Id you can even delete both the Product and its ProductPhoto by only creating two stubs.

    0 讨论(0)
提交回复
热议问题