12/08/2018, 13:15

Repository Pattern và Unit of Work với Entity Framework trong ASP.NET MVC

Repository là một mẫu dùng để tạo ra một lớp abstraction trung gian giữa lớp data và lớp business. Lớp này chứa đựng phương thức thao tác mà để giao tiếp với lớp data để phục vụ cho business từ lớp logic . Mục đích tạo ra lớp này để cách ly với việc tiếp cận data sao cho những thay đổi không ảnh ...

Repository là một mẫu dùng để tạo ra một lớp abstraction trung gian giữa lớp data và lớp business. Lớp này chứa đựng phương thức thao tác mà để giao tiếp với lớp data để phục vụ cho business từ lớp logic . Mục đích tạo ra lớp này để cách ly với việc tiếp cận data sao cho những thay đổi không ảnh hưởng trực tiếp đến lớp logic business.

Ví dụ : Chương trình POS được triển khai tới những cửa hàng khác nhau. Một vài khách hàng muốn dùng Microsoft SQL Server và một số khác muốn Oracle hoặc những database khác. Nhiệm vụ của Repository là phân tách các lớp và thêm một lớp tiếp cận data mới

Unit of work là một mẫu xử lý transaction trong khi thao tác dữ liệu sử dụng mẫu Repository.

alt

I / ORM và EF là gì

1.ORM : Object-relational mapping là một kỹ thuật lập trình để chuyển đổi dữ liệu giữa những loại không tương thích đến ngôn ngữ lập trình hướng đối tượng. Chúng ta có thể xử lý objects/models như những đối tượng trong database. Chúng ta có thể viết code/query trên model đối tượng để lấy dữ liệu.

II/ Advantages of ORM

  • Thao tác dữ liệu (CRUD) được xử lý mà không viết SQL queries
  • Map dữ liệu giữa result set và entity là chức năng chính của ORM
  • Sử Dụng UoW/Context để xử lý transaction trong suốt quá trình thao tác dữ liệu.
  • Viết model trong một chỗ riêng cho việc quản lý model trong database.
  • Nó phù hợp với cách code tự nhiên với ngôn ngữ của chương trình.
  • Lập trình có thể lập trình hướng đối tượng hoàn toàn khi thực hiện bất kỳ hành động nào với lớp business và data.

III/ Entity framework

Map với các đối tượng database. Framework này giúp viết POCO và tạo ra database và nó cũng có thể tạo ra model từ database tồn tại.

Bạn có thể viết những lớp repository khác nhau cho mỗi entity. Nhưng đối với hành động của database một vài phương thức chung sẽ được sử dụng cho hành động CRUD. Nên việc sử dụng nhưng repository khác nhau sẽ tạo ra code dư thừa. Đó là tại sao nên dùng một repository chung.

IV/ Thực hiện

1/ Tạo chương trình ASP.NET MVC web application

alt

2/ Sau đó thêm một project khác cho việc tiếp cận dữ liệu tên là Contact.Repository.

alt

3/ Tạo database cho chương trình.

alt

4/ Quay lại code và thêm ADO.NET Entity Data Model vào Contact.Repository và cập nhật database

alt

Edmx trông như sau:

alt

Quan sát các đối tượng trên mỗi đối tượng đều được tạo ra với các đối tượng liên quan mà được gọi là các thuộc tính Navigation. Bây giờ thêm một tham chiếu đến Contact.Repository cho web project là bạn có thể bắt đầu phát triển. Nhưng ý định là tạo một lớp Repository qua model entity để tiếp cận database từ project web hoặc từ lớp business.Ví dụ này không có lớp business nào nhưng là một thực hành tốt để chia ra từ lớp business.

a/ Thêm lớp dưới đây cho Contact.Repository.

alt

b/ The Base Repository class thực hiện interface và định nghĩa các phương thức thao tác dữ liệu.

