No context type was found in the assembly EF6

2019-07-15 03:53发布

问题:

I'm working on a nopCommerce based project. In order to update custom entity I want to enable EF migrations. So I run following command:

Enable-Migrations -StartUpProjectName nop.web -ContextProjectName Nop.Plugin.Payments.Deposit -verbose

and get the error:

Using StartUp project 'Nop.Web'.
System.Data.Entity.Migrations.Infrastructure.MigrationsException: No context type was found in the assembly 'Nop.Plugin.Payments.Deposit'.
   at System.Data.Entity.Utilities.TypeFinder.FindType(Type baseType, String typeName, Func`2 filter, Func`2 noType, Func`3 multipleTypes, Func`3 noTypeWithName, Func`3 multipleTypesWithName)
   at System.Data.Entity.Migrations.Design.ToolingFacade.GetContextTypeRunner.Run()
   at System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate)
   at System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate)
   at System.Data.Entity.Migrations.Design.ToolingFacade.Run(BaseRunner runner)
   at System.Data.Entity.Migrations.Design.ToolingFacade.GetContextType(String contextTypeName)
   at System.Data.Entity.Migrations.EnableMigrationsCommand.FindContextToEnable(String contextTypeName)
   at System.Data.Entity.Migrations.EnableMigrationsCommand.<>c__DisplayClass2.<.ctor>b__0()
   at System.Data.Entity.Migrations.MigrationsDomainCommand.Execute(Action command)
No context type was found in the assembly 'Nop.Plugin.Payments.Deposit'.

The context class is defined as following:

public class DepositTransactionObjectContext : DbContext, IDbContext
{
    public DepositTransactionObjectContext(string nameOrConnectionString) : base(nameOrConnectionString) { }

    public DepositTransactionObjectContext() { }

    public IList<TEntity> ExecuteStoredProcedureList<TEntity>(string commandText, params object[] parameters) where TEntity : BaseEntity, new()
    {
        throw new System.NotImplementedException();
    }

    public IEnumerable<TElement> SqlQuery<TElement>(string sql, params object[] parameters)
    {
        throw new System.NotImplementedException();
    }

    public int ExecuteSqlCommand(string sql, bool doNotEnsureTransaction = false, int? timeout = null, params object[] parameters)
    {
        throw new System.NotImplementedException();
    }

    public void Detach(object entity)
    {
        throw new System.NotImplementedException();
    }

    public bool ProxyCreationEnabled { get; set; }
    public bool AutoDetectChangesEnabled { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new DepositTransactionMap());

        base.OnModelCreating(modelBuilder);
    }

    public string CreateDatabaseInstallationScript()
    {
        return ((IObjectContextAdapter)this).ObjectContext.CreateDatabaseScript();
    }

    public void Install()
    {
        //It's required to set initializer to null (for SQL Server Compact).
        //otherwise, you'll get something like "The model backing the 'your context name' context has changed since the database was created. Consider using Code First Migrations to update the database"
        Database.SetInitializer<DepositTransactionObjectContext>(null);

        Database.ExecuteSqlCommand(CreateDatabaseInstallationScript());
        SaveChanges();
    }

    public void Uninstall()
    {
        this.DropPluginTable("DepositTransaction");
    }

    public new IDbSet<TEntity> Set<TEntity>() where TEntity : BaseEntity
    {
        return base.Set<TEntity>();
    }
}

I've created empty project and enabled migrations there. Then I've copied and adjusted Configuration.cs so it looks like this:

namespace Nop.Plugin.Payments.Deposit.Migrations
{
    using Data;
    using System.Data.Entity.Migrations;

    internal sealed class Configuration : DbMigrationsConfiguration<DepositTransactionObjectContext>
    {
        public Configuration()
        {
            AutomaticMigrationsEnabled = true;
        }

        protected override void Seed(DepositTransactionObjectContext context)
        {
        }
    }
}

However when I run Add-Migration I get following error:

Using StartUp project 'Nop.Web'.
Using NuGet project 'Nop.Plugin.Payments.Deposit'.
System.Data.Entity.Migrations.Infrastructure.MigrationsException: No migrations configuration type was found in the assembly 'Nop.Plugin.Payments.Deposit'. (In Visual Studio you can use the Enable-Migrations command from Package Manager Console to add a migrations configuration).
   at System.Data.Entity.Utilities.TypeFinder.FindType(Type baseType, String typeName, Func`2 filter, Func`2 noType, Func`3 multipleTypes, Func`3 noTypeWithName, Func`3 multipleTypesWithName)
   at System.Data.Entity.Migrations.Utilities.MigrationsConfigurationFinder.FindMigrationsConfiguration(Type contextType, String configurationTypeName, Func`2 noType, Func`3 multipleTypes, Func`3 noTypeWithName, Func`3 multipleTypesWithName)
   at System.Data.Entity.Migrations.Design.ToolingFacade.BaseRunner.FindConfiguration()
   at System.Data.Entity.Migrations.Design.ToolingFacade.ScaffoldRunner.Run()
   at System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate)
   at System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate)
   at System.Data.Entity.Migrations.Design.ToolingFacade.Run(BaseRunner runner)
   at System.Data.Entity.Migrations.Design.ToolingFacade.Scaffold(String migrationName, String language, String rootNamespace, Boolean ignoreChanges)
   at System.Data.Entity.Migrations.AddMigrationCommand.Execute(String name, Boolean force, Boolean ignoreChanges)
   at System.Data.Entity.Migrations.AddMigrationCommand.<>c__DisplayClass2.<.ctor>b__0()
   at System.Data.Entity.Migrations.MigrationsDomainCommand.Execute(Action command)
