Sнаđошƒаӽ
Sнаđошƒаӽ

Reputation: 17612

Autofac Dependency resolution in constructor vs resolution in the method of usage

Before I begin my question, here is a background of my application:


Consider I have a class that only serves as a lookup class for database tables. My database contains about 350 tables. The following is a simplified example that shows exactly the structure in my application.

(if you think you've got the scenario, you can directly jump to the Question at the bottom.)

Here is the lookup class

public class Lookup
{
    private readonly IRepository<Table001> _table001Repository;
    // repositories Table002 to Table349
    private readonly IRepository<Table350> _table350Repository;

    public Lookup(
        IRepository<Table001> table001Repository,
        // parameters table002Repository to table349Repository
        IRepository<Table350> table350Repository)
    {
        _table001Repository = table001Repository;
        // assignments for table002Repository to table349Repository
        _table350Repository = table350Repository;
    }

    public Table001 GetTable001(/* fields for looking up Table001 */)
    {
        // lookup logic using the repository for Table001, namely _table001Repository
    }

    // other lookup methods for Table002 to Table349

    public Table350 GetTable350(/* fields for looking up Table350 */)
    {
        // lookup logic using the repository for Table350, namely _table350Repository
    }
}

Now, an example class that uses this Lookup class:

public class MyClass
{
    private readonly ILookup _lookup;

    public MyClass(ILookup lookup)
    {
        _lookup = lookup;
    }

    public Method()
    {
        Table001 table001 = _lookup.GetTable001(/* fields */);
        Table002 table002 = _lookup.GetTable002(/* fields */);
        Table003 table003 = _lookup.GetTable003(/* fields */);
        ...
    }
}

Registrations to Autofac are done this way:

// Lookup class registration
builder.RegisterType<Lookup>().As<ILookup>().InstancePerLifetimeScope();

// generic registration for Repositories
builder.RegisterGeneric(typeof(Repository<>)).As(typeof(IRepository<>)).InstancePerLifetimeScope();

Question:

As you can see, the class that actually makes use of the Lookup class is using only three methods from 350 lookup methods. But while instantiating Lookup class in MyClass (the _lookup field), all 350 repositories are resolved too, instantiating 350 repositories. Now my concern is regarding performance/efficiency. Which of the following two options is better performance wise, and why?

  1. Resolving dependencies as is being done now, that is using auto-injection in the constructor.
  2. Resolving the repository only in the method that actually uses the repository instead of auto-injection in the constructor. For example GetTable001() would become this (the field _table001Repository will be removed from Lookup class, so will be the parameter table001Repository in its constructor):

    public Table001 GetTable001(/* fields for looking up Table001 */)
    {
        IRepository<Table001> table001Repository = container.Resolve<IRepository<Table001>>();
        // lookup logic
    }
    

In option 1, all 350 repositories are instantiated while resolving _lookup, even though, for example, the class MyClass uses only three lookup methods. Is the second approach any better than the first? Or, is the first option actually better than the second performance-wise? Also, does the scope of resolution (InstancePerLifetimeScope, InstancePerDependency, etc.) make any difference?

Thanks.


PS: please comment if you think this question should better be posted in SoftwareEngineering. The reason of asking it here is the broad audience SO has.

Upvotes: 1

Views: 908

Answers (2)

Win
Win

Reputation: 62300

Seem like Lookup class itself is a Service Locator Pattern.

IRepository<Table001> table001Repository = container.Resolve<IRepository<Table001>>();

Service Locator has few problems. One of them is it doesn't follow Don't call us, we'll call you principle. It directly ask for the things we need rather than handed them to us.

With the service locator, we have to examine the code, searching for capricious calls that retrieve a required service. Constructor injection allowed us to view dependencies—all of them—with a glance at the constructor, or at a distance, via IntelliSense.

Solution

Ideally, you want to inject IRepository<Table001> to MyClass. I also use the similar approach in my projects.

public class MyClass
{
    private readonly IRepository<Table001> _table001;

    public MyClass(IRepository<Table001> table001)
    {
       _table001 = table001;
    }
}

If you see yourself injecting a lot of dependencies into a single class, it might be that the class violates Single Responsibility Principle. You might want to separate it to different classes.

"creating an object instance is something the .NET Framework does extremely fast. Any performance bottleneck your application may have will appear in other places." - Mark Seemann author of Dependency Injection in .Net

Upvotes: 1

Stephen P.
Stephen P.

Reputation: 2397

There are a few different things to consider here:

First, is anything else using the ILookup instance? If so, you already have an instance of those 350 repositories created, so it doesn't really matter in that respect.

Second, non constructor resolution of injected members (e.g. using ServiceLocator) is considered an anti-pattern (http://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern/). Thus, I would try to avoid if possible. The biggest reason why (as outlined in the article) is that it hides class dependencies from their consumers.

Third, is there any reason you cannot create a new service which consists of only the necessary repository as a dependency? If you can create an IMyClassLookup service which implements the ILookup interface that has only the necessary repositories you need, that's definitely the easiest and cleanest way to go. Then you would just register your new service and use it in your constructor:

New Interface

public interface IMyClassLookup : ILookup
{
}

New Class

public class MyClassLookup : IMyClassLookup
{
    private readonly IWhateverRepository _whateverRepository;

    public MyClassLookup(IWhateverRepository whateverRepository)
    {
        _whateverRepository = whateverRepository;
    }

    // IMyClassLookup implementations here that use _whateverRepository
}

Dependency registration

builder.RegisterType<MyClassLookup>().As<IMyClassLookup>().InstancePerLifetimeScope();

Your class

public class MyClass
{
    private readonly IMyClassLookup _myClassLookup;

    public MyClass(IMyClassLookup myClassLookup)
    {
        _myClassLookup = myClassLookup;
    }

    public Method()
    {
        Table001 table001 = _myClassLookup.GetTable001(/* fields */);
        Table002 table002 = _myClassLookup.GetTable002(/* fields */);
        Table003 table003 = _myClassLookup.GetTable003(/* fields */);
        ...
    }
}

Upvotes: 1

Related Questions