实体框架代码优先自加入,'多重性在角色中无效'

Posted

技术标签:

【中文标题】实体框架代码优先自加入,\'多重性在角色中无效\'【英文标题】:Entity Framework Code First Self Join, 'Multiplicity is not valid in Role'实体框架代码优先自加入,'多重性在角色中无效' 【发布时间】:2013-02-06 10:01:06 【问题描述】:

我被这个错误困住了,找不到解决办法。我尝试了几件事,但无法提出解决方案。

这是我的问题:

代码:

namespace ProjectTracker.Database.Entities

    [DataContract]
    public class User
    
        [DataMember]
        public int Id  get; set; 

        [Required]
        [MaxLength(50)]
        [DataMember]
        public string UserName  get; set; 

        [Required]
        [MaxLength(100)]
        [DataType(DataType.Password)]
        [DataMember]
        public string Password  get; set; 

        [DataMember]
        public bool IsPasswordExpired  get; set; 

        [Required]
        [DataMember]
        public DateTime CreatedDate  get; set; 

        [Required]
        [ForeignKey("CreatedBy")]
        [DataMember]
        public int CreatedByUserId  get; set; 

        [DataMember]
        public virtual User CreatedBy  get; set; 

        [Required]
        [DataMember]
        public DateTime LastUpdatedDate  get; set; 

        [ForeignKey("LastUpdatedBy")]
        [DataMember]
        public int? LastUpdatedByUserId  get; set; 

        [DataMember]
        public virtual User LastUpdatedBy  get; set; 
    

以下是我从 Web 服务调用它时得到的异常详细信息:

请求错误 服务器在处理请求时遇到错误。 异常消息是“检测到一个或多个验证错误 在模型生成期间:\tSystem.Data.Entity.Edm.EdmAssociationEnd:: 多重性在角色“User_LastUpdatedBy_Source”中无效 关系'User_LastUpdatedBy'。因为依赖角色 属性不是关键属性,的上限 从属角色的多重性必须是“*”。 '。查看服务器日志 更多细节。异常堆栈跟踪是:

在 System.Data.Entity.DbModelBuilder.Build(DbProviderManifest providerManifest, DbProviderInfo providerInfo) 在 System.Data.Entity.DbModelBuilder.Build(DbConnection 提供者连接)在 System.Data.Entity.Internal.LazyInternalContext.CreateModel(LazyInternalContext 内部上下文)在 System.Data.Entity.Internal.RetryLazy2.GetValue(TInput input) at System.Data.Entity.Internal.LazyInternalContext.InitializeContext() at System.Data.Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType(Type entityType) at System.Data.Entity.Internal.Linq.InternalSet1.Initialize() 在 System.Data.Entity.Internal.Linq.InternalSet1.get_InternalContext() at System.Data.Entity.Infrastructure.DbQuery1.System.Linq.IQueryable.get_Provider() 在 System.Linq.Queryable.Where[TSource](IQueryable1 source, Expression1 谓词) 在 ProjectTracker.Database.DataAccess.DLAccess.DoesUserExist(字符串 用户名)在 e:\My Own\Projects\ProjectTracker\Database\ProjectTracker.Database.DataAccess\DLAccess.cs:line 31 在 ProjectTracker.Business.BLAccess.BLAccess.DoesUserExists(字符串 用户名)在 e:\My Own\Projects\ProjectTracker\Business\ProjectTracker.Business.BLAccess\BLAccess.cs:line 37 在 ProjectTracker.UI.Web.WS.WebAccess.DoesUserExist(字符串 用户名)在 e:\My Own\Projects\ProjectTracker\UI\ProjectTracker.UI.Web\WS\WebAccess.svc.cs:line 12 在 SyncInvokeDoesUserExist(Object , Object[] , Object[] ) 在 System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(对象 实例,Object[] 输入,Object[]& 输出)在 System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& RPC)在 System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& RPC)在 System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(MessageRpc& rpc) 在 System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)

请告诉我,我在这里做错了什么......

