EF Code First:使用TFT继承在db中单独创建对象

时间:2012-02-14 15:36:54

标签: c# entity-framework inheritance ef-code-first table-per-type

用例示例

用户已在网站上注册(向“用户”表添加记录)。然后转到电子邮件并确认注册。确认链接重定向到具有人员编辑表单的页面,用户填写表单并提交表单(将记录添加到人员表)。

技术说明

我在EF模型中定义了一个简单的2对象Table-Per-Type继承。

实体

public class User
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public string Password { get; set; }
}

public class Person : User
{
    public string FirstName { get; set; }
    public string MiddleName { get; set; }
    public string LastName { get; set; }
}

数据库

public class MainDataContext : DbContext
{
    public DbSet<User> Users { get; set; }
    public DbSet<Person> Persons { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        var configs = modelBuilder.Configurations;
        configs.Add(new UserConfiguration());
        configs.Add(new PersonConfiguration());
    }
}

配置

internal class UserConfiguration : EntityTypeConfiguration<User>
{
    internal UserConfiguration()
    {
        ToTable("Users");

        HasKey(x => x.Id);

        Property(x => x.Id)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
        Property(x => x.Name)
            .IsRequired()
            .HasMaxLength(128);
        Property(x => x.Password)
            .IsRequired()
            .HasMaxLength(128);
        Property(x => x.Email)
            .IsRequired()
            .HasMaxLength(128);
    }
}
internal class PersonConfiguration : EntityTypeConfiguration<Person>
{
    internal PersonConfiguration()
    {
        ToTable("Persons");

        Property(x => x.FirstName)
            .IsRequired()
            .HasMaxLength(16);
        Property(x => x.MiddleName)
            .IsOptional()
            .HasMaxLength(16);
        Property(x => x.LastName)
            .IsRequired()
            .HasMaxLength(16);
    }
}

必须在DbSets中始终如一地创建链接(FK)对象,如下所示:

    var db = new MainDataContext();
    var user = new User
    {
        Name = "someUser",
        Password = "somePassword",
        Email = "someEmail"
    };
    db.Users.Add(user);
    db.SaveChanges();
    // After some time in other some place
    var person = new Person
    {
        Id = user.Id,
        FirstName = "someFirstName",
        LastName = "someLastName"
    };
    db.Persons.Add(person);
    db.SaveChanges();

在捕获到最后一次SaveChanges DbEntityValidationError之后:“一个或多个实体的验证失败。有关详细信息,请参阅'EntityValidationErrors'属性。”所需用户的字段有3个验证错误。

如何单独向FK(继承)关联的DbSets添加条目?

更新

我找到了一个解决方案。但为此你必须删除旧用户。我想这样做而不删除,因为如果Users表与其他表关联,那么它将无法删除。

    var db = new MainDataContext();
    var user = new User
    {
        Name = "someUser",
        Password = "somePassword",
        Email = "someEmail"
    };
    db.Users.Add(user);
    db.SaveChanges();
    // After some time in other some place
    var person = new Person
    {
        Id = user.Id,
        Name = user.Name,
        Password = user.Password,
        Email = user.Email,
        FirstName = "someFirstName",
        LastName = "someLastName"
    };
    db.Users.Remove(user);
    db.Persons.Add(person);
    db.SaveChanges();

但还有另一个问题 - 为人物对象生成一个新的Guid。

如何在不删除用户的旧记录的情况下解决问题?

2 个答案:

答案 0 :(得分:1)

包含用例更新:

你不应该在这里使用继承。您应该使用实体之间的组合和一对一关系:

public class User
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public string Password { get; set; }
    public virtual UserInfo Information{get;set;}
}

internal class UserConfiguration : EntityTypeConfiguration<User>
{
    internal UserConfiguration()
    {
        ToTable("Users");

        HasKey(x => x.Id);

        Property(x => x.Id)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
        Property(x => x.Name)
            .IsRequired()
            .HasMaxLength(128);
        Property(x => x.Password)
            .IsRequired()
            .HasMaxLength(128);
        Property(x => x.Email)
            .IsRequired()
            .HasMaxLength(128);        
    }
}

public class UserInfo
{
    [ForeignKey("InfosUser")]
    [Key]
    public UserId {get;set;}
    public string FirstName { get; set; }
    public string MiddleName { get; set; }
    public string LastName { get; set; }
    public virtual User InfosUser {get;set;}
}

internal class UserInfoConfiguration : EntityTypeConfiguration<UserInfo>
{
    internal UserInfoConfiguration()
    {
        ToTable("Persons");

        Property(x => x.FirstName)
            .IsRequired()
            .HasMaxLength(16);
        Property(x => x.MiddleName)
            .IsOptional()
            .HasMaxLength(16);
        Property(x => x.LastName)
            .IsRequired()
            .HasMaxLength(16);
        HasRequired(ui=>ui.InfosUser).WithOptional(u=>u.Information)
    }
}

并使用它:

    var db = new MainDataContext();
    var user = new User
    {
        Name = "someUser",
        Password = "somePassword",
        Email = "someEmail"
    };
    db.Users.Add(user);
    db.SaveChanges();
    // After some time in other some place
    var user = db.GetPersonFromDb(Id);// Get early saved person from db
    var information = new UserInfo
    {
         FirstName = "someFirstName",
         LastName = "someLastName",
    }
    user.Information = information;
    db.SaveChanges();

答案 1 :(得分:0)

问题似乎是创建的对象不是Person对象。如果将User.id值插入Person表,则可以将该用户作为Person查找并对其进行udpate。

        Guid id;
        using (var db = new MainDataContext())
        {
            var user = new User
            {
                Name = "someUser",
                Password = "somePassword",
                Email = "someEmail"
            };
            db.Users.Add(user);
            db.SaveChanges();
            id = user.Id;
        }

        // After some time in other some place
        //pause here and insert the id GUID into the Person table

        using (var db = new MainDataContext())
        {
            var person = db.Persons.Find(id);
            person.FirstName = "someFirstName";
            person.LastName = "someLastName";
            db.SaveChanges();
        }

将用户的ID添加到EF之外的Person表中可实现您想要的upcast。您可以通过直接数据库更新将对象从“用户”升级到“人”。