在动态实例化具有嵌套类型arg的对象时抛出TypeLoadException

时间:2012-08-16 21:18:55

标签: c# .net nested-generics

虽然为OSS project工作做贡献,但我遇到了TypeLoadException。我正在创建一个接缝,开发人员可以注入自己的Repository类来删除对EF的具体依赖,这样我就可以隔离我的新代码并运行一些测试。

对于具有嵌套类型arg的类型运行Activator.CreateInstance()似乎会在运行时创建它。我之前已经多次使用过这种模式,但这次不同之处在于我正在使用它来动态注入通用的Repository模式实现。问题似乎与那种类型的arg有关。我现在很难过,所以任何帮助都会非常感激。

以下是我收到的错误:

System.TypeLoadException: Could not load type 'Rock.Tests.Fakes.FakeRepository' from assembly 'Rock.Tests, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
    at System.Reflection.RuntimeAssembly.GetType(RuntimeAssembly assembly, String name, Boolean throwOnError, Boolean ignoreCase, ObjectHandleOnStack type)
    at System.Reflection.RuntimeAssembly.GetType(String name, Boolean throwOnError, Boolean ignoreCase)
    at System.Activator.CreateInstance(String assemblyName, String typeName, Boolean ignoreCase, BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes, Evidence securityInfo, ref StackCrawlMark stackMark)
    at System.Activator.CreateInstance(String assemblyName, String typeName)
    at Rock.Data.RepositoryFactory`1.FindRepository() in RepositoryFactory.cs: line 30
    at Rock.Data.Service`1..ctor() in Service.cs: line 34
    at Rock.Core.AttributeService..ctor()
    at Rock.Attribute.Helper.LoadAttributes(IHasAttributes item) in Helper.cs: line 165
    at Rock.Data.ModelWithAttributes`1.get_Attributes() in ModelWithAttributes.cs: line 38
    at Rock.CMS.Page.MapPagesRecursive(Page page) in Page.cs: line 422
    at Rock.CMS.Page.ExportObject() in Page.cs: line 410
    at Rock.Tests.CMS.PageTests.TheExportObjectMethod.ShouldCopyPropertiesOfDTO() in PageTests.cs: line 16

以下是Rock.Data命名空间中的一些相关(带注释的)代码段:

IRepository.cs

public interface IRepository<T> where T : class
{
    // Very basic CRUD repository contract...
}

EFRepository.cs

public class EFRepository<T> where T : Model<T>
{
    // Concrete implementation of IRepository<T> specific to Entity Framework 4.3
}

Service.cs

public class Service<T> where T : Model<T>
{
    private readonly IRepository<T> _repository;

    // Inside this constructor are my changes...
    public Service() // : this(new EFRepository<T>())
    {
        // Instead of hard-coding the use of EFRepository here, I
        // thought it might be worthwhile to add a call out to a
        // factory method implementation.

        var factory = new RepositoryFactory<T>();
        _repository = factory.FindRepository();
    }

    // This constructor never really appears to be called.
    // From my test code's entry point, there's no way for me to
    // explicitly call this constructor, hence the factory implemenation.
    public Service(IRepository<T> repository)
    {
        _repository = repository;
    }
}

RepositoryFactory.cs

// Here's my quick/dirty factory method implementation to try to generically
// instantiate an IRepository of my choosing for testing purposes...
public class RepositoryFactory<T> where T : Model<T>
{
    public IRepository<T> FindRepository()
    {
        var repositoryTypeSetting = ConfiguraitonManager.AppSettings["RepositoryType"];

        if (string.IsNullOrEmpty(repositoryTypeSetting))
        {
            return new EFRepository<T>();
        }

        var settingArray = repositoryTypeSetting.Split(new[] { ',' });

        // I'm aware that Trim() is superfluous here, but this will be part of a development
        // framework, so I'm trying to take whitespace/developer error into account.
        var className = settingArray[0].Trim();
        var assemblyName = settingArray[1].Trim();

        // I've tried with and without Unwrap(), the exception originates from Activator.CreateInstance()
        return (IRepository<T>) Activator.CreateInstance(assemblyName, className).Unwrap();
    }
}

以下是我在单独的Rock.Tests项目中使用的一些虚假对象和测试代码片段......

FakeRepository.cs

// Basic fake/stub implementation
public class FakeRepository<T> : IRepository<T> where T : Model<T>
{
    // Nothing here yet other than `throw new NotImplementedException()` for each method in the contract
    // We never make it here...
}

PageTests.cs

// Very basic XUnit test example...
public class PageTests
{
    public class TheExportMethod
    {
        [Fact]
        public void ShouldNotBeEmpty()
        {
            var page = new Page { Name = "FooPage" };
            var result = page.Export();
            Assert.NotEmpty(result);
        }
    }
}

的App.config

<configuration>
    <appSettings>
        <clear/>
        <add key="RepositoryType" value="Rock.Tests.Fakes.FakeRepository,Rock.Tests"/>
    </appSettings>
</configuration>

希望这完全涵盖它。提前谢谢!

1 个答案:

答案 0 :(得分:1)

看起来您正在尝试实例化一个开放的泛型类型,因为您的配置文件没有为FakeRepository<T>指定泛型类型参数。你需要做更多的事情:

<add key="RepositoryType" value="Rock.Tests.Fakes.FakeRepository`1[[Some.ModelType,Some.ModelAssembly]],Rock.Tests"/>