public class BaseRepository<T> : IBaseRepository<T>
        where T : class
{
    private readonly IUnitOfWork _unitOfWork;
    internal DbSet<T> dbSet;
    public BaseRepository(IUnitOfWork unitOfWork)
    {
        if (unitOfWork == null) throw new ArgumentNullException("unitOfWork");
        _unitOfWork = unitOfWork;
        this.dbSet = _unitOfWork.Db.Set<T>();
    }

    /// <summary>
    /// Returns the object with the primary key specifies or throws
    /// </summary>
    /// <typeparam name="TU">The type to map the result to</typeparam>
    /// <param name="primaryKey">The primary key</param>
    /// <returns>The result mapped to the specified type</returns>
    public T Single(object primaryKey)
    {
        var dbResult = dbSet.Find(primaryKey);
        return dbResult;
    }

    public T SingleOrDefault(object primaryKey)
    {
        var dbResult = dbSet.Find(primaryKey);
        return dbResult;
    }

    public bool Exists(object primaryKey)
    {
        return dbSet.Find(primaryKey) == null ? false : true;
    }

    public virtual int Insert(T entity)
    {
       dynamic obj= dbSet.Add(entity);
       this._unitOfWork.Db.SaveChanges();
       return obj.Id;
    }

    public virtual void Update(T entity)
    {
        dbSet.Attach(entity);
        _unitOfWork.Db.Entry(entity).State = EntityState.Modified;
        this._unitOfWork.Db.SaveChanges();
    }

    public int Delete(T entity)
    {
        if (_unitOfWork.Db.Entry(entity).State == EntityState.Detached)
        {
            dbSet.Attach(entity);
        }
        dynamic obj=dbSet.Remove(entity);
        this._unitOfWork.Db.SaveChanges();
        return obj.Id;
    }

    public IUnitOfWork UnitOfWork { get { return _unitOfWork; } }
    internal DbContext Database { get { return _unitOfWork.Db; } }

    public Dictionary<string, string> GetAuditNames(dynamic dynamicObject)
    {
        throw new NotImplementedException();
    }

    public IEnumerable<T> GetAll()
    {
        return dbSet.AsEnumerable().ToList();
    }
    }
}

Trong lớp này IUnitOfWork interface là sử dụng để duy trì transaction trong khi thêm mới hoặc cập nhật dữ liệu.

IUnitOfWork chứa 2 phương thức, Commit() và StartTransaction(), mà sẽ thực hiện bởi lớp UnitOfWork. Lớp này chịu trách nhiệm cho cung cấp DbContext hoặc Transaction để bắt đầu và thực hiện transaction.

Dùng lớp BaseRepository<T> bạn sẽ làm việc với Contact, Address model.Trong EF, bạn có các model tương đương với các model là các bảng trong database.

Bây giờ bạn sẽ tạo ra 2 lớp repository mà thừa kế lớp BaseRepository. Minh dụ của lớp repository này sẽ giao tiếp giữa lớp database và lớp business.

a/ ContactRepository

public class ContactRepository: BaseRepository<Contact>
{
   public ContactRepository(IUnitOfWork unit):base(unit)
    {
    }
}

b/ AddressRepository

public class AddressRepository : BaseRepository<Address>
{
  public AddressRepository(IUnitOfWork unit)
   : base(unit)
   {
   }
}

c/ Lớp controller để gọi lớp repository theo cách sau:

```Language

public class ContactController : Controller
{

    private IUnitOfWork uow = null;
    private ContactRepository repo = null;
    public ContactController()
    {

        uow = new UnitOfWork();
        repo = new ContactRepository(uow);
    }
    //
    // GET: /Default1/

    public ActionResult Index()
    {
        var contacts = repo.GetAll();
        return View(contacts.ToList());
    }

    //
    // GET: /Default1/Details/5

    public ActionResult Details(int id = 0)
    {
        Contact contact = repo.SingleOrDefault(id);
        if (contact == null)
        {
            return HttpNotFound();
        }
        return View(contact);
    }

    //
    // GET: /Default1/Create

    public ActionResult Create()
    {
        return View();
    }

    //
    // POST: /Default1/Create

    [HttpPost]
    public ActionResult Create(Contact contact)
    {
        if (ModelState.IsValid)
        {
            repo.Insert(contact);
            return RedirectToAction("Index");
        }

        return View(contact);
    }

    //
    // GET: /Default1/Edit/5

        public ActionResult Edit(int id = 0)
    {
        Contact contact = repo.SingleOrDefault(id);
        if (contact == null)
        {
            return HttpNotFound();
        }
        return View(contact);
    }

    //
    // POST: /Default1/Edit/5

    [HttpPost]
    public ActionResult Edit(Contact contact)
    {
        if (ModelState.IsValid)
        {
            repo.Update(contact);
            return RedirectToAction("Index");
        }
        return View(contact);
    }

    //
    // GET: /Default1/Delete/5

    public ActionResult Delete(int id = 0)
    {
        Contact contact = repo.SingleOrDefault(id);
        if (contact == null)
        {
            return HttpNotFound();
        }
        return View(contact);
    }

    //
    // POST: /Default1/Delete/5

    [HttpPost, ActionName("Delete")]
    public ActionResult DeleteConfirmed(int id)
    {
        Contact contact = repo.SingleOrDefault(id);

        repo.Delete(contact);
        return RedirectToAction("Index");
    }

    protected override void Dispose(bool disposing)
    {
        uow.Dispose();
        base.Dispose(disposing);
    }
}

V/ Kết Luận

Sử dụng mẫu Repository để chia lớp data từ lớp logical. Nếu kiến trúc chương trình muốn thay đổi hệ thống lưu nó thì không phải lo lắng về business logic. Qua việc thực hiện mâux Repository là một tiếp cận tốt để xây dựng chương trình của bạn tuy có chậm hơn chút so với việc sử dụng trực tiếp đến data.

0