Conventional Dependency Injection in Autofac

Instead of containing a definition or hello-world Autofac basics, this article will focus on bringing new aspect to registration and composition root that we currently use on some projects at Trendyol

Dependency injection, which is a must and a very useful asset, provides a nice loosely coupled and structured capability whenever you develop an enterprise application.

However, when a project grows and code becomes giantic, we can say that there is a problem caused by growing registration codes, composition root inflation, readibility issues, etc.

It is hard to write new registration code to composition root because of necessity of knowledge which layer responsible for the registration, which registration should invoke first and so on.

On the other hand, we may have not only registration cost, but also auto-mapping registration cost and some similar costs.

Let’s consider composition root bloating example from nopCommerce below:

public virtual void Register(ContainerBuilder builder, ITypeFinder typeFinder, NopConfig config)
{
   builder.RegisterType<BackInStockSubscriptionService>().As<IBackInStockSubscriptionService>().InstancePerLifetimeScope();
   builder.RegisterType<CategoryService>().As<ICategoryService>().InstancePerLifetimeScope();
   builder.RegisterType<CompareProductsService>().As<ICompareProductsService>().InstancePerLifetimeScope();
   builder.RegisterType<RecentlyViewedProductsService>().As<IRecentlyViewedProductsService>().InstancePerLifetimeScope();
   builder.RegisterType<ManufacturerService>().As<IManufacturerService>().InstancePerLifetimeScope();
   builder.RegisterType<PriceFormatter>().As<IPriceFormatter>().InstancePerLifetimeScope();
   builder.RegisterType<ProductAttributeFormatter>().As<IProductAttributeFormatter>().InstancePerLifetimeScope();
   builder.RegisterType<ProductAttributeParser>().As<IProductAttributeParser>().InstancePerLifetimeScope();
   builder.RegisterType<ProductAttributeService>().As<IProductAttributeService>().InstancePerLifetimeScope();
   builder.RegisterType<ProductService>().As<IProductService>().InstancePerLifetimeScope();
   builder.RegisterType<CopyProductService>().As<ICopyProductService>().InstancePerLifetimeScope();
   builder.RegisterType<SpecificationAttributeService>().As<ISpecificationAttributeService>().InstancePerLifetimeScope();
   builder.RegisterType<ProductTemplateService>().As<IProductTemplateService>().InstancePerLifetimeScope();
   builder.RegisterType<CategoryTemplateService>().As<ICategoryTemplateService>().InstancePerLifetimeScope();
   builder.RegisterType<ManufacturerTemplateService>().As<IManufacturerTemplateService>().InstancePerLifetimeScope();
   builder.RegisterType<TopicTemplateService>().As<ITopicTemplateService>().InstancePerLifetimeScope();
   builder.RegisterType<AddressAttributeFormatter>().As<IAddressAttributeFormatter>().InstancePerLifetimeScope();
   builder.RegisterType<AddressAttributeParser>().As<IAddressAttributeParser>().InstancePerLifetimeScope();
   builder.RegisterType<AddressAttributeService>().As<IAddressAttributeService>().InstancePerLifetimeScope();
   builder.RegisterType<AddressService>().As<IAddressService>().InstancePerLifetimeScope();
   builder.RegisterType<AffiliateService>().As<IAffiliateService>().InstancePerLifetimeScope();
   builder.RegisterType<VendorService>().As<IVendorService>().InstancePerLifetimeScope();
   builder.RegisterType<SearchTermService>().As<ISearchTermService>().InstancePerLifetimeScope();
   builder.RegisterType<GenericAttributeService>().As<IGenericAttributeService>().InstancePerLifetimeScope();
   builder.RegisterType<FulltextService>().As<IFulltextService>().InstancePerLifetimeScope();
   builder.RegisterType<MaintenanceService>().As<IMaintenanceService>().InstancePerLifetimeScope();
   builder.RegisterType<CustomerAttributeFormatter>().As<ICustomerAttributeFormatter>().InstancePerLifetimeScope();
   builder.RegisterType<CustomerAttributeParser>().As<ICustomerAttributeParser>().InstancePerLifetimeScope();
   builder.RegisterType<CustomerAttributeService>().As<ICustomerAttributeService>().InstancePerLifetimeScope();
   builder.RegisterType<CustomerService>().As<ICustomerService>().InstancePerLifetimeScope();
   builder.RegisterType<CustomerRegistrationService>().As<ICustomerRegistrationService>().InstancePerLifetimeScope();
   builder.RegisterType<CustomerReportService>().As<ICustomerReportService>().InstancePerLifetimeScope();

   //...so on
}

We will have always new registrations in the future and we will put them into these registrations, at the end of the day, we are faced with a bloated code.

How we can handle this ? Here it is, the remedy is conventional assembly registration!

Generally we register classes as 3 lifetime which are TransientSingleton and LifetimeScope.

We sure this rate is approximately %90 of entire registrations. So, let’s ask ourselves: Is this done with some kind of elegant or automatic way ? Can it be managed from a central place ? Might we get rid of manual registrations ?

The answer is “yes we can”.

This approach is based on Interface Marking Pattern and uses 3 interfaces ITransientDependencyISingletonDependency, ILifetimeScopeDependency. As the name implies, these interfaces mark an implementation to register as its own indicated.

  • ITransientDependency = Autofac.InstancePerDependency()
  • ISingletonDependency = Autofac.SingleInstance()
  • ILifetimeScopeDependency = Autofac.InstancePerLifetimeScope()

For instance, CacheManager implements ICacheManager:

public class CacheManager : ICacheManager, ITransientDependency
{
}

Normally, we go Autofac module and register this implementation explicitly. But now we mark the implementation as ITransientDependency, framework or our conventional registration mechanism fills this registration automatically to Autofac’s Container.

Let’s take Autofac’s AssignableTo method. It searches given assembly and finds implementations that assignable to given ITransientDependency.

builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
       .AssignableTo<ITransientDependency>()
       .InstancePerDependency();

However, we should not forget, if we don’t use AsImplementedInterfaces method, Autofac will just register concrete types which means just CacheManager and we must use this concrete type while resolve operations. This is not we exactly want to. We also want to resolve through ICacheManager, but don’t want to register or load any ITransientDependency burden to container, because we will never resolve through
these interfaces that we used for sake of the conventional mechanism.

At the first glance we can use this method, but if we write this code snippet all Autofac’s registration modules we definitely violate the DRY principle.

In this situation we should write an abstraction that will never know ITransientDependency as a service or registration and it should provide any resolution that we want to by implementation interfaces like ICacheManager and also we keep it DRY.

That’s why we need to use an abstraction library providing us entire register and resolve operations and the other conventional registration mechanism in an easy way.

See: Autofac.Extras.IocManager.

See also for examples: Stove framework that we use in currently.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: