1

在为OSS 项目做贡献的同时,我遇到了 TypeLoadException。我正在创建一个接缝,开发人员可以通过该接缝注入他们自己的 Repository 类来删除对 EF 的具体依赖,这样我就可以隔离我的新代码并运行一些测试。

似乎Activator.CreateInstance()针对具有嵌套类型 arg 的类型运行会在运行时创建它时遇到麻烦。我以前多次使用过这种模式,但这次不同的是我使用它来动态注入一个通用的存储库模式实现。问题似乎确实与该类型 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
}

服务.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);
        }
    }
}

应用程序配置

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

希望这涵盖了它相当彻底。提前致谢!

4

1 回答 1

1

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

<add key="RepositoryType" value="Rock.Tests.Fakes.FakeRepository`1[[Some.ModelType,Some.ModelAssembly]],Rock.Tests"/>
于 2012-08-17T20:30:44.740 回答