1

仕事でOSS プロジェクトに貢献しているときに、TypeLoadException に遭遇しました。私は、新しいコードを分離していくつかのテストを実行できるように、開発者が独自の Repository クラスを挿入して EF への具体的な依存関係を削除できるようにするシームの作成に取り組んでいます。

Activator.CreateInstance()ネストされた型引数を持つ型に対して実行すると、実行時にそれを作成する際にレンチがスローされるようです。このパターンはこれまで何度も使用してきましたが、今回の違いは、汎用リポジトリ パターンの実装を動的に挿入するために使用していることです。問題は実際にはその型 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>

うまくいけば、それはそれをかなり完全にカバーしています。前もって感謝します!

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 に答える