简体   繁体   English

实体框架代码首次级联删除一对多

[英]Entity Framework Code First Cascading Delete One To Many

I am attempting to create a small demo solution to experiment with EF CF Cascading deletes. 我正在尝试创建一个小型演示解决方案来试验EF CF Cascading删除。

With the code I have written I am getting the following error when trying to add a Person with 2 cars. 使用我编写的代码,我在尝试添加2辆车的人时遇到以下错误。

The aim is to add a person with 2 cars. 目的是增加一个人2辆车。 Then delete the person and linked cars get deleted at the same time. 然后删除该人并同时删除链接的汽车。

System.InvalidCastException: Unable to cast object of type 'System.Collections.Generic.List`1[EF_Cascading_Delete_Experiment.Car]' to type 'EF_Cascading_Delete_Experiment.Car'. System.InvalidCastException:无法转换类型为'System.Collections.Generic.List`1 [EF_Cascading_Delete_Experiment.Car]'的对象以键入'EF_Cascading_Delete_Experiment.Car'。

I am trying to build a simple example where there is a Person with a list of Cars 我正在尝试构建一个简单的示例,其中有一个带有汽车列表的人员

Here are my Person & Car Classes: 这是我的人员和汽车类:

public class Person
{
    [Key]
    public int Id { get; set; }
    public string Name { get; set; }
    public List<Car> Cars { get; set; }
}
public class Car
{
    [Key]
    public int id { get; set; }
    public string CarName { get; set; }
}

Here is my simplistic code trying to ad a Person with 2 cars: 这是我的简单代码,试图为一个有2辆车的人做广告:

public static void CarTest()
    {
        using (Model1 db = new Model1())
        {
            Person personToAdd = new Person();
            personToAdd.Name = "trev";
            personToAdd.Cars = new List<Car>();

            Car car1 = new Car
            {
                CarName = "Vectra"
            };

            Car car2 = new Car
            {
                CarName = "Focus"
            };

            personToAdd.Cars.Add(car1);
            personToAdd.Cars.Add(car2);

            db.Person.Add(personToAdd);

            db.SaveChanges();
        }
    }

The error occurs at the line 错误发生在该行

db.Person.Add(personToAdd);

This is my DbContext: 这是我的DbContext:

public class Model1 : DbContext
{
    // Your context has been configured to use a 'Model1' connection string from your application's 
    // configuration file (App.config or Web.config). By default, this connection string targets the 
    // 'EF_Cascading_Delete_Experiment.Model1' database on your LocalDb instance. 
    // 
    // If you wish to target a different database and/or database provider, modify the 'Model1' 
    // connection string in the application configuration file.
    public Model1()
        : base("name=Model1")
    {
    }

    // Add a DbSet for each entity type that you want to include in your model. For more information 
    // on configuring and using a Code First model, see http://go.microsoft.com/fwlink/?LinkId=390109.

    public virtual DbSet<Person> Person { get; set; }
    public virtual DbSet<Car> Car { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Person>()
            .HasOptional(a => a.Cars)
            .WithOptionalDependent()
            .WillCascadeOnDelete(true);
    }
}

The migration code generated by EF looks like this: EF生成的迁移代码如下所示:

public partial class addedbackeverythingincludingcascadingdelete : DbMigration
{
    public override void Up()
    {
        CreateTable(
            "dbo.Cars",
            c => new
                {
                    id = c.Int(nullable: false, identity: true),
                    CarName = c.String(),
                })
            .PrimaryKey(t => t.id);

        CreateTable(
            "dbo.People",
            c => new
                {
                    Id = c.Int(nullable: false, identity: true),
                    Name = c.String(),
                    Cars_id = c.Int(),
                })
            .PrimaryKey(t => t.Id)
            .ForeignKey("dbo.Cars", t => t.Cars_id, cascadeDelete: true)
            .Index(t => t.Cars_id);

    }

    public override void Down()
    {
        DropForeignKey("dbo.People", "Cars_id", "dbo.Cars");
        DropIndex("dbo.People", new[] { "Cars_id" });
        DropTable("dbo.People");
        DropTable("dbo.Cars");
    }
}

To me, it looks like the migration code is not correct? 对我来说,看起来迁移代码不正确? Which would have been generated based on my Person & Car class. 哪个会根据我的Person&Car类生成。 But I cannot work out why? 但我无法解释为什么?

When I look at the tables in the database they look wrong. 当我查看数据库中的表时,它们看起来是错误的。

在此输入图像描述

Surely in the Car table there should be a PersonId? 当然在Car表中应该有一个PersonId? Not a CarId in the Person table? 在Person表中不是CarId?

SOLUTION: 解:

With many thanks to Ivan this was my solution. 非常感谢伊万,这是我的解决方案。 I have put it here so I can mark his question as the answer. 我把它放在这里所以我可以将他的问题标记为答案。

My classes now look like this: 我的课程现在看起来像这样:

public class Person
{
    [Key]
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual List<Car> Cars { get; set; }
}
public class Car
{
    [Key]
    public int id { get; set; }
    public string CarName { get; set; }
}

When testing, even though Ivan says I shouldn't need it I found the cascading delete wouldn't work unless I kept the this code: 在测试时,即使Ivan说我不需要它,我发现级联删除不起作用,除非我保留这段代码:

modelBuilder.Entity<Person>()
            .HasMany(a => a.Cars)
            .WithOptional() // or `WithRequired() in case Car requires Person
            .WillCascadeOnDelete(true);

The fluent relationship configuration 流畅的关系配置

modelBuilder.Entity<Person>()
    .HasOptional(a => a.Cars)
    .WithOptionalDependent()
    .WillCascadeOnDelete(true);

is wrong. 是错的。 HasOptional , HasRequired , WithOptionalDependent , WithOptionalPrincipal etc. are for one-to-one relationships, while you have one-to-many . HasOptionalHasRequiredWithOptionalDependentWithOptionalPrincipal等是one-to-one关系,而你有one-to-many

The correct configuration is as follows: 正确的配置如下:

modelBuilder.Entity<Person>()
    .HasMany(a => a.Cars)
    .WithOptional() // or `WithRequired() in case Car requires Person
    .WillCascadeOnDelete(true);

Now the migration should look like this: 现在迁移应该如下所示:

CreateTable(
    "dbo.People",
    c => new
        {
            Id = c.Int(nullable: false, identity: true),
            Name = c.String(),
        })
    .PrimaryKey(t => t.Id);

CreateTable(
    "dbo.Cars",
    c => new
        {
            id = c.Int(nullable: false, identity: true),
            CarName = c.String(),
            Person_Id = c.Int(),
        })
    .PrimaryKey(t => t.id)
    .ForeignKey("dbo.People", t => t.Person_Id, cascadeDelete: true)
    .Index(t => t.Person_Id);

Actually You haven't added Person property in your car model class. 实际上您没有在汽车模型类中添加Person属性。 Your model should be like this. 你的模型应该是这样的。

See Here. 看这里。 Cascade delete in one to many relationship 级联删除一对多关系

public class Person
{
    [Key]
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual ICollection<Car> Cars { get; set; }
}
public class Car
{
    [Key]
    public int id { get; set; }
    public string CarName { get; set; }
    public virtual Person Person { get; set;}
}

Now run migration. 现在运行迁移。 And Test it. 并测试它。

First, i think it's better to add the virtual keyword in Person class (for lazy loading) : 首先,我认为最好在Person类中添加virtual关键字(对于延迟加载):

public virtual List<Car> Cars { get; set; }

You can add a reference to the Person on the car class with required annotation : 您可以使用所需的注释添加对car类的Person的引用:

[Required]
public virtual Person Person { get; set; }

since a person is required, deleting a person will cascade delete all his cars. 由于需要一个人,删除一个人将级联删除所有他的车。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM