8

Giả sử tôi có một thực thể User và tôi muốn thiết lập thuộc tính CreationTime của nó trong constructor thành DateTime.Now. Nhưng là người dùng thử nghiệm đơn vị tôi không muốn truy cập DateTime.Now trực tiếp nhưng sử dụng ITimeProvider:Làm thế nào để sử dụng một container DI/IoC với chất kết dính mô hình trong ASP.NET MVC 2+?

public class User { 
    public User(ITimeProvider timeProvider) { 
     // ... 
     this.CreationTime = timeProvider.Now; 
    } 

    // ..... 
} 

public interface ITimeProvider { 
    public DateTime Now { get; } 
} 

public class TimeProvider : ITimeProvider { 
    public DateTime Now { get { return DateTime.Now; } } 
} 

Tôi đang sử dụng NInject 2 trong ứng dụng ASP.NET MVC 2.0 của mình. Tôi có một UserController và hai phương thức Create (một cho GET và một cho POST). Một cho GET là thẳng về phía trước nhưng một cho POST là không thẳng và không phải như vậy về phía trước: P bởi vì tôi cần phải mess với các chất kết dính mô hình để nói với nó để có được một tham chiếu của một thực hiện ITimeProvider để có thể xây dựng một cá thể người dùng.

public class UserController : Controller { 

    [HttpGet] 
    public ViewResult Create() { 
     return View(); 
    } 

    [HttpPost] 
    public ActionResult Create(User user) { 

     // ... 

    } 
} 

Tôi cũng muốn có thể giữ tất cả các tính năng của trình kết nối mô hình mặc định.

Bất kỳ cơ hội nào để giải quyết điều này đơn giản/thanh lịch/etc? : D

+0

liên quan: Ninject thread cụ thể http://stackoverflow.com/questions/9186560/ninject-inject-a-dependency-into-a-custom-model-binder-and-using-inrequestscope –

Trả lời

5

Làm thế nào về thay vì sử dụng một ITimeProvider thử điều này:

public class User 
{ 
    public Func<DateTime> DateTimeProvider =() => DateTime.Now; 

    public User() 
    { 
     this.CreationTime = DateTimeProvider(); 
    } 
} 

Và trong thử nghiệm đơn vị của bạn:

var user = new User(); 
user.DateTimeProvider =() => new DateTime(2010, 5, 24); 

Tôi biết rằng đây không phải là rất tao nhã nhưng thay vì can thiệp vào mô hình chất kết dính này có thể là một giải pháp. Nếu điều này không giống như một giải pháp tốt, bạn có thể thực hiện một mô hình tùy chỉnh chất kết dính và ghi đè lên phương pháp CreateModel nơi bạn sẽ tiêm các phụ thuộc trong constructor của mô hình.

+0

Vâng, điều này có thể hiệu quả. Một biến thể của bạn có thể là một hàm tạo mặc định như sau: User(): this (TimeProvider.Instance) {} Tôi vẫn hy vọng một cách đơn giản để tiêm một [cái gì đó] giữa trình kết nối DefaultModel và ASP.NET MVC 2 Khung .. –

+2

Sau đó, ghi đè 'DefaultModelBinder' là tuyến đường bạn sẽ phải thực hiện. –

1

Một tùy chọn khác là tạo một lớp khác để đại diện cho người dùng chưa được duy trì mà không có thuộc tính ngày tạo.

Thậm chí nếu CreationDate là một trong những bất biến của User, nó có thể được vô hiệu hóa trong mô hình khung nhìn của bạn - và bạn có thể đặt nó ở xa hơn trong bộ điều khiển hoặc lớp miền của bạn. Sau khi tất cả, nó có thể không quan trọng, nhưng nên các thuộc tính ngày tạo ra thực sự đại diện cho thời điểm bạn xây dựng một trường hợp người dùng, hoặc nó sẽ là thích hợp hơn cho nó để đại diện cho thời điểm người dùng gửi dữ liệu của họ? Không.

20

Một vài quan sát:

Đừng tiêm phụ thuộc chỉ để truy vấn họ trong constructor

Không có lý do gì để tiêm một ITimeProvider thành một người sử dụng chỉ để gọi Now ngay lập tức. Chỉ cần tiêm thời gian sáng tạo trực tiếp thay vì:

public User(DateTime creationTime) 
{ 
    this.CreationTime = creationTime; 
} 

Một nguyên tắc thực sự tốt của ngón tay cái liên quan đến DI là nhà xây dựng nên thực hiện không có logic.

Không sử dụng DI với ModelBinders

Một ASP.NET MVC ModelBinder là một nơi thực sự nghèo để làm DI, đặc biệt là bởi vì bạn không thể sử dụng Constructor Injection. Lựa chọn duy nhất còn lại là static Service Locator anti-pattern.

ModelBinder chuyển thông tin HTTP GET và POST thành đối tượng được nhập mạnh, nhưng khái niệm các loại này không phải là đối tượng miền, nhưng tương tự như Data Transfer Objects.

Một giải pháp tốt hơn cho ASP.NET MVC là để bỏ ModelBinders tùy chỉnh hoàn toàn và thay vào đó ôm hôn một cách rõ ràng rằng những gì bạn nhận được từ các kết nối HTTP là không của bạn đầy đủ đối tượng miền.

Bạn có thể có một tra cứu đơn giản hoặc mapper để lấy đối tượng tên miền của bạn trong điều khiển của bạn:

public ActionResult Create(UserPostModel userPost) 
{ 
    User u = this.userRepository.Lookup(userPost); 
    // ... 
} 

nơi this.userRepository là một sự phụ thuộc tiêm.

+0

"Một giải pháp tốt hơn cho ASP.NET MVC là loại bỏ hoàn toàn ModelBinders tùy chỉnh và thay vào đó một cách rõ ràng chấp nhận rằng những gì bạn nhận được từ kết nối HTTP không phải là đối tượng miền đầy đủ của bạn." Bây giờ đó là một suy nghĩ thú vị. Điều gì sẽ xảy ra nếu chúng ta có nhiều thực thể với các thuộc tính chung như ngày tạo trong ví dụ trên và chúng ta muốn đặt tất cả chúng trong một chất kết dính cho tất cả các thực thể của chúng ta (chúng ta có tất cả các thực thể kế thừa một lớp thực thể cơ sở và đây là nơi mà trình kết nối được đính kèm) ? Trong trường hợp đó bán phá giá các chất kết dính sẽ dẫn đến sao chép mã trong mỗi Tạo trên mỗi bộ điều khiển ?? – mare

+2

Vấn đề là nó không quan trọng. Bạn không nhận được một thực thể, bạn nhận được một HTTP POST (hoặc GET). Chấp nhận giao thức. Có nhiều cách khác để tránh trùng lặp mã. –

+2

Như asp.net mvc 3 có hỗ trợ cho imodelbinderprovider nó cho thấy họ đã suy nghĩ dọc theo dòng chính xác, do đó tôi không đồng ý với ý kiến ​​của bạn chưa hiểu bạn đang đến từ đâu. Một lựa chọn khác có thể là tạo một bộ điều khiển cơ sở xử lý logic lặp đi lặp lại ... – Haroon

Các vấn đề liên quan