我需要创建一个表,该表由引用同一模型/表的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.
InverseProperty
指的是导航属性,而不是外键属性,并缩短导航属性名称,例如Sender
而不是UserMessageSender
。所以类似的东西
[InverseProperty("Sender")]
public virtual ICollection<UserMessage> MessagesSent { get; set; }
[InverseProperty("Receiver")]
public virtual ICollection<UserMessage> MessagesReceived { get; set; }
在.NET中,在属性名中以类型名开头是不常见的,而在列名前加上其表的名称则更常见。