【问题讨论】:

我已经尝试了几件事 很好!但是为了防止我们重做所有这些,你能告诉你你尝试了什么吗? @GertArnold 我查看了这个链接,但这让我更加困惑......bit.ly/VTP0Su 和这个bit.ly/11Ucqri 但这与“代码优先”无关......跨度> 【参考方案1】:

EF 映射约定尝试推断User.CreatedByUser.LastUpdatedBy 之间的一对一关系。这会失败,因为这两个导航属性同时用不是主键的外键表示,而且 EF 仅支持共享主键一对一关系。

无论如何,这并不重要,因为您不想要一对一的关系,而是实际上需要两个一对多的关系:一个用户可以创建许多其他用户,而一个用户可以修改许多其他用户。

要实现这一点,您必须通过使用 Fluent API 显式定义两个关系来覆盖约定:

modelBuilder.Entity<User>()
    .HasRequired(u => u.CreatedBy)  // this could be a problem, see below
    .WithMany()
    .HasForeignKey(u => u.CreatedByUserId);

modelBuilder.Entity<User>()
    .HasOptional(u => u.LastUpdatedBy)
    .WithMany()
    .HasForeignKey(u => u.LastUpdatedByUserId);

可能需要将CreatedBy 设为可选,即CreatedByUserId 必须是int? 类型,并且在上面的映射中,您必须将HasRequired 替换为HasOptional,否则您无法创建第一个用户不违反 FK 约束。

您可能可以应用一个技巧,例如使用CreatedByUserId 在数据库中直接创建第一个用户,允许NULL 值,然后将此用户分配为他自己的创建者,然后更改数据库架构,从而禁止NULL .

编辑

有关“EF 映射约定尝试推断User.CreatedByUser.LastUpdatedBy 之间的一对一关系”的更多详细信息。

当 EF 在启动期间分析您的模型类时,它使用

    您使用 Fluent API 的配置, 您应用的数据注释, 一组用于从导航属性和属性名称推断关系的特定约定。

除非使用 Fluent API 或数据注释明确指定,否则将应用这些规则集。你可以在这里找到完整的约定:http://msdn.microsoft.com/en-us/library/system.data.entity.modelconfiguration.conventions(v=vs.103).aspx

在您原来的User 类的情况下,它是应用于您的模型并检测一对一关系的AssociationInverseDiscoveryConvention。文档说:

将导航属性检测为每个属性的倒数的约定 其他当只有一对导航属性之间存在 相关类型。

“唯一”一对导航属性是 CreatedByUser 中引用 User - 在 User 中是第二个导航属性 LastUpdatedBy 引用回 User。 (这有点令人困惑,因为“相关类型”是相同的 - UserUser,但这里的约定适用于不同类型之间的相同方式。)因为两者都是引用(而不是集合),所以 EF 假设关系必须是一对一的(而不是一对多或多对多)。

有时约定不会按照您的意愿推断关系。然后,您必须使用 Fluent API 或数据注释覆盖映射约定。 (在您的示例中注释不够,您必须在此处使用 Fluent API。)

【讨论】:

感谢您的提示。我对您的声明EF mapping conventions try to infer a one-to-one relationship between User.CreatedBy and User.LastUpdatedBy 有点困惑。我是否以错误的方式制作外键?我在这里没有得到什么? 我已经这样做了,它解决了这个问题,但你能解释一下吗?两个外键是如何建立一对一关系的??? @NaveedButt:见我上面的编辑。我希望这能澄清一点。

以上是关于实体框架代码优先自加入,'多重性在角色中无效'的主要内容,如果未能解决你的问题,请参考以下文章

如何在使用实体框架时获取所有用户角色

如何在 EF 代码优先中禁用链接表的级联删除?

多对多和自我参考表!获取用户拥有的所有角色。实体框架

带有实体框架的 ASP.NET MVC Core 项目中的种子角色

实体框架用户和角色管理

在快速自定义的NopCommerce中使用实体框架(EF)代码优先迁移