EF 4 Code First и M2M2M

Используя Entity Framework 4 и сначала код, как создать модель, поддерживающую этот сценарий:

В приложении есть пользователи, каждый пользователь принадлежит к одной или нескольким группам, и для каждой группы пользователь может иметь одну или другие роли.

Пример:

Я хотел бы иметь возможность сказать "дайте мне Лизу", и ответ вернет пользовательский объект для Лизы с группами, к которым она принадлежит. Для каждой группы есть свойство списка со всеми ролями, которые она имеет для этой конкретной группы

Может ли кто-нибудь помочь мне смоделировать это, сначала используя код, любые примеры справки/кода, было бы здорово!

/С уважением Винблад


person Vinblad    schedule 21.03.2011    source источник


Ответы (1)


Изменить: вот новая модель для вашего требования.

public class User
{
    public virtual int Id { get; set; }
    public virtual ICollection<UserPermission> Permissions { get; set; }
}

// Permission is extended junction table to model M:N between 
// User and Group but in addition it contains relation to Roles.
// The ony disadvantage is that this model doesn't control that
// role in the collection is also the role related to group. You
// must either enforce it in application logic or create some additional
// database construct to check it.
public class UserPermission
{
    public virtual int UserId {  get; set; }
    public virtual int GroupId { get; set; }

    public virtual Group Group { get; set; }
    public virtual ICollection<Role> Roles { get; set; }
}

public class Group
{
    public virtual int Id { get; set; }
    public virtual ICollection<UserPermission> UserPermissions { get; set; }
    public virtual ICollection<Role> Roles { get; set; }
}


public class Role
{
    public virtual int Id { get; set; }
    public virtual ICollection<Group> Groups { get; set; }
    public virtual ICollection<UserPermission> UserPermissions { get; set; }
}

public class Context : DbContext
{
    public DbSet<User> Users { get; set; }
    public DbSet<Group> Groups { get; set; }
    public DbSet<Role> Roles { get; set; }
    public DbSet<UserPermission> UserPermissions { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        // Permission has composite key
        modelBuilder.Entity<UserPermission>()
            .HasKey(p => new {p.UserId, p.GroupId});

        // Permission doesn't have navigation property to user
        modelBuilder.Entity<User>()
            .HasMany(u => u.Permissions)
            .WithRequired()
            .HasForeignKey(p => p.UserId);

        modelBuilder.Entity<Group>()
            .HasMany(g => g.UserPermissions)
            .WithRequired(p => p.Group)
            .HasForeignKey(p => p.GroupId);
    }
}

Как описано в коде, есть небольшой недостаток. Вы можете избежать этого недостатка, обеспечив целостность данных в БД с помощью дополнительных FK, которые нельзя сначала смоделировать с помощью кода. Вы можете использовать собственный инициализатор, чтобы добавить этот FK:

public class CustomInitializer : DropCreateDatabaseIfModelChanges<Context>
{
    protected override void Seed(Context context)
    {
        context.Database.ExecuteSqlCommand(
            @"ALTER TABLE [dbo].[RoleUserPermissions]  
             WITH CHECK ADD CONSTRAINT [FK_RoleUserPermissions_RoleGroups] 
             FOREIGN KEY([Role_Id], [UserPermission_GroupId])
             REFERENCES [dbo].[RoleGroups] ([Role_Id], [Group_Id])");
    }
}

Просто добавьте это в инициализацию вашего приложения (только для отладки — приложение не должно иметь возможность удалять свою базу данных в выпуске):

Database.SetInitializer(new CustomInitializer());
person Ladislav Mrnka    schedule 21.03.2011
comment
Спасибо, Ладислав, но я не уверен, что это правильно. Если я выберу пользователей, я смогу увидеть группы, к которым он принадлежит, но если я прочитаю это правильно, я не смогу увидеть, какие роли у пользователя для определенной группы. Это вернет все роли, добавленные для группы, а не для пользователя, принадлежащего к этой группе. Я хотел бы иметь возможность сказать, дайте мне Лизу, и ответ вернет пользовательский объект для Лизы с группами, к которым она принадлежит. Для каждой группы есть свойство списка со всеми ролями, которые она имеет для этой конкретной группы. - person Vinblad; 21.03.2011
comment
Так что это совсем другое требование. Я подумаю об этом. - person Ladislav Mrnka; 21.03.2011
comment
Извините, я думал, что я ясно выразился в своем первом описании, когда писал: каждый пользователь принадлежит к одной или нескольким группам, и для каждой группы пользователь может иметь одну или несколько ролей. Надеюсь, вы можете помочь мне теперь, когда вы понимаете мое требование. - person Vinblad; 21.03.2011
comment
Привет Ладислав! Спасибо за отличный ответ! Работает так, как я надеялся. У меня есть еще один вопрос, надеюсь, вы сможете ответить. Есть ли способ предотвратить объекты, имеющие свойства, которые ему не нужны для целей приложения. Например, объект роли, у которого есть группы и права пользователя. Это необязательно для приложения и существует только для того, чтобы база данных была создана с правильными таблицами. Можно ли вместо этого сделать это во время OnModelCreating с настройками и удалением свойств? - person Vinblad; 22.03.2011
comment
Это возможно, но вам все равно нужно свойство навигации хотя бы на одной стороне отношения. Вы удалите свойства из роли и сопоставите ее так же, как описано во втором фрагменте сопоставления. - person Ladislav Mrnka; 22.03.2011