调用AppDomain.CurrentDomain.AssemblyResolve时,ResolveEventArgs.RequestingAssembly为null

时间:2014-09-09 23:00:18

标签: c# sql-server-2008-r2 sql-server-2012 entity-framework-6

作为我们持续集成工作的一部分,我们已经创建了一个自定义部署应用程序,用于处理Entity Framework 6.0 Code First数据库迁移。我们获取目标DLL并将其与当前部署的DLL进行比较,以确定该路径是向上还是向下的迁移路径。为了确保我们正在加载正确版本的数据上下文的DLL,我们正在侦听AppDomain.CurrentDomain.AssemblyResolve(请参阅:Loading Dependent Assemblies Manually)。

using System;
using System.Collections.Concurrent;
using System.IO;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;

public enum MigrationsSource
{
    Target = 1,
    Deployed= 2
}

public class AssemblyLoader
{
    private readonly ConcurrentDictionary<string, MigrationsSource> m_Sources = new ConcurrentDictionary<string, MigrationsSource>();
    private string m_DeployedPath;
    private string m_TargetPath;

    public AssemblyLoader()
    {
        AppDomain.CurrentDomain.AssemblyResolve += ResolveDependentAssembly;
    }

    ~AssemblyLoader()
    {
        AppDomain.CurrentDomain.AssemblyResolve -= ResolveDependentAssembly;
    }

    private Assembly ResolveDependentAssembly(object sender, ResolveEventArgs args)
    {
        MigrationsSource source;

        if (m_Sources.TryGetValue(BuildAssemblyId(args.RequestingAssembly), out source))
        {
            var assemblyName = new AssemblyName(args.Name);

            string targetPath = Path.Combine(
                source == MigrationsSource.Deployed ? m_DeployedPath : m_TargetPath, string.Format("{0}.dll", assemblyName.Name));

            assemblyName.CodeBase = targetPath;

            //We have to use LoadFile here, otherwise we won't load a differing
            //version, regardless of the codebase because only LoadFile
            //will actually load a *new* assembly if it's at a different path
            //See: http://msdn.microsoft.com/en-us/library/b61s44e8(v=vs.110).aspx
            var dependentAssembly = Assembly.LoadFile(assemblyName.CodeBase);
            m_Sources.TryAdd(BuildAssemblyId(dependentAssembly), source);

            return dependentAssembly;
        }

        return null;
    }

    private string BuildAssemblyId(Assembly assembly)
    {
        return
            Convert.ToBase64String(
            HashAlgorithm.Create("SHA1")
                .ComputeHash(UTF8Encoding.UTF8.GetBytes(
                    string.Format("{0}|{1}", assembly.FullName, assembly.Location))));
    }
}

将部署简单数据上下文(单个表中的单个表)部署到运行SQL Server 2012的服务器(运行Windows Server 2012)时,此工作正常,但在将相同的简单数据上下文部署到a时会发生以下错误服务器运行SQL Server 2008 R2(运行Windows Server 2008 Standard):

Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object.
   at EntityFramework.AssemblyLoader.BuildAssemblyId(Assembly assembly) in c:\EntityFramework\AssemblyLoader.cs:line 103
   at EntityFramework.AssemblyLoader.ResolveDependentAssembly(Object sender, ResolveEventArgs args) in c:\EntityFramework\AssemblyLoader.cs:line 42
   at System.AppDomain.OnAssemblyResolveEvent(RuntimeAssembly assembly, String assemblyFullName)
   at System.RuntimeTypeHandle.GetTypeByName(String name, Boolean throwOnError, Boolean ignoreCase, Boolean reflectionOnly, StackCrawlMarkHandle stackMark, IntPtr pPrivHostBinder, Boolean loadTypeFromPartialName, ObjectHandleOnStack type)
   at System.RuntimeTypeHandle.GetTypeByName(String name, Boolean throwOnError, Boolean ignoreCase, Boolean reflectionOnly, StackCrawlMark& stackMark, IntPtr pPrivHostBinder, Boolean loadTypeFromPartialName)
   at System.RuntimeType.GetType(String typeName, Boolean throwOnError, Boolean ignoreCase, Boolean reflectionOnly, StackCrawlMark& stackMark)
   at System.Type.GetType(String typeName, Boolean throwOnError)
   at System.Data.Entity.Infrastructure.DependencyResolution.ClrTypeAnnotationSerializer.Deserialize(String name, String value)
   at System.Data.Entity.Core.SchemaObjectModel.SchemaElement.CreateMetadataPropertyFromXmlAttribute(String xmlNamespaceUri, String attributeName, String value)
   at System.Data.Entity.Core.SchemaObjectModel.SchemaElement.AddOtherContent(XmlReader reader)
   at System.Data.Entity.Core.SchemaObjectModel.SchemaElement.ParseAttribute(XmlReader reader)
   at System.Data.Entity.Core.SchemaObjectModel.SchemaElement.Parse(XmlReader reader)
   at System.Data.Entity.Core.SchemaObjectModel.Schema.HandleEntityTypeElement(XmlReader reader)
   at System.Data.Entity.Core.SchemaObjectModel.Schema.HandleElement(XmlReader reader)
   at System.Data.Entity.Core.SchemaObjectModel.SchemaElement.ParseElement(XmlReader reader)
   at System.Data.Entity.Core.SchemaObjectModel.SchemaElement.Parse(XmlReader reader)
   at System.Data.Entity.Core.SchemaObjectModel.Schema.HandleTopLevelSchemaElement(XmlReader reader)
   at System.Data.Entity.Core.SchemaObjectModel.Schema.InternalParse(XmlReader sourceReader, String sourceLocation)
   at System.Data.Entity.Core.SchemaObjectModel.Schema.Parse(XmlReader sourceReader, String sourceLocation)
   at System.Data.Entity.Core.SchemaObjectModel.SchemaManager.ParseAndValidate(IEnumerable`1 xmlReaders, IEnumerable`1 sourceFilePaths, SchemaDataModelOption dataModel, AttributeValueNotification providerNotification, AttributeValueNotification providerManifestTokenNotification, ProviderManifestNeeded providerManifestNeeded, IList`1& schemaCollection)
   at System.Data.Entity.Core.SchemaObjectModel.SchemaManager.ParseAndValidate(IEnumerable`1 xmlReaders, IEnumerable`1 sourceFilePaths, SchemaDataModelOption dataModel, DbProviderManifest providerManifest, IList`1& schemaCollection)
   at System.Data.Entity.Core.Metadata.Edm.EdmItemCollection.LoadItems(IEnumerable`1 xmlReaders, IEnumerable`1 sourceFilePaths, SchemaDataModelOption dataModelOption, DbProviderManifest providerManifest, ItemCollection itemCollection, Boolean throwOnError)
   at System.Data.Entity.Core.Metadata.Edm.EdmItemCollection.Init(IEnumerable`1 xmlReaders, IEnumerable`1 filePaths, Boolean throwOnError)
   at System.Data.Entity.Core.Metadata.Edm.EdmItemCollection..ctor(IEnumerable`1 xmlReaders)
   at System.Data.Entity.Utilities.XDocumentExtensions.GetStorageMappingItemCollection(XDocument model, DbProviderInfo&providerInfo)
   at System.Data.Entity.Migrations.Infrastructure.EdmModelDiffer.Diff(XDocument sourceModel, XDocument targetModel, Lazy`1 modificationCommandTreeGenerator, MigrationSqlGenerator migrationSqlGenerator, String sourceModelVersion, String targetModelVersion)
   at System.Data.Entity.Migrations.DbMigrator.IsModelOutOfDate(XDocument model, DbMigration lastMigration)
   at System.Data.Entity.Migrations.DbMigrator.Upgrade(IEnumerable`1 pendingMigrations, String targetMigrationId, String lastMigrationId)
   at System.Data.Entity.Migrations.DbMigrator.UpdateInternal(String targetMigration)
   at System.Data.Entity.Migrations.DbMigrator.<>c__DisplayClassc.<Update>b__b()
   at System.Data.Entity.Migrations.DbMigrator.EnsureDatabaseExists(Action mustSucceedToKeepDatabase)
   at System.Data.Entity.Migrations.DbMigrator.Update(String targetMigration)
   at System.Data.Entity.Internal.DatabaseCreator.CreateDatabase(InternalContext internalContext, Func`3 createMigrator, ObjectContext objectContext)
   at System.Data.Entity.Internal.InternalContext.CreateDatabase(ObjectContext objectContext, DatabaseExistenceState existenceState)
   at System.Data.Entity.Database.Create(DatabaseExistenceState existenceState)
   at System.Data.Entity.CreateDatabaseIfNotExists`1.InitializeDatabase(TContext context)
   at System.Data.Entity.Internal.InternalContext.<>c__DisplayClassf`1.<CreateInitializationAction>b__e()
   at System.Data.Entity.Internal.InternalContext.PerformInitializationAction(Action action)
   at System.Data.Entity.Internal.InternalContext.PerformDatabaseInitialization()
   at System.Data.Entity.Internal.LazyInternalContext.<InitializeDatabase>b__4(InternalContext c)
   at System.Data.Entity.Internal.RetryAction`1.PerformAction(TInput input)
   at System.Data.Entity.Internal.LazyInternalContext.InitializeDatabaseAction(Action`1 action)
   at System.Data.Entity.Internal.LazyInternalContext.InitializeDatabase()
   at EntityFramework.DbDeploymentManager.HandleDatabaseInitialization(DatabaseEndpoint endpoint) in c:\EntityFramework\DbDeploymentManager.cs:line 185
   at EntityFramework.DbDeploymentManager.Deploy() in c:\EntityFramework\DbDeploymentManager.cs:line 67
   at EntityFramework.Deployer.Program.Main(String[] args) in c:\EntityFramework.Deployer\Program.cs:line 23

某些日志记录输出显示BuildAssemblyId正在抛出NullReferenceException,因为传入的args.RequestingAssemblynullargs.Name中的值是包含数据上下文的DLL的名称。这会在创建表时创建上下文和数据种子之间抛出,但是为空。

应用程序在每台机器上独立运行。我们通过将每台计算机更新到.NET 4.5.1来排除.NET Framework差异。此外,我们在同一台机器上运行部署应用程序;我们使用安装了SQL Server 2012的机器。最后,部署应用程序和简单数据上下文都引用了相同版本的EntityFramework。

修改

有人建议表对象上的属性可能是问题的根源。

[Table("dbo.tblCustomer")]
public class Customer
{
    public Guid Id { get; set; }
    public string Name { get; set; }
}

我们删除了TableAttribute,但在SQL Server 2008 R2上仍然失败,错误在SQL Server 2012上变得可重现。

1 个答案:

答案 0 :(得分:1)

我们应用的粘性绷带是在确定目标和已部署的DLL之后以及在初始化数据库之前取消注册我们的ResolveDependentAssembly事件。这在我们目前的情况下可以正常工作,因为我们只是将数据上下文部署到一个环境中。

我们的长期解决方案是为目标和每个部署创建单独的应用程序域。