상속된 클래스에서 종속성 주입 처리

종속성 주입(DI)은 훌륭한 것입니다. 종속성을 클래스의 생성자(가장 일반적)에 매개변수로 추가하고 DI 컨테이너에 등록하면 됩니다. 그러면 DI 컨테이너가 나머지를 관리합니다. DI의 주요 이점 중 일부는 더 나은 테스트 가능성, 더 큰 유지 관리 및 더 큰 재사용 가능성입니다.

// without DI
public class OrderController : Controller
{
    public ActionResult Post(Order order)
    {
        using (var dbContent = new MyDbContext())
        {
            dbContext.Orders.Add(order);
            dbContent.SaveChanges();

            return Ok();
        }
    }

    public ActionResult Get()
    {
        using (var dbContext = new MyDbContext())
        {
            var orders = dbContext.Orders.ToList();

            return new JsonResult(orders);
        }
    }
}

// with DI
public class OrderController : Controller
{
    private readonly MyDbContext _dbContext;

    public OrderController(MyDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public ActionResult Post(Order order)
    {
        _dbContext.Orders.Add(order);
        _dbContent.SaveChanges();

        return Ok();
    }

    public ActionResult Get()
    {
        var orders = _dbContext.Orders.ToList();

        return new JsonResult(orders);
    }
}


그러나 나는 최근에 DI가 상속된 클래스에 종속성 주입이 될 수 있는 실제적인 고통이 될 수 있는 사용 사례를 발견했습니다.



문제



저는 최근에 Mediatr 패키지로 많은 작업을 해왔습니다. 요청/요청 처리기 패턴을 사용하여 시스템에서 명령을 실행했습니다(Jason Taylor's Clean Architecture solution에서 영감을 받음).

저는 일반적으로 공통 종속성과 기능을 포함하는 RequestHandler 기본 클래스를 만듭니다. 그런 다음 각 구체적인 요청 핸들러는 이 기본 클래스에서 상속할 수 있습니다.

public abstract class RequestHandler<TRequest, TResponse> : IRequestHandler<TRequest, TResponse>
    where TRequest : IRequest<TResponse>
{
    public RequestHandler(IApplicationDbContext dbContext)
    {
        DbContext = dbContext;
    }

    protected IApplicationDbContext DbContext { get; }

    public abstract Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken);
}

public MyRequestHandler : RequestHandler<MyRequest, MyResponse>
{
    public MyRequestHandler(IApplicationDbContext dbContext) : base(dbContext) { }

    public override Task<MyResponse> Handle(MyRequest request, CancellationToken cancellationToken)
    {
        // handler logic
    }
}


기본 클래스에 더 많은 종속성을 추가하려고 할 때 문제가 발생합니다. 이제 모든 구체적인 요청 핸들러를 살펴보고 생성자를 업데이트하여 새로운 종속성도 가져와야 합니다. 다행히 코드가 누락되면 컴파일되지 않으므로 런타임 오류의 위험은 없지만 모든 단일 요청 핸들러를 업데이트해야 하는 것은 여전히 ​​엄청나게 지루한 작업입니다. 또한 클래스의 의도를 모호하게 만드는 매우 큰 생성자로 끝날 수 있습니다.

public abstract class RequestHandler<TRequest, TResponse> : IRequestHandler<TRequest, TResponse>
    where TRequest : IRequest<TResponse>
{
    public RequestHandler(IApplicationDbContext dbContext, ICurrentUser currentUser)
    {
        DbContext = dbContext;
        CurrentUser = currentUser;
    }

    protected IApplicationDbContext DbContext { get; }
    protected ICurrentUser CurrentUser { get; }

    public abstract Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken);
}

public MyRequestHandler : RequestHandler<MyRequest, MyResponse>
{
    public MyRequestHandler(IApplicationDbContext dbContext, ICurrentUser currentUser) : base(dbContext, currentUser) { }

    public override Task<MyResponse> Handle(MyRequest request, CancellationToken cancellationToken)
    {
        // handler logic
    }
}


솔루션 - 종속성 집계



이 문제에 대한 해결책은 정말 간단합니다. 종속성을 직접 주입하는 대신 종속성을 포함하는 새 클래스(집계라고 함)를 만들고 대신 주입합니다.

public interface IDependencyAggregate 
{
    IApplicationDbContext DbContext { get; }
    ICurrentUser { get; }
}

public class DependencyAggregate : IDependencyAggregate
{
    public DependencyAggregate(IApplicationDbContext dbContext, ICurrentUser currentUser)
    {
        DbContext = dbContext;
        CurrentUser = currentUser;
    }

    public IApplicationDbContext DbContext { get; }
    public ICurrentUser CurrentUser { get; }
}

public abstract class RequestHandler<TRequest, TResponse> : IRequestHandler<TRequest, TResponse>
    where TRequest : IRequest<TResponse>
{
    public RequestHandler(IDependencyAggregate aggregate)
    {
        DbContext = aggregate.DbContext;
        CurrentUser = aggregate.CurrentUser;
    }

    protected IApplicationDbContext DbContext { get; }
    protected ICurrentUser CurrentUser { get; }

    public abstract Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken);
}

public MyRequestHandler : RequestHandler<MyRequest, MyResponse>
{
    public MyRequestHandler(IDependencyAggregate aggregate) : base(aggregate) { }

    public override Task<MyResponse> Handle(MyRequest request, CancellationToken cancellationToken)
    {
        // handler logic
    }
}


이제 새 종속성을 추가하려는 경우 코드를 변경해야 하는 유일한 위치는 DependencyAggregate 클래스와 RequestHandler 기본 클래스입니다(상속된 클래스를 변경할 필요가 없음).



결론



이 게시물에서는 기본 클래스에 주입할 종속성 집계 클래스를 만들어 상속된 클래스에서 종속성 주입을 관리하는 간단한 방법을 설명했습니다. 이렇게 하면 상속된 모든 클래스를 변경해야 하는 경우 새 종속성을 쉽게 도입할 수 있습니다.

주로 풀 스택 .NET 및 Vue 웹 개발에 대해 게시합니다. 게시물을 놓치지 않으려면 이 블로그 및 subscribe to my newsletter 을 팔로우하십시오. 이 게시물이 도움이 되셨다면 좋아요와 공유 부탁드립니다. 에서 나를 찾을 수도 있습니다.

좋은 웹페이지 즐겨찾기