【问题标题】:Entity Framework table-per-type inheritance - adding inherited type to database giving error "Invalid column name"Entity Framework table-per-type继承-将继承的类型添加到数据库中,给出错误“无效的列名”
【发布时间】:2013-01-30 16:45:27
【问题描述】:

我已经有一段时间了,我真的无法弄清楚。我正在使用 EF5、Mvc 4、table-per-type 继承。

我有继承自 UserProfile 的租户和房东类。注册用户时选择他们想要的帐户类型,并根据此选择将他们在表单上输入的详细信息(用户名、名字、姓氏、电子邮件、帐户类型、密码)添加到 UserProfile 表中。然后,我希望将该表的 PK 作为外键添加到与他们选择的帐户类型(租户或房东)匹配的表中。

我试图实现上述using this method,但这导致了该问题中描述的问题。

我现在创建了一个 RegisterTenantModel 类,其属性映射到租户,并且我正在将 RegisterTenantModel 类型的对象传递给 AccountController/Register,如下所示...

// ...
if (tenantModel.AccountType == AccountType.Tenant)
                {
                    using (var db = new LetLordContext())
                    {
                        var t = db.UserProfile.Create<Tenant>();

                        WebSecurity.CreateUserAndAccount(tenantModel.UserName, tenantModel.Password,
                             t = new Tenant
                            {
                               AccountType = tenantModel.AccountType,
                               DateWantToRentFrom = DateTime.Now, 
                               DateWantToRentTo = DateTime.Now, 
                               DesiredPropertyLocation = "",
                               Email = tenantModel.Email, 
                               FirstName = tenantModel.FirstName,
                               UserId = WebSecurity.CurrentUserId, 
                               UserName = WebSecurity.CurrentUserName

                               // More properties that map to Tenant entity
                               // ...
                            },
                            false);

                        db.SaveChanges();

...但是现在t= new Tenant 中的每个列名都出现错误Invalid column name

有没有人做过类似的事情?我以正确的方式接近这个吗?非常感谢您的帮助。

编辑

根据 Antonio Simunovic 在下面发布的内容,我想我已经意识到问题所在了。我的WebSecurity.InitializeDatabaseConnection() 是这样的……

WebSecurity.InitializeDatabaseConnection("LetLordContext", "UserProfile", 
"UserId", "UserName", autoCreateTables: true);

...所以当我在AccountController/Register 中调用Websecurity.CreatUserAndAccount() 时,它会根据WebSecurity.InitializeDatabaseConnection() 中的参数写入UserProfile 表。查看上面链接的问题,您会看到,根据表单上选择的帐户类型,将通过调用将租户或房东添加到 UserProfile 表中......

var tenant = db.UserProfile.Create<Tenant>();
//...
db.UserProfile.Add(tenant);
db.SaveChanges();

...导致重复的条目被添加到 UserProfile 表中。

我认为解决方案是创建一个新表,并将WebSecurity.InitializeDatabaseConnection() 指向它。

【问题讨论】:

  • 我认为你的编辑是正确的。在我所有成员资格的实现中,我已将其配置为使用我的用户表,而不是默认的成员资格/其他特定于成员资格框架的表。我不知道这是最佳实践,但是当不直接使用它时,这是我一直看到的方式。 (也不是 100% 确定这条评论是相关的)。
  • 刚刚尝试过,它可以工作,可能不是很漂亮,但每个类型的表继承与成员资格一起工作。感谢您的帮助。

标签: c# asp.net-mvc database entity-framework


【解决方案1】:

您的 WebSecurity.InitializeDatabaseConnection() 方法调用是什么样的?该调用标识数据库表来存储用户数据。

WebSecurity.CreateUserAndAccount() 方法不使用上下文来存储提供的数据,它只是将第三个调用参数的对象属性名称映射到 InitializeDatabaseFile() 方法中定义的表中的列。

如果您不熟悉 SimpleMembership 机制,请查看这篇文章: http://weblogs.asp.net/jgalloway/archive/2012/08/29/simplemembership-membership-providers-universal-providers-and-the-new-asp-net-4-5-web-forms-and-asp-net-mvc-4-templates.aspx

【讨论】:

  • 方法是这样的 WebSecurity.InitializeDatabaseConnection("LetLordContext", "UserProfile", "UserId", "UserName", autoCreateTables: true);如果它不使用上下文来存储数据,有什么可以实现我想要的吗?
【解决方案2】:

我看到了完全相同的症状,这是因为我没有在 DbContext 上将基类声明为 DbSet&lt;BaseClassName&gt;

(违反直觉,我从不需要在我的应用程序中引用基类实体的集合,但你去吧。)

【讨论】:

    【解决方案3】:

    当我忘记用 TPT 中的表名注释子类时,我看到了这个错误。

    [Table("Tenant")]
    public class Tenant : UserProfile { ...
    

    就这么简单吗?

    编辑

    快速搜索还建议将实体中的字段配对,然后一次将它们添加回一个字段,以查看是否应归咎于单个字段(它会提供指示许多失败列的错误消息)。这听起来有点可疑,但可能值得一试:

    Entity Framework 5 Invalid Column Name error

    【讨论】:

    • 很遗憾,Tenant 类同上。一定有办法吗?他们肯定不会发布带有 UserProfile 模型的 Mvc 4 - 依赖于网络安全 - 无法扩展?
    • 添加了我发现的其他内容的更新,尽管我自己还没有看到。
    • 我遇到了这个问题,但是当我在浏览器中看到错误页面时,它会将所有字段列为无效。我也调试过,正确的数据被传递给 CreateUserAndAccount 方法,它只是抛出异常。
    • 请注意,即使只有一个是问题所在,他也会使所有字段都无效。这听起来很可疑,但可能值得一试。我也会从删除 DateTime 的东西开始。我并不是在提倡这是一个好主意,只是一个主意:)
    • 您链接的问题似乎不相关。这个问题似乎是 EF 试图使用 TPC 继承而不是 TPT。
    【解决方案4】:

    解决方案

    1. 确保所有派生类显式映射到表。两种简单的方法是:
      1. TableAttribute类属性:[Table("Tenant")]
      2. ToTable Fluent API 方法:ToTable("Tenant")
    2. 确保基本类型为abstract
    3. 确保您任何表调用MapInheritedProperties配置方法。该方法意味着 TPC。
    4. 确保将基本类型注册为模型的一部分。两种简单的方法是:

      1. DbContext 的子类添加访问器属性:

        public DbSet<UserProfile> Users { get; set; }
        
      2. DbContext 的子类中覆盖OnModelCreating 并调用DbModelBuilder.Entity

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);
            modelBuilder.Entity<UserProfile>();
        }
        

    说明

    正如错误消息所示,Entity Framework 似乎正在“派生”表中查找“基”表的列。这表明它正在尝试使用“每个具体类型的表”(TPC)继承而不是“每个类型的表”(TPT)。

    让实体框架使用 TPT 是相当复杂的。如果您非常仔细地研究the example implementation at Inheritance with EF Code First: Part 2 – Table per Type (TPT),您可能会发现自己遗漏了什么。 The follow-up article, Inheritance with EF Code First: Part 3 – Table per Concrete Type (TPC),将所需配置总结如下:

    如您目前所见,我们使用其 Requires 方法来自定义 TPH。我们还使用了它的ToTable 方法来创建TPT,现在我们使用它的MapInheritedPropertiesToTable 方法来创建我们的TPC 映射。

    我的实验表明,上面的总结过于简单化了。看起来,一个看似无关的配置差异可能会导致使用意外的继承策略。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-12-28
      • 1970-01-01
      • 2011-01-02
      • 2013-06-13
      相关资源
      最近更新 更多