简体   繁体   中英

IOC Container Circular Dependency Between Service and Factory

I have the following classes/interfaces:

public class DataExpressionViewModelFactory : IDataExpressionViewModelFactory
{
    private readonly IDataExpressionService DataExpressionService;

    public DataExpressionViewModelFactory(IDataExpressionService dataExpressionService)
    {
        DataExpressionService = dataExpressionService;
    }

    public DataExpressionViewModel Create(DatabaseTableColumn column)
    {
        return new DataExpressionViewModel(DataExpressionService, column);
    }
}

public class DataExpressionService : IDataExpressionService
{
    private readonly IDataExpressionViewModelFactory DataExpressionViewModelFactory;

    ...
}

Using an IOC container ( Microsoft.Extensions.DependencyInjection ), there is a circular dependency between them. An IDataExpressionViewModelFactory requires an IDataExpressionService , and vice versa.

An IDataExpressionViewModelFactory creates DataExpressionViewModel s, passing in an IDataExpressionService . DataExpressionViewModel s use IDataExpressionService to validate data expression strings (user input) and create further DataExpressionViewModel s from those, as expressions can be nested within each other (a recursive model).

Is there a way I can resolve the circular dependency?

After refactoring the application to make IDataExpressionViewModel a model ( IDataExpression ), and splitting the service into a "writer" service and an "object creation" service, which removed the circular dependency, I came upon a solution that works for the original design from reading about delegate factories .

I was using delegates as factories for other entities, but I actually had two constructors for this particular entity and so used a factory class instead. Now knowing about delegate factories, I would state that the code smell (or one of them) in my original design was having the service instantiated within the factory itself.

To use a delegate factory to remove the circular dependency:

Delegate Factory Class

public delegate IDataExpressionViewModel CreateDataExpressionViewModel(DatabaseTableColumn column);
public delegate IDataExpressionViewModel CreateDataExpressionViewModelWithExpression(DatabaseTableColumn column, string expression);

public class DataExpressionViewModelDelegateFactory
{
    public readonly CreateDataExpressionViewModel;
    public readonly CreateDataExpressionViewModelWithExpression

    public DataExpressionViewModelDelegateFactory(
        CreateDataExpressionViewModel createDataExpressionViewModel,
        CreateDataExpressionViewModelWithExpression createDataExpressionViewModelWithExpression
    )
    {
        CreateDataExpressionViewModel = createDataExpressionViewModel;
        CreateDataExpressionViewModelWithExpression = createDataExpressionViewModelWithExpression;
    }
}

IOC Implementation (in App.xaml.cs)

services.AddTransient((provider) => new DataExpressionViewModelDelegateFactory(
    new CreateDataExpressionViewModel(
        (column) => new DataExpressionViewModel(
            provider.GetRequiredService<IDataExpressionService>(),
            column
        )
    ),
    new CreateDataExpressionViewModelWithExpression(
        (column, expression) => new DataExpressionViewModel(
            provider.GetRequiredService<IDataExpressionService>(),
            column,
            expression
        )
    )
));

The delegate factory is not dependent on the service, as the object creation dependencies on the service are resolved on creation by the ServiceProvider , so the dependency is removed.

As per the article I linked, if lots of delegates are required and it becomes too cumbersome, you can also use Func in the factory class rather than creating specific delegates for every method.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM