简体   繁体   中英

seeding code first fluent api database EF code-first issues

im having trouble seeding and creating a database matching my needs. I have 2 entities; User and Department. A User requires a department, and a department can have many users, but at the same time, a Department requires a single user to be its DepartmentChief. I can figure out how i can set this up, every attempt has resulted in exceptions of various kind.


Here is my context :

public class FravaerContext : DbContext
{
    public FravaerContext() : base("name=DefaultConnection")
    {
        Database.SetInitializer(new DBInitializer());
    }


    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<User>().HasRequired<Department>(u => u.Department).WithMany(d => d.Users).WillCascadeOnDelete(false);
        modelBuilder.Entity<Department>().HasRequired(d => d.DepartmentChief);

        //modelBuilder.Entity<Absence>().HasRequired<User>(a => a.User).WithMany(u => u.Absences).WillCascadeOnDelete(false);
        base.OnModelCreating(modelBuilder);
    }

    public DbSet<Absence> Absences { get; set; }
    public DbSet<Department> Departments { get; set; }
    public DbSet<User> Users { get; set; }

}

public class DBInitializer : DropCreateDatabaseAlways<FravaerContext>
{
    protected override void Seed(FravaerContext context)
    {
        for (int i = 1; i <= 4; i++)
        {
            User chief = new User() {Id = i, Absences = new List<Absence>(), Email = $"chief{i}@chief.dk", FirstName = $"Chief{i}",LastName = $"Chiefsen{i}",Password = "admin",UserName = ""+i,Role = Role.DepartmentChief};

            Department d = new Department() {Id = i, Users = new List<User>() { chief }, DepartmentChief = chief };


            chief.Department = d;

            context.Departments.Add(d);
        }
        base.Seed(context);
    }
}

And my entities (AbstractEntity only provides Id to entity-classes):

  public class Department : AbstractEntity
{
    public List<User> Users { get; set; }
    public User DepartmentChief { get; set; }

}

public class User : AbstractEntity
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string UserName { get; set; }
    public string Password { get; set; }
    public string Email { get; set; }
    public List<Absence> Absences { get; set; }
    public Department Department { get; set; }
    public Role Role { get; set; }

}

Well, you're having all this errors because you've created circular required dependencies between your entities and now the database doesn't know how to insert them properly.

You'll need then to loose your model a bit so one of the required properties becomes optional and then you'll be able to seed your database correctly.

Let's say you set the Department as optional in your User entity.

At the moment you are letting EF to generate your foreign key as you're not defining it on your OnModelCreating method.

So start by exposing your foreign key on your user:

public class User : AbstractEntity
{
    public int? DepartmentId { get; set; }
}

Note that I've set it as nullable so we can now define this relationship as optional:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<User>()
        .HasOptional(u => u.Department)
        .WithMany(d => d.Users)
        .HasForeignKey(x => x.DepartmentId)
        .WillCascadeOnDelete(false);
}

Ensure you initialize your Users property on Department's constructor so you don't get a NullReferenceException trying to add users to the departments:

public Department()
{
    Users = new List<User>();    
}

Create a new migration and update your database. Then you can use this initializer:

public class DBInitializer : DropCreateDatabaseAlways<FravaerContext>
{
    protected override void Seed(FravaerContext context)
    {
        //create the users first
        //save the users in a list so later we can create the departments
        var users = new List<User>();

        for (var i = 1; i <= 4; i++)
        {
            var user = new User
            {
                Id = i,
                Absences = new List<Absence>(),
                Email = $"chief{i}@chief.dk",
                FirstName = $"Chief{i}",
                LastName = $"Chiefsen{i}",
                Password = "admin",
                UserName = "user" + i,
                Role = Role.DepartmentChief
            };
            users.Add(user);
            context.Users.Add(user);
        }

        //create one department for each user (with that user as the chief)
        var departmentId = 1;

        //save the departments so later we can seed them with users
        var departments = new List<Department>();
        foreach (var user in users)
        {
            var d = new Department
            {
                Id = departmentId++,
                DepartmentChief = user
            };

            departments.Add(d);
            context.Departments.Add(d);
        }

        //seeed the departments with users
        foreach (var department in departments)
        {
            //add 5 users to each department
            for (var i = 1; i < 5; i++)
            {
                var user = new User
                {
                    Absences = new List<Absence>(),
                    Email = $"user{department.Id}{i}@user.dk",
                    FirstName = $"User{department.Id}{i}",
                    LastName = $"Username{department.Id}{i}",
                    Password = "user",
                    UserName = "" + department.Id + i,
                    Role = Role.DepartmentChief
                };
                department.Users.Add(user);
            }
        }
    }
}

What I've done here is decompose the seeding task into smaller pieces, so I first create the chief users (with no department), later I create the departments with the previously created users and lastly the users for each departments.

The advantage of this method is that you have full control over how many users, departments and chiefs you create.

Hope this helps!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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