No migrations configuration type was found in the assembly 'Nop.Plugin.Payments.Deposit'. (In Visual Studio you can use the Enable-Migrations command from Package Manager Console to add a migrations configuration).

I read a lot of same questions and everywhere the reason was wrong project specified in the Enable-Migrations cmdlet (not one having context defined). But it's not my case as you can see.

What else could be a reason?

回答1:

Soooo I've managed to solve the problem.

For investigation I've downloaded sources of Entity Framework and checked all methods mentioned in exception stack. In the method System.Data.Entity.Utilities.TypeFinder.FindType I've found such instruction:

var types = _assembly.GetAccessibleTypes()
                                 .Where(t => baseType.IsAssignableFrom(t));

Which lead me to GetAccessibleTypes() method where I found this:

return assembly.DefinedTypes.Select(t => t.AsType());

Having DefinedTypes is a property of System.Reflection.Assembly class I've tried to get this property manually by loading my assembly in Powershell and getting this property:

$a = [System.Reflection.Assembly]::LoadFrom("P:\nopCommerce\Presentation\Nop.Web\Plugins\Payments.Deposit\Nop.Plugin.Payments.Deposit.dll")
$a.DefinedTypes

The result was emptyness as there were no types at all.

So I tried to get types in a different way:

$a.GetTypes()

The result was an error:

Exception calling "GetTypes" with "0" argument(s): "Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information."

When I checked LoaderException property I've found following:

$Error[0].Exception.InnerException.LoaderExceptions

Could not load file or assembly 'Autofac, Version=3.5.0.0, Culture=neutral, PublicKeyToken=17863af14b0044da' or one of its dependencies. The system cannot find the file specified. Could not load file or assembly 'Nop.Services, Version=3.8.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified. Could not load file or assembly 'System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of its dependencies. The system cannot find the file specified. Could not load file or assembly 'System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of its dependencies. The system cannot find the file specified. Could not load file or assembly 'System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of its dependencies. The system cannot find the file specified. Could not load file or assembly 'System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of its dependencies. The system cannot find the file specified.

It looked .NET tried to load all assemblies, my assembly depended on and of course could not as they are not in same folder because they are shared, located elsewhere and loaded by start up project, not by mine.

So I've copied all required assemblies to same folder as mine and tried to Enable-Migrations again. This time it was performed without any errors!

I ask myself why wouldn't Enable-Migrations load all those assemblies itself as it takes start up project into consideration. But it's rather minor issue.



回答2:

You also need to add -ProjectName parameter to specify where you want the migrations to go. So for example:

Enable-Migrations -ProjectName Nop.Plugin.Payments.Deposit -StartUpProjectName nop.web -ContextProjectName Nop.Plugin.Payments.Deposit -verbose

It should default to the project you have selected in the console window. https://coding.abel.nu/2012/03/ef-migrations-command-reference/