Thứ Tư, 16 tháng 1, 2013

The Art of Agile Development


Agile là gì?
Agile là một triết lí (philosophy) cho việc phát triển phần mềm. Nói cách khác, đó là một cách “tư duy” về các dự án phần mềm. Các triết lí của Agile được cụ thể hóa bởi một số phương pháp phát triển phần mềm (method), chẳng hạn như Extreme Programming (XP) hay Scrum, gọi tắt là các phương pháp Agile.
Mỗi phương pháp Agile bao gồm một tập hợp các quy tắc (pratice), chẳng hạn quy tắc về sử dụng công cụ quản lí mã nguồn, quy tắc về các chuẩn lập trình hay quy tắc trình diễn sản phẩm hàng tuần cho khách hàng.
Triết lí Agile được đưa ra trong một bản tuyên ngôn (manifesto) gồm 4 điểm và được làm rõ hơn bởi 12 quy tắc.
Vì sao chúng ta cần Agile?
Theo quan niệm truyền thống, một dự án phần mềm được coi là thành công khi sản phẩm được giao đúng hạn, trong ngân sách cho phép và làm đúng yêu cầu của khách hàng. Trên thực tế, nhiều dự án thỏa mãn tất cả các tiêu chí này nhưng rút cuộc vẫn bị coi là thất bại bởi phần mềm làm ra không được người dùng ưa thích, hoặc không mang lại nhiều lợi ích cho các cá nhân, tổ chức sử dụng chúng.
Ngoài các yếu tố truyền thống nói trên, một dự án phần mềm chỉ được coi là thành công khi thỏa mãn ba tiêu chí: Thành công ở mức cá nhân, thành công về mặt kĩ thuật và thành công ở mức công ty. Thành công ở mức cá nhân giúp kích thích các thành viên trong nhóm. Thành công về kĩ thuật đảm bảo khả năng bảo trì và tiến hóa của sản phẩm. Vị trí của nhóm sẽ được bảo đảm nhờ các thành công mà nhóm mang lại cho công ty. Các phương pháp Agile giúp cho dự án phần mềm đạt được ba thành công này.
Ba tiêu chí đánh giá thành công của một dự án phần mềm
Thành công ở mức công ty
Agile nhắm đến việc tạo ra giá trị cho công ty trong khi làm giảm chi phí. Đồng thời, Agile giúp sớm xác định các kì vọng đối với sản phẩm đang được phát triển. Nhờ đó, những dự án không mang lại giá trị như mong đợi sẽ được phát hiện sớm, tiết kiệm chi phí cho công ty.
Theo phương pháp Agile, các chuyên gia về nghiệp vụ (business) sẽ làm việc trực tiếp cùng với đội dự án. Các chức năng quan trọng nhất của sản phẩm được tập trung phát triển trước và được đưa vào vận hành sớm nhất có thể. Các phiên bản mới với các tính năng mới sẽ lần lượt được đưa thêm vào.
Agile giúp tăng cường khả năng giao tiếp giữa các thành viên trong nhóm. Chất lượng mã nguồn được cải tiến liên tục. Tiến độ dự án cũng được xem xét và đánh giá một cách thường xuyên.
Thành công về mặt kĩ thuật
Trong Extreme Programming, một phương pháp tuân theo triết lí Agile, các lập trình viên làm việc cùng nhau. Nhờ vậy, các chi tiết quan trọng sẽ không bị bỏ sót, mỗi đoạn code sẽ được kiểm tra bởi ít nhất hai người. Các lập trình viên liên tục tích hợp những đoạn code vừa viết vào hệ thống, cho phép một phiên bản mới của phần mềm được “ra lò” bất cứ khi nào nó góp thêm một giá trị đáng kể. Hơn nữa, toàn bộ đội dự án tập trung hoàn thành một chức năng trước khi chuyển sang chức năng tiếp theo. Bởi vậy, tiến độ công việc được kiểm soát tốt hơn và dự án có thể dễ dàng “chuyển hướng” khi có những thay đổi từ phía khách hàng.
Ngoài ra, Extreme Programming cũng đề xuất những quy tắc giúp tạo ra các thiết kế và các đọan mã tốt. Chẳng hạn, quy tắc “phát triển dựa trên kiểm thử” (test-driven development) trợ giúp lập trình viên viết các chương trình thực hiện đúng chức năng mong muốn.
Thành công về mặt cá nhân
Mỗi thành viên trong dự án Agile, dù ở bất kì cương vị nào, cũng đều cảm nhận được một cách rõ ràng sự thành công của bản thân.
Các lập trình viên nhận thấy trình độ kĩ thuật cũng như tầm ảnh hưởng của mình đối với dự án được nâng cao, chẳng hạn trong việc ước lượng và lập kế hoạch. Quyền tự chủ của đội dự án cũng được tăng cường.
Các tester nhận thấy họ có ảnh hưởng lớn đến chất lượng sản phẩm, đồng thời giảm được các công việc lặp lại một cách nhàm chán.
Nhà quản lí dự án hài lòng vì kiểm soát được tiến độ công việc, dự án thực hiện đúng các cam kết và làm thỏa mãn khách hàng.
Khách hàng, người sử dụng, các chuyên gia nghiệp vụ cảm thấy hài lòng vì điều kiển được hướng đi của dự án và các ý kiến được lắng nghe.
Các nhà lãnh đạo cao cấp sẽ cảm thấy hài lòng vì dự án mang lại lợi nhuận lớn cho công ty.
Bản tuyên ngôn 4 điểm cùng 12 quy tắc của Agile
Tuyên ngôn 4 điểm của Agile là:
  1. Cá nhân và các tương tác quan trọng hơn quy trình và công cụ.
  2. Tập trung làm cho phần mềm chạy được thay vì viết tài liệu.
  3. Cộng tác trực tiếp với khách hàng thay vì dựa trên hợp đồng.
  4. Phản ứng với các thay đổi thay vì tuân theo một kế hoạch định sẵn.
Bản tuyên ngôn được cụ thể hóa bằng 12 nguyên tắc sau:
  1. Ưu tiên cao nhất của dự án là thỏa mãn khách hàng bằng việc bàn giao sản phẩm sớm và liên tục.
  2. Hoan nghênh các thay đổi từ phía khách hàng, kể cả các thay đổi vào giai đoạn cuối.
  3. Bàn giao sản phẩm theo chu kì từ vài tuần đến vài tháng. Chu kì ngắn tốt hơn chu kì dài.
  4. Các nhân viên hiểu nghiệp vụ và các lập trình viên phải làm việc cùng nhau hàng ngày.
  5. Tổ chức dự án xoay quanh những cá nhân tích cực. Hỗ trợ và tin tưởng họ.
  6. Phương pháp giao tiếp tốt nhất trong đội dự án là gặp mặt trực tiếp.
  7. Các chức năng đã họat động là thước đo chính cho tiến độ dự án.
  8. Khuyến khích phát triển bền vững: Lập trình viên, người dùng, nhà quản lí…phải có khả năng tham gia dự án một cách liên tục.
  9. Liên tục cải tiến chất lượng thiết kế và mã nguồn.
  10. Tính đơn giản giữ vai trò c
    ốt yếu. Làm càng ít càng tốt.
  11. Những yêu cầu và thiết kế tốt nhất được nảy nở từ những nhóm làm việc tự chủ.
  12. Sau những khoảng thời gian nhất định, đội dự án xem xét cách thức cải tiến hiệu quả công việc.
Áp dụng Agile
Không phải dự án nào cũng nên áp dụng Agile. Nếu mục tiêu của bạn là tăng năng suất lao động, làm cho các lập trình viên làm việc nhanh hơn thì Agile có thể không phù hợp bởi các quy tắc của nó không nhằm tăng năng suất của đội dự án.Trước khi quyết định áp dụng Agile cho dự án của mình, bạn phải trả lời được câu hỏi: liệu Agile có giúp bạn thành công hơn hay không?.
Các dự án có đặc điểm sau đây có thể phù hợp với Agile:
  • Mức độ rủi ro thấp.
  • Thành viên nhóm có kinh nghiệm.
  • Yêu cầu thay đổi thường xuyên.
  • Kích thước nhóm nhỏ. Các thành viên làm việc cùng một địa điểm.
  • Văn hóa công ty ưa thích sự “không trật tự” (thrive on chaos).
Trái lại, những điều kiện sau đây là vật cản cho việc áp dụng Agile:
  • Kích thước nhóm lớn ( hơn 20 thành viên bao gồm lập trình viên, tester,…).
  • Các thành viên phân tán về mặt địa lí (ví dụ các dự án outsource).
  • Văn hóa làm việc theo mệnh lệnh.

Thứ Ba, 15 tháng 1, 2013

Áp dụng Dependency Injection với StructureMap vào ASP.NET MVC


Bài viết này nhằm giới thiệu về mẫu Dependency Injection và cách áp dụng chúng vào ASP.NET MVC bằng thư viện StructureMap.

1. Giới thiệu về Dependency Injection (DI)

Dependency Injection (gọi tắt là DI) là một mẫu thiết kế phần mềm cho phép chọn lựa thành phần phần mềm cần sử dụng tại thời điểm chạy ứng dụng chứ không phải tại thời điểm biên dịch. Và nhớ thế, nó có thể được dùng như là một cách để nạp các plugin một cách tự động, hoặc tự động thay đổi đối tượng cần sử dụng ở trong các môi trường khác nhau (ví dụ như môi trường test và môi trường thực tế).
DI là mẫu thiết kế bao gồm ít nhất 3 thành phần sau:
·         Một đối tượng/lớp có các lời gọi đến các thành phần chưa biết trước
·         Lời khai báo các thành phần mà lớp đó phụ thuộc, đó là các interface định nghĩa các phương thức mà lớp nói trên có thể sử dụng.
·         Một injector (đôi khi được gọi là provider hoặc container) giúp tạo ra các đối tượng của các lớp được cài đặt áp dụng theo các interface được yêu cầu nói trên.
Dưới đây là một hình vẽ mô tả mẫu thiết kế DI

Hình vẽ này minh hoạt một lớp A cần sử dụng một số phương thức được mô tả trong một interface là ISerrvice A, trong thời gian phát triển, ClassA hoàn toàn không có nhận thức về các kế thừa của IServiceA, nó chỉ gọi các phương thức của một đối tượng IServiceA. Khi chương trình khởi chạy, thì đối tượng Builder sẽ khởi tạo một đối tượng ClassA và gắn vào nó những đối tượng mà nó phụ thuộc, trong trường hợp đối tượng nó bị thuộc là IServiceA, Builder sẽ khởi tạo một đối tượng ServiceA và gắn vào cho đối tượng ClassA.
Một số câu hỏi bạn sẽ đặt ra, đó là, tại sao lại là đối tượng ServiceA mà không phải các đối tượng khác, tại vì sao Builder (hay Container) biết mà inject (gắn) vào…v.v Các thắc mắc này sẽ được làm rõ qua ví dụ và qua các project mà bạn sẽ tự làm trong tương lai.
Một số trường hợp bạn sẽ cần áp dụng DI
·         Nếu bạn muốn dự án có thể mở rộng một cách dễ dàng
·         Nếu bạn muốn dự án có thể hỗ trợ nhiều cấu hình triển khai khác nhau
·         Bạn  muốn áp dụng TDD (Test Driven Development) vào dự án và muốn ứng dụng được kiểm thử tự động trong đa số trường hợp
·         Nếu bạn muốn cô lập hóa một số hệ thống con khỏi ứng dụng và sau đó có thể có các giải pháp thay thế, tuy nhiên lại không làm xáo trộn toàn bộ hệ thống.
·         Bạn muốn áp dụng cơ chế plug-in cho các thành phần của ứng dụng

