Reputation: 11
I'm trying to create my first EF code first objects into database. Unfortunately when trying to save my objects into DB I get this exception:
Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements, or store-generated values.
Here is the code I use to fill the 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();
}
Actually I would like to create an object of type FileGroup
. This object contains either files (CloudFile
) or directories (CloudDirectory
). The directories can contain either other directories or files.
Here are the classes for the mentioned objects:
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;
}
}
}
Any idea what I need to change in my objects so that they can be stored successfully do DB?
Upvotes: 1
Views: 203
Reputation: 2226
Several changes in your test code (the code comments explain them):
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();
}
As the relationships between the entities are a bit too complex for EF automatic conventions to deal with them, you should add explicit mappings for them in your 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);
}
Note: I assumed here that FileGroup
is not mandatory for a given CloudObject
. If it is mandatory you should change the method HasOptional
to HasRequired
.
You should also change the type of this property:
public abstract class CloudObject
{
...
//public virtual Node Parent { get; set; }
public virtual CloudDirectory Parent { get; set; }
...
}
Your migration has to look somewhat like this:
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);
Upvotes: 1