Search code examples
autofacautofac-configurationautofac-module

Autofac Module Implementation - Connection String


Intro: - I am new to programming and I have used constructor injection and the factory pattern a lot. This will be my first time using a third party tool, Autofac, for DI. I understand the basics but I'm looking for understanding on a few things and I hope you can help.

Project Setup: N-Tier MVC Application (Data, Models, Tests, UI projects)(NuGet Autofac and Autofac.MVC5 installed only in UI project)

Question: Should Autofac be installed via NuGet in every project within my N-Tier MVC application or is just having it in the UI project okay? I am experimenting with Modules and curious if my DataAccessModule should be placed in my Data project vs the UI project.

Multi-Part Question: I'm trying to inject the connection string into my repository and I'm not sure if I'm doing this correctly. The repositories get registered based on what value for RepositoryType is set in my Web.config file. I am using ADO and stored procedures in my example. Reading about Autofac.Module, I thought this would be the way to go.

AutofacConfig

public static class AutofacConfig
{
    public static void RegisterComponents()
    {
        //Autofac - install Autofac and Autofac.MVC5 via NuGet

        //Autofac: create the builder with which components/services are registered.
        var builder = new ContainerBuilder();

        //register all the components that we will use in our container
        //register HomeController as I only have one
        builder.RegisterType<HomeController>().InstancePerRequest();

        //register DataAccessModule
        builder.RegisterModule(new DataAccessModule
        {
            ConnectionString = ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString,
            RepositoryType = ConfigurationManager.AppSettings["RepositoryType"].ToString()
        });

        //builds our container
        var container = builder.Build();

        //let ASP.NET MVC know that it should locate services using the AutofacDependencyResolver. This is Autofac’s implementation of the IDependencyResolver interface.
        DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
    }

}

DataAccessModule

public class DataAccessModule : Module
{
    public string ConnectionString { get; set; }
    public string RepositoryType { get; set; }

    protected override void Load(ContainerBuilder builder)
    {

        switch (RepositoryType)
        {
            case "ADO":
                builder.RegisterType<DirectorRepositoryADO>().As<IDirectorRepository>().WithParameter("connectionString",ConnectionString).InstancePerRequest();
                builder.RegisterType<DvdDirectorRepositoryADO>().As<IDvdDirectorRepository>().WithParameter("connectionString", ConnectionString).InstancePerRequest();
                builder.RegisterType<DvdRepositoryADO>().As<IDvdRepository>().WithParameter("connectionString", ConnectionString).InstancePerRequest();
                builder.RegisterType<RatingRepositoryADO>().As<IRatingRepository>().WithParameter("connectionString", ConnectionString).InstancePerRequest();
                break;
        }

    }

}

Repository

public class RatingRepositoryADO : IRatingRepository
{
    private readonly string  _connectionString;

    public RatingRepositoryADO(string connectionString)
    {
        _connectionString = connectionString;
    }

    public IEnumerable<Rating> GetAll()
    {
        List<Rating> ratings = new List<Rating>();

        using (var cn = new SqlConnection(_connectionString))
        {
            SqlCommand cmd = new SqlCommand("RatingSelectAll", cn);
            cmd.CommandType = CommandType.StoredProcedure;

            cn.Open();

            using (SqlDataReader dr = cmd.ExecuteReader())
            {
                while (dr.Read())
                {
                    Rating currentRow = new Rating();
                    currentRow.RatingId = (byte)dr["RatingId"];
                    currentRow.RatingName = dr["RatingName"].ToString();

                    ratings.Add(currentRow);
                }
            }
        }

        return ratings;
    }
}
  • Am I way off on how I have implemented my Module and passing in the connection string?
  • Should I separate my connection into a separate class that implements an interface with a method of GetConnectionString and resolve it and set the instance scope as a singleinstance instead of the way I have it?
  • Should repositories use InstancePerRequest or InstancePerLifetimeScope? I've seen both in some examples so I just want to know what is best to use and why/when

Question: Anywhere I see the "new" keyword in my project should I try and inject that dependency? For example, in my repository, the SqlConnection or SqlCommand objects?

Any help or guidance you can provide would be greatly appreciated. I trying to perform a very basic example to better understand how I might configure other components

Thank you in advance!


Solution

  • You need intalled Autofac package in every assembly, where you want to configure dependencies. I recommend to place DataBaseModule into assembly with repositories classes. Keep repository interfaces in some high-level assembly like BusinessLayer, add reference to it from DataLayer and UI assemblies and make repositories classes internal. So you can hide your implementations behind the abstractions. Make DatabaseModule public, it should be the only one, who knows about repositories implentations. Consider CompositionRoor pattern and take a look on Onion Architecture to understand the whole idea.

    Your injection of connection string is pretty nice. You can also encapsulate creating of SqlConnection in factory class with corresponding interface. And then inject IConnectionFactory to your repositories and gain more flexibility. Take a look on Dapper by the way for mapping data from DB to objects.

    While repositories are stateless(don't hold any muttable fields or properties) use SingleInstance() policy instead of others. Your repositories are stateless now, keep them stateless.

    You don't have to replace every new operator with DI pattern. Use it for "unstable" dependencies only. For instance classes 'String' and 'Int32' are stable like a rock, you don't need to avoid the new operator for them. In context of your repository SqlCommand and SqlDataReader are stable. You will never change SqlCommand with MongoComman, but keep SqlDataReader as it is. You will change the whole repository, I suppose. Class RatingRepositoryADO is unstable dependency for your application, so you hide it behind the interface and inject into controllers. Take a look on Mark Seeman book for theory and best practicies.

    Hope it helps.