实体框架代码第一个错误与自引用对象

时间:2017-02-01 17:24:43

标签: c# entity-framework ef-code-first

我正在尝试将我的第一个EF代码首先创建到数据库中。不幸的是,当我试图将我的对象保存到DB时,我得到了这个例外:

  

无法确定相关操作的有效排序。   由于外键约束,模型可能存在依赖关系   要求或商店生成的值。

以下是我用来填充数据库的代码:

using (var context = new MySample.Context())
{
    context.Database.CreateIfNotExists();

    CloudFile f = new CloudFile(, "file1");

    CloudDirectory d = new CloudDirectory(CloudObject.CloudContentType.Customer, "directory1");
    d.Nodes.Add(d);

    FileGroup g = new FileGroup();
    g.CloudObjects.Add(d);

    context.FileGroups.Add(g);
    context.SaveChanges();
}

实际上我想创建一个FileGroup类型的对象。该对象包含文件(CloudFile)或目录(CloudDirectory)。目录可以包含其他目录或文件。

以下是上述对象的类:

public class FileGroup {

    public FileGroup()
    {
        CloudObjects = new HashSet<CloudObject>();
    }

    public int FileGroupId { get; set; }

    public string Name { get; set; }

    public virtual ICollection<CloudObject> CloudObjects { get; set; }
}


public abstract class CloudObject {

    public CloudObject(string name)
    {
        Name = name;
    }

    public int CloudObjectId { get; set; }

    public string Name { get; set; }

    public abstract bool hasChildren { get; }

    public int? ParentId { get; set; }

    public virtual Node Parent { get; set; }

    public int? FileGroupId { get; set; }

    public virtual FileGroup FileGroup { get; set; }
}

public class CloudDirectory : CloudObject {

    public CloudDirectory(string name) :base(name)
    {
        Nodes = new HashSet<CloudObject>();
    }

    public override bool hasChildren
    {
        get
        {
            return Nodes.Any();
        }
    }

    public virtual ICollection<CloudObject> Nodes { get; set; }
}

public class CloudFile : CloudObject {

    public CloudFile(string name) : base(name)
    {
    }

    public string ETag { get; set; }

    public override bool hasChildren
    {
        get
        {
            return false;
        }
    }
}

知道我需要在我的对象中进行哪些更改才能成功存储它们吗?

1 个答案:

答案 0 :(得分:1)

测试代码中的一些更改(代码注释解释了它们):

using (var context = new MySample.Context())
{
    context.Database.CreateIfNotExists();

    CloudFile f = new CloudFile("file1");

    CloudDirectory d = new CloudDirectory("directory1");

    //This seems to be wrong:
    //d.Nodes.Add(d);
    d.Nodes.Add(f); //I guess you don't want d to be its own parent, but want it to be f's parent

    FileGroup g = new FileGroup();
    g.CloudObjects.Add(d);
    //Another way to do it:
    //d.FileGroup = g;

    //You also want f to be in the FileGroup, you have to add it explicitly
    g.CloudObjects.Add(f);
    //Another way to do it: 
    //f.FileGroup = g;

    context.FileGroups.Add(g);
    context.SaveChanges();
}

由于实体之间的关系对于处理它们的EF自动约定而言有点过于复杂,您应该在DbContext中为它们添加显式映射:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    ...
    modelBuilder.Entity<CloudObject>().HasOptional(x => x.Parent).WithMany(x => x.Nodes).HasForeignKey(x => x.ParentId);
    modelBuilder.Entity<CloudObject>().HasOptional(x => x.FileGroup).WithMany(x => x.CloudObjects).HasForeignKey(x => x.FileGroupId);
}

注意:我在此假设FileGroup对于给定的CloudObject不是必需的。如果是强制性的,您应该将方法HasOptional更改为HasRequired

您还应该更改此属性的类型:

public abstract class CloudObject
{
    ...
    //public virtual Node Parent { get; set; }
    public virtual CloudDirectory Parent { get; set; }
    ...
}

您的迁移必须看起来像这样:

CreateTable(
    "dbo.CloudObjects",
        c => new
            {
                CloudObjectId = c.Int(nullable: false, identity: true),
                Name = c.String(),
                ParentId = c.Int(),
                FileGroupId = c.Int(),
                ETag = c.String(),
                Discriminator = c.String(nullable: false, maxLength: 128),
            })
        .PrimaryKey(t => t.CloudObjectId)
        .ForeignKey("dbo.FileGroups", t => t.FileGroupId)
        .ForeignKey("dbo.CloudObjects", t => t.ParentId)
        .Index(t => t.ParentId)
        .Index(t => t.FileGroupId);

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