Bản thân người viết, đã sử dụng DI trong vòng hơn bốn năm qua, từ Castle Windsor đến StructureMap, và đến nay là Autofac hoặc MEF. Sử dụng DI có cái lợi là rất tốt cho việc áp dụng UnitTest và TDD, tuy nhiên, điểm bất lợi là khiến cho bạn sẽ mất nhiều thời gian hơn một chút khi bạn chưa quen, và có những vấn đề bạn sẽ gặp khó khăn khi áp dụng DI. Ví dụ đơn giản nhất là câu hỏi tôi cần cung cấp bao nhiêu đối tượng DbContext (khi dùng Entity Framework) cho ứng dụng, một đối tượng trên 1 request, một đối tượng cho toàn thời gian sống của chương trình ? Hay làm sao để áp dụng DI cho việc xây dựng các WCF/Web Services, hoặc làm sao để áp dụng vào việc sử dụng các WCF Services. Để trả lời được các câu hỏi kiểu như trên, bạn nên luyện tập sử dụng dần dần và áp dụng vào các dự án từ nhỏ đến lớn, đừng vội hăm hở áp dụng ngay vào các dự án chính của mình nếu bạn chưa có kinh nghiệm.
Một tiết lộ nho nhỏ, ba website cấu thành nên Jou Lập trình đều áp dụng Dependency Injection ngay từ đầu và chúng chạy khá tốt. Chúng sử dụng Autofac. Còn tại sao tôi giới thiệu StructureMap với các bạn, vì nó dễ sử dụng và sẽ là một khởi đầu suôn sẻ với DI nếu bạn là người mới bắt đầu.

2. StructureMap – Open source dependency injector for .NET

Có nhiều công cụ đóng vai trò của một Builder (Container/Injector), nổi tiếng nhất vẫn là Microsoft Unity, Castle Windsor, Structure Map, NInject và Autofac. Tuy nhiên, Microsoft Unity và Castle Windsor khá phức tạp trong việc cài đặt, các phương tiện như Structure Map và Ninject dễ sử dụng hơn.
Trước đây DI thường chỉ được áp dụng cho các dự án lớn dành cho doanh nghiệp, nhưng với StructureMap và sự đơn giản mà nó mang lại, bạn có thể áp dụng nó một cách dễ dàng. Tuy nhiên, hiện tại StructureMap không được cập nhật nhiều từ năm 2009, tuy nhiên vì nó đơn giản nhưng rất mạnh mẽ và dễ sử dụng mà người ta vẫn dùng StructureMap như một trong những lựa chọn hàng đầu khi muốn áp dụng Dependency Injection vào ứng dụng của mình. Đáng tiếc StructureMap không hỗ trợ cho các ứng dụng Silverlight/Windows Phone 7. Nếu bạn muốn áp dụng DI với các ứng dụng SL/WP7, bạn nên lựa chọn Ninject hoặc Autofac. Với tôi thì Autofac là sự thay thế tuyệt với đối với StrutureMap, nhưng Ninject có vẻ dễ sử dụng hơn.
StructureMap giúp việc áp dụng DI dễ dàng hơn nhiều so với trước đây. Khi bạn bắt đầu viết ứng dụng với tư duy là luôn áp dụng nguyên lý DI, thì mã của bạn sẽ dễ hiểu hơn, dễ dàng thay đổi hơn, ít lỗi hơn và đặc biệt là linh hoạt hơn. Thay vì đau đầu mỗi khi muốn thay đổi một thành phần trong ứng dụng vì sợ nó sẽ làm hỏng các thành phần khác, khi bạn sử dụng DI, bạn sẽ biến ứng dụng của bạn thành các thành phần độc lập, không lặp lại; các thành phần có khả năng gắn kết cao, và vì thế bạn có thể dễ dàng kết hợp chúng lại để tạo nên một ứng dụng tuyệt vời.
Để sử dụng StructureMap, bạn cần viếng thăm website của công cụ này (http://docs.structuremap.net ), hãy đọc và làm theo các ví dụ, khi bạn đã quen với StructureMap, bạn có thể sử dụng bất kỳ công cụ DI/IoC nào khác một cách dễ dàng.. ^^

3. Demo

Tôi sẽ sử dụng lại ví dụ của một bài viết về Linq-to-XML để biểu diễn cho bài viết này, tuy nhiên nó sẽ được thay đổi tên đôi chút và được thay đổi để phù hợp với một ứng dụng áp dụng DI. Bạn có thể ghé thăm bài viết Giới thiệu về Linq-to-XML và đọc sơ qua bài viết nếu bạn chưa biết về Linq-to-XML và muốn hiểu rõ hơn về logic của ứng dụng Demo về quản lý các ghi chú (Note).
Giả sử tôi đã sử dụng ứng dụng được tạo ở bài viết Giới thiệu về Linq-to-XML để quản lý các ghi chú, tuy nhiên, sau đó một thời gian, vì muốn sử dụng CSDL quan hệ để có thể cải tiến về sau, tôi cần phải thay đổi NoteRepository để có thể truy xuất dữ liệu trong SQL Server hoặc SQLCE. Nhưng tôi không muốn sửa code của XmlNoteRepository cũ vì nó vẫn tốt trong các trường hợp đơn giản, tôi muốn làm thêm một Repository mới, và muốn có sự tồn tại của hai phiên bản repository ở đâu đó trong ứng dụng, đặc biệt khi cần áp dụng Repository nào, tôi không cần phải đi thay đổi các lớp đang sử dụng chúng.

3.1. Định nghĩa giao diện INoteRepository

Việc đầu tiên, tôi sẽ định nghĩa một giao diện chung, một contract, để khi thiết lập các Repository cho Note đều phải tuân thủ theo, đó là INoteRepository với cài đặt như sau:


using System.Collections.Generic;
using DependencyInjectionEx.Models;
 
namespace DependencyInjectionEx.Repositories
{
    public interface INoteRepository
    {
        void Insert(Note note);
        IList GetAll();
        bool Delete(int id);
        bool Update(Note note);
        Note Get(int id);
    }
}
 

3.2. Điều chỉnh lại XmlNoteRepository

Điều chỉnh lại XmlNoteRepository để nó kế thừa từ INoteRepository:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Xml.Linq;
using DependencyInjectionEx.Models;
 
namespace DependencyInjectionEx.Repositories
{
    public class XmlNoteRepository : INoteRepository
    {
        private readonly string _filePath;
 
        public XmlNoteRepository()
        {
            _filePath = HttpContext.Current.Server.MapPath("~/App_Data/Data.xml");
        }
 
        public void Insert(Note note)
        {
            var doc = XDocument.Load(_filePath);
            try
            {
                var lastId = (from c in doc.Descendants("Note")
                              select (int)c.Element("Id")
                            ).OrderBy(x => x).Max();
 
                note.Id = lastId + 1;
 
            }
            catch (Exception)
            {
                note.Id = 1;
            }
 
            var noteNode =
                      new XElement("Note",
                      new XElement("Id", note.Id),
                      new XElement("Content", note.Content),
                      new XElement("Title", note.Title),
                      new XElement("CreateDate", note.CreateDate.ToShortDateString())
                  );
            doc.Element("Notes").Add(noteNode);
            doc.Save(_filePath);
            //doc.Descendants("Customer").Single(t => t.Descendants("CustomerID").Single().Value == "1")
 
            //    .Add(purchase);
        }
 
        public IList GetAll()
        {
            var doc = XDocument.Load(_filePath);
            try
            {
                var list = (from c in doc.Descendants("Note")
                            select new Note
                                       {
                                           Id = (int)c.Element("Id"),
                                           Title = (string)c.Element("Title"),
                                           Content = (string)c.Element("Content"),
                                           CreateDate = (DateTime)c.Element("CreateDate")
                                       }
                            ).OrderByDescending(x => x.Id).ToList();
                return list;
            }
            catch (Exception)
            {
                return new List();
            }
        }
 
        public bool Delete(int id)
        {
            try
            {
                var doc = XDocument.Load(_filePath);
                doc.Elements("Notes").Elements("Note").Where(x => x.Element("Id").Value == id.ToString()).Remove();
                doc.Save(_filePath);
                return true;
            }
            catch (Exception)
            {
                return false;
            }
 
        }
 
        public bool Update(Note note)
        {
            try
            {
                var doc = XDocument.Load(_filePath);
                var noteNode = doc.Elements("Notes").Elements("Note").Where(x => x.Element("Id").Value == note.Id.ToString()).Take(1);
                noteNode.Elements("Title").SingleOrDefault().Value = note.Title;
                noteNode.Elements("Content").SingleOrDefault().Value = note.Content;
                doc.Save(_filePath);
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }
 
        public Note Get(int id)
        {
            var doc = XDocument.Load(_filePath);
            try
            {
                var note = (from c in doc.Descendants("Note")
                            where c.Element("Id").Value == id.ToString()
                            select new Note
                            {
                                Id = (int)c.Element("Id"),
                                Title = (string)c.Element("Title"),
                                Content = (string)c.Element("Content"),
                                CreateDate = (DateTime)c.Element("CreateDate")
                            }
                            ).SingleOrDefault();
                return note;
            }
            catch (Exception)
            {
                return null;
            }
        }
    }
}
 

3.3. Điều chỉnh NoteController

Chúng ta không muốn NoteController biết nó đang sử dụng instance (thực thể) nào của INoteRepository, hay nói một cách khác, việc sử dụng lớp kế thừa nào của INoteRepository được quyết định tại thời điểm chạy ứng dụng (runtime) và độc lập với các lớp sử dụng chúng (như NoteController). Mã của NoteController sẽ được điều chỉnh để chỉ nhận biến cho constructor là một đối tượng kiểu INoteRepository:


using System;
using System.Web.Mvc;
using DependencyInjectionEx.Models;
using DependencyInjectionEx.Repositories;
 
namespace DependencyInjectionEx.Controllers
{
    public class NoteController : Controller
    {
        private readonly INoteRepository _noteRepository;
        //
        // GET: /Note/
        public NoteController(INoteRepository noteRepository)
        {
            _noteRepository = noteRepository;
        }
 
        public ActionResult Index()
        {
            var items = _noteRepository.GetAll();
            return View(items);
        }
 
        [HttpGet]
        public ActionResult Create()
        {
            return View();
        }
        [HttpPost]
        public ActionResult Create(Note note)
        {
            note.CreateDate = DateTime.Now;
            if (ModelState.IsValid)
            {
                _noteRepository.Insert(note);
                return RedirectToAction("Index");
            }
 
            return View(note);
        }
 
        public ActionResult Delete(int id)
        {
            var result = _noteRepository.Delete(id);
            return RedirectToAction("Index");
        }
 
        [HttpGet]
        public ActionResult Edit(int id)
        {
            var note = _noteRepository.Get(id);
            return View(note);
        }
 
        [HttpPost]
        public ActionResult Edit(Note note)
        {
            if (ModelState.IsValid)
            {
                _noteRepository.Update(note);
                return RedirectToAction("Index");
            }
            return View(note);
        }
    }
}
 
 Bạn hãy lưu ý những dòng in đậm, và có thể bạn sẽ thắc mắc, nếu mình chạy ứng dụng bây giờ thì chuyện gì sẽ xảy ra nhỉ? Câu chuyện xảy ra đó là ứng dụng MVC sẽ không biết xử lý thế nào với một NoteController không có cấu tử không có tham số (parameter less constructor), và nó càng không biết phải đối xử thế nào với tham số INoteRepository.
May thay, ASP.NET MVC có hỗ trợ Dependency Injection đó là DependencyResolver, bạn chỉ cần viết lớp kế thừa giao diện IDependencyResolver với các phương thức cần cài đặt là GetService (lấy dịch vụ tương ứng với kiểu dữ liệu là tham số đưa vào) và GetServices (lấy hết tất cả các dịch vụ là thực thể tương ứng với kiểu dữ liệu là tham số đưa vào). Nhưng chúng ta biết cài đặt lớp kế thừa từ IDenpendencyResolver như thế nào đây? Thắc mắc này sẽ được giải đáp sau, chúng ta sẽ cài đặt thêm một phiên bản của INoteRepository để dễ dàng thay đổi khi cần thiết.

3.4. Cài đặt thêm NoteRepository để truy xuất dữ liệu thông qua Entity Framework Code First

Để sử dụng EF Code First, chúng ta cần tạo ra lớp kế thừa từ lớp DbContext với tên gọi là NoteDbContext:


using System.Data.Entity;
using DependencyInjectionEx.Models;
 
namespace DependencyInjectionEx.Repositories
{
    public class NoteDbContext: DbContext
    {
        public DbSet  Notes { get; set; }
    }
}
 
 Tiếp theo chúng ta cài đặt cho NoteRepository kế thừa từ INoteRepository và sử dụng NoteDbContext:


using System.Collections.Generic;
using System.Linq;
using DependencyInjectionEx.Models;
 
namespace DependencyInjectionEx.Repositories
{
    public class NoteRepository: INoteRepository
    {
        private readonly NoteDbContext _db = new NoteDbContext();
 
        public void Insert(Note note)
        {
            _db.Notes.Add(note);
            _db.SaveChanges();
        }
 
        public IList GetAll()
        {
            return _db.Notes.ToList();
        }
 
        public bool Delete(int id)
        {
            var note = _db.Notes.SingleOrDefault(x => x.Id == id);
 
            if (note != null)
            {
                _db.Notes.Remove(note);
                _db.SaveChanges();
                return true;
            }
 
            return false;
        }
 
        public bool Update(Note note)
        {
            var updateNote = Get(note.Id);
 
            if (updateNote == null) return false;
 
            updateNote.Title = note.Title;
            updateNote.Content = note.Content;
            updateNote.CreateDate = note.CreateDate;
 
            _db.SaveChanges();
            return true;
        }
 
        public Note Get(int id)
        {
            var note = _db.Notes.SingleOrDefault(x => x.Id == id);
            return note;
        }
    }
}
 
 Để NoteRepository có thể kết nối được với Database, chúng ta cần định nghĩa một chuỗi kết nối và đặt nó trong tập tin Web.config với tên gọi là NoteDbContext. Với ví dụ này tôi sử dụng một CSDL SqlCE 4.0 và lưu nó ở thư mục App_Data, bạn không cần phải tạo nó từ trước, NoteDbContext khi chạy lần đầu tiên sẽ tạo ra nó. Điều chỉnh hoặc thêm vào node như sau:



  
    "NoteDbContext"
connectionString="Data Source=|DataDirectory|MyData.sdf;Persist Security Info=False;" providerName="System.Data.SqlServerCE.4.0" />

3.5 Sử dụng StructureMap để áp dụng DI vào ứng dụng

Lúc này ứng dụng của bạn vẫn chưa thể chạy được, khi truy xuất vào NoteController sẽ gây ra lỗi, và vì vậy chúng ta cần sự trợ giúp của các thư viện hỗ trợ DI và hỗ trợ DI cho ASP.NET MVC 3 như StructureMap, NInject, Unity.. Ở đây tôi chọn StructureMap.
Hãy sử dụng Nuget và cài đặt hai gói dưới đây:

Lúc đó thư viện StructureMap sẽ được tham chiếu trong dự án của bạn, và sẽ xuất hiện hai lớp IoC và SmDependencyResolver hỗ trợ DI tại thư mục DependencyResolution.
Lớp IoC đóng vai trò quyết định trong việc lựa chọn dịch vụ/lớp cho contract/interface. Giả sử chúng ta chọn XmlNoteRepository cho INoteRepository, chúng ta cần xác định chúng trong lớp IoC như sau:


using DependencyInjectionEx.Repositories;
using StructureMap;
 
namespace DependencyInjectionEx.DependencyResolution {
    public static class IoC {
        public static IContainer Initialize() {
            ObjectFactory.Initialize(x =>
                                         {
                                             x.Scan(scan =>
                                                        {
                                                            scan.TheCallingAssembly();
                                                            scan.WithDefaultConventions();
                                                        });
                                             x.For().HttpContextScoped().Use();
                                         });
            return ObjectFactory.Container;
        }
    }
}
 
 Thứ hai sẽ là lớp SmDependencyResolver:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using DependencyInjectionEx.Repositories;
using StructureMap;
 
namespace DependencyInjectionEx.DependencyResolution
{
    public class SmDependencyResolver : IDependencyResolver {
 
        private readonly IContainer _container;
 
        public SmDependencyResolver(IContainer container) {
            _container = container;
        }
 
        public object GetService(Type serviceType) {
            if (serviceType == null) return null;
            try {
                  return serviceType.IsAbstract || serviceType.IsInterface
                           ? _container.TryGetInstance(serviceType)
                           : _container.GetInstance(serviceType);
            }
            catch {
 
                return null;
            }
        }
 
        public IEnumerable<object> GetServices(Type serviceType) {
            return _container.GetAllInstances(serviceType).Cast<object>();
        }
    }
}
 
 Khi bạn muốn NoteRepository là sự lựa chọn mặc định, bạn hãy điều chỉnh dòng code trong IoC như dưới đây


x.For().HttpContextScoped().Use(); 
 Nhờ vào thư viện WebActivator.dll, hai lớp trên sẽ được sử dụng một cách tự động trong ứng dụng của bạn, và bạn hãy thử tải ví dụ về và hạy chạy thử, bạn sẽ thấy ngạc nhiên, kỳ lạ, và sẽ dần yêu thích DI.
Lúc này, bạn sẽ đặt ra một câu hỏi, tôi viết cả mấy chục dòng code chỉ để làm một việc đơn giản là khởi tạo đối tượng _noteRepository ngay trong constructor của NoteController. Có lợi ích gì ở đây?
Bạn hãy thử tưởng tượng, chúng ta có hàng chục lớp sử dụng INoteRepository và bạn phải đi thay thế hàng chục nơi mỗi khi bạn cần thay đổi loại CSDL, và mệt mỏi hơn nữa nếu có hàng chục các dịch vụ khác nhau có nhiều biến thể như vậy. Thay đổi một nơi duy nhất, hoặc thay đổi trong tập tin cấu hình mà không cần biên dịch lại sẽ là sự lựa chọn tốt hơn nhiều.

4. Kết luận

Trong bài viết này tôi đã chia sẻ cùng bạn về Dependency Injection một cách sơ lược và cách áp dụng thư viện DI là StructureMap.dll vào ứng dụng ASP.NET MVC 3. Chúc các bạn ứng dụng DI thành công!
GetServices(Type serviceType) { return _container.GetAllInstances(serviceType).Cast(); } } } Khi bạn muốn NoteRepository là sự lựa chọn mặc định, bạn hãy điều chỉnh dòng code trong IoC như dưới đây x.For().HttpContextScoped().Use(); Nhờ vào thư viện WebActivator.dll, hai lớp trên sẽ được sử dụng một cách tự động trong ứng dụng của bạn, và bạn hãy thử tải ví dụ về và hạy chạy thử, bạn sẽ thấy ngạc nhiên, kỳ lạ, và sẽ dần yêu thích DI. Lúc này, bạn sẽ đặt ra một câu hỏi, tôi viết cả mấy chục dòng code chỉ để làm một việc đơn giản là khởi tạo đối tượng _noteRepository ngay trong constructor của NoteController. Có lợi ích gì ở đây? Bạn hãy thử tưởng tượng, chúng ta có hàng chục lớp sử dụng INoteRepository và bạn phải đi thay thế hàng chục nơi mỗi khi bạn cần thay đổi loại CSDL, và mệt mỏi hơn nữa nếu có hàng chục các dịch vụ khác nhau có nhiều biến thể như vậy. Thay đổi một nơi duy nhất, hoặc thay đổi trong tập tin cấu hình mà không cần biên dịch lại sẽ là sự lựa chọn tốt hơn nhiều. 4. Kết luận Trong bài viết này tôi đã chia sẻ cùng bạn về Dependency Injection một cách sơ lược và cách áp dụng thư viện DI là StructureMap.dll vào ứng dụng ASP.NET MVC 3. Chúc các bạn ứng dụng DI thành công!