by oclockvn at 04:45 PM 11/17/2016

sử dụng autofac trong asp.net mvc

autofac

Là 1 ioc container như bao ioc container khác (Unity/Ninject/StructureMap/...). Sở dĩ mình chọn autofac là vì:

Autofac is always kept up to date to support the latest version of ASP.NET MVC

và autofac intergrate with asp.net mvc giúp cho việc setup và sử dụng đơn giản hơn bản giờ hết.

Trong phạm vi bài viết này mình sẽ không nói về Inversion of Control (IoC)/ IoC container mà chỉ hướng dẫn cách sử dụng Autofac ioc container trong asp.net mvc. Bên cạnh đó, sử dụng ioc hay không cũng tùy vào ý muốn của bạn (có lẽ bạn nên tìm hiểu nhiều hơn để biết mình có nên/cần sử dụng ioc hay không).

Xem tại đây để biết các thuật ngữ được khi sử dụng autofac. Mình xin giải thích 1 cách "tương đối" 1 số thuật ngữ cơ bản để bạn có thể dễ dàng hình dung:

  1. Service: [là] 1 interface, trong đó expose các methods để "sử dụng"
public interface IFoo // autofac Service 
{
    int Add(int x, int y);
}
  1. Component: [là] 1 class/type kế thừa 1 interface (service) nào đó
public class Foo : IFoo // autofac component
{ }
  1. Dependencies: [là] 1 hoặc nhiều các service được chèn (inject) vào 1 phương thức của component
public class Foo : IFoo
{
    // IBar is a service
    // bar is a dependency
    public int DoSomething(IBar bar)
    {
        return 0;
    }
}
  1. Parameter: [là] 1 tham số của 1 hàm nhưng không phải là dependency
public class Foo : IFoo
{
    // bar is dependency, score is parameter
    public int DoSomething(IBar bar, int score)
    {
        return 0;
    }
}

setup

Ở đây mình sử dụng Asp.NET Mvc integration, cho nên mình sẽ cài package này luôn thông qua nuget

Install-Package Autofac.Mvc5

đăng ký

Hầu hết các IoC container đều cho phép đăng ký thông qua xml hoặc code behind. Mình thích sử dụng code behind hơn (vì có intellisence ^^). Trong Mvc bạn có thể đăng ký 1 lần duy nhất ở Application_Start

protected void Application_Start()
{
    IoCContainer.InitContainer();

    // other things
}

Đăng ký container:

// remember to using these namespaces
using Autofac;
using Autofac.Integration.Mvc;

public class IoCContainer
{
    public static void InitContainer()
    {
        var builder = new ContainerBuilder();

        // register assembly
        builder.RegisterControllers(typeof(your-namespace.MvcApplication).Assembly);

        // register model binder
        builder.RegisterModelBinders(Assembly.GetExecutingAssembly());
        builder.RegisterModelBinderProvider();

        // register web abstractions
        builder.RegisterModule<AutofacWebTypesModule>();

        // enable property injection in view pages
        builder.RegisterSource(new ViewRegistrationSource());

        // enable property injection into action filters
        builder.RegisterFilterProvider();

        // set the dependency resolver to to autofac
        var container = builder.Build();
        DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
    }
}

Bạn có thể lược bỏ các register optional đi nếu không sử dụng (minimum setup)

public class IoCContainer
{
    public static void InitContainer()
    {
        var builder = new ContainerBuilder();

        // register assembly
        builder.RegisterControllers(typeof(your-namespace.MvcApplication).Assembly);

        // set the dependency resolver to to autofac
        var container = builder.Build();
        DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
    }
}

Nếu bạn có sử dụng Repository

public class IoCContainer
{
    public static void InitContainer()
    {
        var builder = new ContainerBuilder();

        // register assembly
        builder.RegisterControllers(typeof(your-namespace.MvcApplication).Assembly);

        // register repositories
        builder.Register(t => new PostRepository(new YourDbContext())).As<IPostRepository>();

        // set the dependency resolver to to autofac
        var container = builder.Build();
        DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
    }
}

Nếu bạn có sử dụng RenericRepository (lưu ý thứ tự đăng ký, vì IPostRepository kế thừa từ IGenericRepository nên mình phải đăng ký IGenericRepository trước)

public class IoCContainer
{
    public static void InitContainer()
    {
        var builder = new ContainerBuilder();

        // register assembly
        builder.RegisterControllers(typeof(your-namespace.MvcApplication).Assembly);

        // register generic repository
        builder.RegisterGeneric(typeof(GenericRepository<,>))
            .As(typeof(IGenericRepository<,>))
            .WithParameter("db", new YourDbContext());

        // register repositories
        builder.Register(t => new PostRepository(new YourDbContext())).As<IPostRepository>();

        // set the dependency resolver to to autofac
        var container = builder.Build();
        DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
    }
}

Với interface IGenericRepository của mình như sau

public interface IGenericRepository<TEntity, TKey> : 
    IDisposable where TEntity : class

Và GenericRepository với constructor

public class GenericRepository<TEntity, TKey> : 
    IGenericRepository<TEntity, TKey> where TEntity : class
    {
        public GenericRepository(YourDbContext db)
        {
            // init
        }
    }

Một số chú ý:

  • Register dùng để đăng ký 1 type bất kỳ
  • RegisterGeneric dùng để đăng ký 1 generic type
  • với extension method .WithParameter(name, value), bạn có thể truyền tham số mặc định cho constructor

Xem thêm 1 số cách register khác tại: autofac registration document

instance scope

Instance scope hiểu đơn giản là phạm vi sử dụng của 1 instance sau khi được khởi tạo. Bạn có thể xem thêm trong document của autofac để biết thêm.

Với những service có sử dụng connection/transaction..., bạn nên kế thừa interface IDisposable để autofac disposal dispose object qua mỗi request/response (xem thêm để biết, sử dụng lâu sẽ hiểu :v)

Sử dụng

Giả sử mình có 1 service như sau:

public interface IPostRepository
{
    List<Post> GetAllPost();
}

Component implement service:

public class PostRepository : GenericRepository<Post, int>, IPostRepository
{
    public PostRepository(YourDbContext db) : base(db)
    { }

    public List<Post> GetAllPost()
    {
        return new List<Post>();
    }
}

Cuối cùng, trong Controller

public class HomeController : BaseController
{
    private IPostRepository _postRepo;

    public HomeController(IPostRepository postRepo) // inject
    {
        _postRepo = postRepo;
    }

    public ActionResult Index()
    {
        var posts = _postRepo.GetAllPost(); // using

        return View(model);
    }
}

Tất nhiên bạn vẫn có thể khai báo container hoặc sử dụng service locator như thường.

Kết

Vẫn còn khá nhiều tranh cãi (trên mạng) về việc sử dụng IoC, tuy nhiên lợi ích của nó thì không thể chối cãi. Bạn thấy đấy, code rất gọn gàng, modular..bla bla bla :))

Hy vọng qua bài viết này bạn có thêm 1 option cho mình khi lựa chọn 1 IoC container ưa thích cho mình. Good look ^^!

relate posts
by {{relate.UpdatedBy}} at 02/21/2018

{{relate.Title}}

  • {{relate.TotalComment}}
  • {{relate.View}}
  • {{relate.AveragePoint}}
[{{postCtrl.comments.length}}] comments
all comments {{ postCtrl.isHiddenComment ? 'show comments' : 'hide comments' }}
what do you want to say?

(*) markdown supported with html disabled