实体框架如何使两个外键引用同一个表

时间:2021-06-12 18:35:10

标签: entity-framework model entity-framework-core foreign-keys relationship

我需要创建一个表,该表由 2 个引用相同模型/表的外键组成。我尝试了很多方法,比如为同一个表的每个外键创建一个自己的 ICollection,或者使用 Inverseproperty 和 fluentapi 但它从来没有奏效(总是出错)。

我真正需要的(我也尝试过上述选项)是: 我的表 1:

using Messanger_Backend.SideClasses;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;

namespace Messanger_Backend.Models
{
    [Index(nameof(User.UserPhoneNumber), IsUnique = true)]
    public class User : BaseEntity
    {
        public User()
        {
            UserMessages = new HashSet<UserMessage>();
        }
        [Key]
        public int UserID { get; set; }

        [Column(TypeName="nvarchar(50)")]
        public string UserFirstname { get; set; }
        [Column(TypeName = "nvarchar(50)")]
        public string UserLastname { get; set; }
        [Column(TypeName = "nvarchar(20)")]
        [Required]
        public string UserPhoneNumber { get; set; }


        public ICollection<UserMessage> UserMessages { get; set; }

    }
}

我的桌子 2:

using Messanger_Backend.SideClasses;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;

namespace Messanger_Backend.Models
{
    public class UserMessage : BaseEntity
    {

        [Key]
        public int UserMessageID { get; set; }


        public int UserMessageSenderID { get; set; }
        public User UserMessageSender { get; set; }

        public int UserMessageReceiverID { get; set; }
        public User UserMessageReceiver { get; set; }

        [Column(TypeName="nvarchar(500)")]
        public string UserMessageText { get; set; }
        
    }
}

更新 我尝试了@David Browne - 微软的建议,但我也没有工作。这是我尝试过的方法:

用户:

using Messanger_Backend.SideClasses;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;

namespace Messanger_Backend.Models
{
    [Index(nameof(User.UserPhoneNumber), IsUnique = true)]
    public class User : BaseEntity
    {
        public User()
        {
            UserMessagesSender = new HashSet<UserMessage>();
            UserMessagesReceiver = new HashSet<UserMessage>();
        }
        [Key]
        public int UserID { get; set; }

        [Column(TypeName="nvarchar(50)")]
        public string UserFirstname { get; set; }
        [Column(TypeName = "nvarchar(50)")]
        public string UserLastname { get; set; }
        [Column(TypeName = "nvarchar(20)")]
        [Required]
        public string UserPhoneNumber { get; set; }
        [Column(TypeName = "nvarchar(250)")]
        public string UserStatus { get; set; }


        [InverseProperty("UserMessageSenderID")]
        public virtual ICollection<UserMessage> UserMessagesSender { get; set; }
        [InverseProperty("UserMessagesReceiverID")]
        public virtual ICollection<UserMessage> UserMessagesReceiver { get; set; }

    }
}

```



UserMessage:
```
using Messanger_Backend.SideClasses;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;

namespace Messanger_Backend.Models
{
    public class UserMessage : BaseEntity
    {


        [Key]
        public int UserMessageID { get; set; }

        public int UserMessageSenderID { get; set; }
        [ForeignKey("UserMessageSenderID")]
        public User UserMessageSender { get; set; }
        public int UserMessageReceiverID { get; set; }
        [ForeignKey("UserMessageReceiverID")]
        public User UserMessageReceiver { get; set; }

        [Column(TypeName="nvarchar(500)")]
        public string UserMessageText { get; set; }


        [Column(TypeName = "DateTime")]
        public DateTime UserMessageReceived { get; set; }
        [Column(TypeName = "DateTime")]
        public DateTime UserMessageRead { get; set; }
    }
}

