Entity Framework

.NET 개발의 경우 ORM의 99%가 Entity Framework를 사용합니다. Entity Framework에서 제공하는 DbContext는 Unit of Work 및 Repository 패턴을 위해 생성되었지만 비즈니스 프로젝트는 인프라에 의존하기 때문에 비즈니스 프로젝트에서 DbContext를 사용하는 것은 좋지 않습니다. 결과적으로 별도의 리포지토리와 UnitOfWork 인터페이스/클래스가 개발 및 개발되는데 이는 일반적으로 번거로운 작업이 아닙니다. 엔터티별로 리포지토리 인터페이스가 정의된 후 구현해야 하며 단위 테스트를 개별적으로 작성하는 것은 비효율의 끝을 보여주는 프로세스라고 할 수 있습니다. 저는 예전에 Java를 사용할 때 JPA를 사용했는데 인터페이스를 지정하여 구현 클래스 없이 바로 사용할 수 있다는 생각에 주목하고 오픈소스 프로젝트를 nuget에 업로드했습니다.

아이디어는 간단합니다. UnitOfWork 인터페이스가 삽입되면 주어진 인터페이스를 상속하는 모든 인터페이스가 발견되고 Interceptor 및 Refection을 사용하여 인스턴스가 생성되며 해당 인스턴스는 요청 시 반환됩니다.

아래와 같이 All Assemblies에서 내가 정의한 IEpaRepository를 상속하는 모든 인터페이스를 찾고 Castle의 ProxyGenerator로 생성한 인터페이스를 저장합니다.

AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes())
    .Where(p => p.IsInterface && p.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEpaRepository<,>)))
    .ToList().ForEach(c =>
    {
        var proxy = _proxyGenerator.CreateInterfaceProxyWithoutTarget(c, new EpaRepositoryInterceptor(_dbContext));
        if (!_repositories.ContainsKey(c))
        {
            _repositories.Add(c, proxy);
        }
    });

EpaRepositoryInterceptor는 Refection을 사용하여 인스턴스를 생성합니다.

/// <summary>
/// An interceptor class to create an instance for any repository interface inheriting <see cref="IEpaRepository{TEntity,TKey}"/>
/// </summary>
internal class EpaRepositoryInterceptor : IInterceptor
{
    private readonly IEpaDbContext _dbContext;

    public EpaRepositoryInterceptor(IEpaDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    /// <inheritdoc />
    public void Intercept(IInvocation invocation)
    {
        var args = invocation.Method.DeclaringType?.GenericTypeArguments;
        if (args is not { Length: 2 })
        {
            throw new InvalidOperationException("The interface should have two generic types, Entity and Key.");
        }

        var type = typeof(EpaRepository<,>).MakeGenericType(args(0), args(1));

        var instance = Activator.CreateInstance(type, _dbContext);
        var method = instance?.GetType().GetMethod(invocation.Method.Name);
        var resultSet = method?.Invoke(instance, invocation.Arguments);

        invocation.ReturnValue = resultSet;
    }
}

이와 같은 인스턴스를 만들고 모든 요청에서 해당 가상 인스턴스를 반환하면 IOrderRepository를 정의할 필요가 없고 OrderRepository를 구현해야 하므로 생산성이 기하급수적으로 증가할 수 있습니다.

3년전쯤 비슷한 오픈소스를 만들때 제약이 너무 많았는데 이번 새버전은 정말 깔끔하고 심플하고 기존 소스의 변경이 거의 없어서 매우 만족합니다.

너겟에 대한 더 많은 예제와 WIKI…

https://www.nuget.org/packages/EntityFrameworkCore.PersistenceApi/

EntityFrameworkCore.PersistenceApi 1.0.0

작업 단위와 자동으로 연결된 EntityFramework 리포지토리 패턴.

www.nuget.org