删除在多个实体上拆分的表行时出错

时间:2015-01-31 23:34:44

标签: c# entity-framework

我想删除在两个实体上拆分的表格行。

如果我尝试删除主要实体,如果在我未使用context.Entry(...).Reference加载相关的其他实体之前,我会收到错误。

在我要删除整行之前检索相关实体有点傻吗?

如果我继续评论context.Entry(...)

,则会出现以下错误
  

遇到无效数据。缺少必需的关系。检查   State Entries确定约束违规的来源。

我添加以下代码。可以请某人帮我删除拆分实体,而不必在之前“加载”相关实体吗?

 using System.Data.Entity;
 using System.Linq;

 namespace Split
 {
   class Program
   {
     static void Main(string[] args)
     {
        using (var context = new DataContext())
        {
            var product = new Product()
            {
                Name = "my Article",
                Photo = new ProductPhoto() { PhotoUrl = "http://myfoto.jpg" }
            };

            context.Products.Add(product);
            context.SaveChanges();
        }

        using (var context = new DataContext())
        {
            var product = context.Products.First();
            //context.Entry(product).Reference(e => e.Photo).Load();
            context.Products.Remove(product);
            context.SaveChanges();
        }
     }
   }

  class Product
  {
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
    public virtual ProductPhoto Photo { get; set; }
  }

  class ProductPhoto
  {
    public virtual int ProductId { 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<ProductPhoto> ProductPhotos { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Product>()
            .ToTable("Products")
            .HasKey(e => e.Id)
            .HasRequired(e => e.Photo)
            .WithRequiredPrincipal(e => e.Product);

        modelBuilder.Entity<ProductPhoto>()
            .ToTable("Products")
            .HasKey(e => e.ProductId);

        base.OnModelCreating(modelBuilder);
     }
   }
}

6 个答案:

答案 0 :(得分:4)

执行此操作的最佳方法是使用存根实体:实体对象只能获得Id值:

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

如果您知道Product的ID,您甚至可以通过仅创建两个存根来删除Product及其ProductPhoto

答案 1 :(得分:0)

可能会像这样加载产品:

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

保存一行,但仍会从数据库加载照片。

答案 2 :(得分:0)

尝试将级联删除规则添加到模型中。您必须在数据库中有相应的DELETE规则,以避免将依赖项加载到内存中as noted here.

答案 3 :(得分:0)

Gert Arnold建议的解决方案就是内存 存根一个用于实体,另一个用于相关的子实体 我粘贴了一个新的代码(见评论)

我发布了Gert Arnold提出的想法的解决方案,也许代码可以优化,我试图尽可能多地使用它。

如果您的实体包含任何并发令牌,那么这些属性也是 用于构造DELETE语句。您仍然可以使用存根实体 方法,但您需要为并发令牌设置值 属性也是如此。

引自:“编程实体框架:DbContext 由Julia Lerman和Rowan Miller(O'Reilly)。版权所有2012 Julia Lerman和 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);
        }

    }
}

答案 4 :(得分:0)

  

用此代码替换覆盖方法并检查

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

答案 5 :(得分:0)

我知道这不是“正确的”答案,但是由于麻烦的是需要进行表拆分,并且在删除时会出错,所以我只用了一个很好的旧SQL命令:

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

当您花费数小时试图使一个小功能正常工作时,总是一个选择!