```


Error I receive on second option:

PM> Add-Migration "Init Migration"
Build started...
Build succeeded.
System.InvalidOperationException: The [InverseProperty] attribute on property 'User.UserMessagesReceiver' is not valid. The property 'UserMessagesReceiverID' is not a valid navigation on the related type 'UserMessage'. Ensure that the property exists and is a valid reference or collection navigation.
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.InversePropertyAttributeConvention.ConfigureInverseNavigation(IConventionEntityTypeBuilder entityTypeBuilder, MemberInfo navigationMemberInfo, IConventionEntityTypeBuilder targetEntityTypeBuilder, InversePropertyAttribute attribute)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.InversePropertyAttributeConvention.Process(IConventionEntityTypeBuilder entityTypeBuilder, MemberInfo navigationMemberInfo, Type targetClrType, InversePropertyAttribute attribute)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.InversePropertyAttributeConvention.ProcessEntityTypeAdded(IConventionEntityTypeBuilder entityTypeBuilder, MemberInfo navigationMemberInfo, Type targetClrType, InversePropertyAttribute attribute, IConventionContext`1 context)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.NavigationAttributeConventionBase`1.ProcessEntityTypeAdded(IConventionEntityTypeBuilder entityTypeBuilder, IConventionContext`1 context)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.ImmediateConventionScope.OnEntityTypeAdded(IConventionEntityTypeBuilder entityTypeBuilder)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.OnEntityTypeAddedNode.Run(ConventionDispatcher dispatcher)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.DelayedConventionScope.Run(ConventionDispatcher dispatcher)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.ConventionBatch.Run()
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.ConventionBatch.Dispose()
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.ImmediateConventionScope.OnModelInitialized(IConventionModelBuilder modelBuilder)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.OnModelInitialized(IConventionModelBuilder modelBuilder)
   at Microsoft.EntityFrameworkCore.Metadata.Internal.Model..ctor(ConventionSet conventions, ModelDependencies modelDependencies)
   at Microsoft.EntityFrameworkCore.ModelBuilder..ctor(ConventionSet conventions, ModelDependencies modelDependencies, Boolean _)
   at Microsoft.EntityFrameworkCore.ModelBuilder..ctor(ConventionSet conventions, ModelDependencies modelDependencies)
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.CreateModel(DbContext context, IConventionSetBuilder conventionSetBuilder, ModelDependencies modelDependencies)
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.GetModel(DbContext context, IConventionSetBuilder conventionSetBuilder, ModelDependencies modelDependencies)
   at Microsoft.EntityFrameworkCore.Internal.DbContextServices.CreateModel()
   at Microsoft.EntityFrameworkCore.Internal.DbContextServices.get_Model()
   at Microsoft.EntityFrameworkCore.Infrastructure.EntityFrameworkServicesBuilder.<>c.<TryAddCoreServices>b__7_3(IServiceProvider p)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitFactory(FactoryCallSite factoryCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass1_0.<RealizeService>b__0(ServiceProviderEngineScope scope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
   at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
   at Microsoft.EntityFrameworkCore.DbContext.get_InternalServiceProvider()
   at Microsoft.EntityFrameworkCore.DbContext.Microsoft.EntityFrameworkCore.Infrastructure.IInfrastructure<System.IServiceProvider>.get_Instance()
   at Microsoft.EntityFrameworkCore.Infrastructure.Internal.InfrastructureExtensions.GetService[TService](IInfrastructure`1 accessor)
   at Microsoft.EntityFrameworkCore.Infrastructure.AccessorExtensions.GetService[TService](IInfrastructure`1 accessor)
   at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(Func`1 factory)
   at Microsoft.EntityFrameworkCore.Design.Internal.DbContextOperations.CreateContext(String contextType)
   at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.AddMigration(String name, String outputDir, String contextType, String namespace)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigrationImpl(String name, String outputDir, String contextType, String namespace)
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.AddMigration.<>c__DisplayClass0_0.<.ctor>b__0()
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.<>c__DisplayClass3_0`1.<Execute>b__0()
   at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)
The [InverseProperty] attribute on property 'User.UserMessagesReceiver' is not valid. The property 'UserMessagesReceiverID' is not a valid navigation on the related type 'UserMessage'. Ensure that the property exists and is a valid reference or collection navigation.

1 个答案:

答案 0 :(得分:0)

InverseProperty 指的是导航属性,而不是外键属性,并缩短导航属性名称,例如 Sender 而不是 UserMessageSender。所以像

[InverseProperty("Sender")]
public virtual ICollection<UserMessage> MessagesSent { get; set; }

[InverseProperty("Receiver")]
public virtual ICollection<UserMessage> MessagesReceived { get; set; }

在 .NET 中,在属性名称中预置类型名称是不常见的,而在列名中预置其表名则更为常见。

相关问题