【问题标题】:ResolveEventArgs.RequestingAssembly is null when AppDomain.CurrentDomain.AssemblyResolve is called调用 AppDomain.CurrentDomain.AssemblyResolve 时 ResolveEventArgs.RequestingAssembly 为 null
【发布时间】:2014-09-09 23:00:17
【问题描述】:

作为我们持续集成工作的一部分,我们创建了一个自定义部署应用程序来处理 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)的服务器时工作正常,但将相同的简单数据上下文部署到服务器运行 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 上可以重现。

【问题讨论】:

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


    【解决方案1】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-07-10
      • 2014-05-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多