简体   繁体   English

如何在单元测试中验证 EF Core DBContext 配置

[英]How to validatie EF Core DBContext config in an unit test

Currently when there is a mistake in the config of the DBContext model, we get an error at run-time.当前,当 DBContext 模型的配置出现错误时,我们会在运行时收到错误消息。 For example:例如:

The entity type 'MyObject' requires a primary key to be defined.实体类型“MyObject”需要定义一个主键。 If you intended to use a keyless entity type call 'HasNoKey()'.如果您打算使用无键实体类型,请调用“HasNoKey()”。

This error is throw when EF Core is used for the first time in the application.首次在应用程序中使用 EF Core 时会抛出此错误。 I would like to validate the model in an unit test.我想在单元测试中验证模型。

What I have tried我试过的

I noticed the ModelValidator.Validate , in the stacktrace so I tried this:我注意到ModelValidator.Validate中的ModelValidator.Validate ,所以我尝试了这个:

[TestMethod]
public void MyDbContext_DoesNotThrowExceptions()
{
    // Arrange
    var myDbContext = CreateMyDbContext();
    IModel model = myDbContext.Model;

    var validator = new ModelValidator(new ModelValidatorDependencies(???,???)) // [EntityFrameworkInternal] public ModelValidatorDependencies([NotNull] ITypeMappingSource typeMappingSource,[NotNull] IMemberClassifier memberClassifier)

    // Act
    validator.Validate(model, ???); // public virtual void Validate(IModel model,IDiagnosticsLogger<DbLoggerCategory.Model.Validation> logger)
}

Unfortunately I don't now how to create the ModelValidator / ModelValidatorDependencies or what to pass as IDiagnosticsLogger - see ???不幸的是,我现在不知道如何创建ModelValidator / ModelValidatorDependencies或作为IDiagnosticsLogger传递的IDiagnosticsLogger - 见??? in code.在代码中。 Also the [EntityFrameworkInternal] gives me the idea that I have the wrong approach. [EntityFrameworkInternal]让我觉得我有错误的方法。

Any one an idea how to fix this unit test?任何人都知道如何修复此单元测试? (By creating the ModelValidator or by another approach) (通过创建ModelValidator或通过其他方法)

Details细节

The full stack trace of the error:错误的完整堆栈跟踪:

Test method Foo threw exception: 
System.InvalidOperationException: The entity type 'MyEntity' requires a primary key to be defined. If you intended to use a keyless entity type call 'HasNoKey()'.
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelValidator.ValidateNonNullPrimaryKeys(IModel model, IDiagnosticsLogger`1 logger)
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelValidator.Validate(IModel model, IDiagnosticsLogger`1 logger)
   at Microsoft.EntityFrameworkCore.Infrastructure.RelationalModelValidator.Validate(IModel model, IDiagnosticsLogger`1 logger)
   at Microsoft.EntityFrameworkCore.SqlServer.Internal.SqlServerModelValidator.Validate(IModel model, IDiagnosticsLogger`1 logger)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.ValidatingConvention.ProcessModelFinalized(IConventionModelBuilder modelBuilder, IConventionContext`1 context)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.ImmediateConventionScope.OnModelFinalized(IConventionModelBuilder modelBuilder)
   at Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.OnModelFinalized(IConventionModelBuilder modelBuilder)
   at Microsoft.EntityFrameworkCore.Metadata.Internal.Model.FinalizeModel()
   at Microsoft.EntityFrameworkCore.ModelBuilder.FinalizeModel()
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.CreateModel(DbContext context, IConventionSetBuilder conventionSetBuilder)
   at Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.GetModel(DbContext context, IConventionSetBuilder conventionSetBuilder)
   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.Infrastructure.DatabaseFacade.get_Dependencies()
   at Microsoft.EntityFrameworkCore.Infrastructure.DatabaseFacade.Microsoft.EntityFrameworkCore.Internal.IDatabaseFacadeDependenciesAccessor.get_Dependencies()
   at Microsoft.EntityFrameworkCore.RelationalDatabaseFacadeExtensions.GetFacadeDependencies(DatabaseFacade databaseFacade)
   at Microsoft.EntityFrameworkCore.RelationalDatabaseFacadeExtensions.ExecuteSqlRaw(DatabaseFacade databaseFacade, String sql, IEnumerable`1 parameters)
   at Microsoft.EntityFrameworkCore.RelationalDatabaseFacadeExtensions.ExecuteSqlRaw(DatabaseFacade databaseFacade, String sql, Object[] parameters)
   at myCode

Solution解决方案

With soms tweaks (EF Core 3 validate method has 2 parameters), Vasil's answer worked for me.通过 soms 调整(EF Core 3 验证方法有 2 个参数),Vasil 的回答对我有用。 I have now:我现在有了:

[TestMethod]
public void MyDbContext_DoesNotThrowExceptions()
{
    // Arrange
    var serviceProvider = CreateServiceProvider();

    var validator = serviceProvider.GetService<IModelValidator>();
    var context = serviceProvider.GetService<MyDbContext>();
    var logger = serviceProvider.GetService<IDiagnosticsLogger<DbLoggerCategory.Model.Validation>>();

    // Act
    validator.Validate(context.Model, logger);
}

private static ServiceProvider CreateServiceProvider()
{
    var serviceCollection = new ServiceCollection()
        .AddEntityFrameworkSqlServer()
        .AddDbContext<MyDbContext>((sp, options) => options
            .UseSqlServer(new SqlConnection()));
    return serviceCollection.BuildServiceProvider();
}

I've looked around in the EFCore github repository and this is the best i've managed to get working.我在 EFCore github 存储库中环顾四周,这是我设法开始工作的最好方法。

var serviceCollection = new Microsoft.Extensions.DependencyInjection.ServiceCollection();
serviceCollection.AddEntityFrameworkSqlServer().AddDbContext<Context>();
var serviceProvider = serviceCollection.BuildServiceProvider();
var validator = serviceProvider.GetService<IModelValidator>();
var context = serviceProvider.GetService<Context>();

validator.Validate(context.Model); 

All the type used are coming from the ef namespace or from packages which ef depends on, so you should be able to get it running without issues.使用的所有类型都来自 ef 命名空间或来自 ef 所依赖的包,因此您应该能够毫无问题地运行它。

I've tested it with single entity without primary key and it throw exception 'The entity type 'XXXXX' requires a primary key to be defined', so this should be pretty close to what ef is doing when executing the first query.我已经用没有主键的单个实体对其进行了测试,它抛出异常“实体类型‘XXXXX’需要定义一个主键”,因此这应该与 ef 在执行第一个查询时所做的非常接近。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM