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

时间:2013-02-06 10:01:06

标签: .net entity-framework code-first .net-4.5

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

这是我的问题:

代码:

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'。因为依赖角色   属性不是关键属性,是上限   依赖角色的多样性必须为'*'。 ”。请参阅服务器日志   更多细节。异常堆栈跟踪是:

     

at System.Data.Entity.DbModelBuilder.Build(DbProviderManifest)   providerManifest,DbProviderInfo providerInfo)at   System.Data.Entity.DbModelBuilder.Build(的DbConnection   providerConnection)   System.Data.Entity.Internal.LazyInternalContext.CreateModel(LazyInternalContext   internalContext)at   System.Data.Entity.Internal.RetryLazy 2.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.InternalSet 1.Initialize()at   System.Data.Entity.Internal.Linq.InternalSet 1.get_InternalContext() at System.Data.Entity.Infrastructure.DbQuery 1.System.Linq.IQueryable.get_Provider()   在System.Linq.Queryable.Where [TSource](IQueryable 1 source, Expression 1谓词)at at   ProjectTracker.Database.DataAccess.DLAccess.DoesUserExist(字符串   e:\ My中的userName)   自己的\项目\ ProjectTracker \数据库\ ProjectTracker.Database.DataAccess \ DLAccess.cs:行   31在ProjectTracker.Business.BLAccess.BLAccess.DoesUserExists(String   e:\ My中的userName)   自己的\项目\ ProjectTracker \企业\ ProjectTracker.Business.BLAccess \ BLAccess.cs:行   37在ProjectTracker.UI.Web.WS.WebAccess.DoesUserExist(String   e:\ My中的userName)   自己的\项目\ ProjectTracker \ UI \ ProjectTracker.UI.Web \ WS \ WebAccess.svc.cs:行   12在SyncInvokeDoesUserExist(Object,Object [],Object [])处   System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(对象   实例,对象[]输入,对象[]&输出)   System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc&安培;   rpc)at   System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc&安培;   rpc)at   System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(MessageRpc&安培;   rpc)at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean   isOperationContextSet)

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

1 个答案:

答案 0 :(得分:11)

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值,然后将此用户指定为他自己的创建者,然后更改数据库模式以便{ {1}}被禁止。

修改

有关“ EF映射惯例的详细信息尝试推断NULLUser.CreatedBy之间的一对一关系。”:

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

  1. 使用Fluent API配置
  2. 您的应用数据注释,
  3. 一组用于推断导航属性和属性名称之间关系的约定。
  4. 除非使用Fluent API或数据注释明确指定,否则将应用这些中心集。您可以在此处找到完整的约定:http://msdn.microsoft.com/en-us/library/system.data.entity.modelconfiguration.conventions(v=vs.103).aspx

    如果您的原始User.LastUpdatedBy课程是适用于您的模型的AssociationInverseDiscoveryConvention并且检测到一对一的关系。文档说:

      

    检测导航属性的惯例是每个的反转   其他当时只存在一对导航属性   相关类型。

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

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