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;
}
}
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!
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.