2010-03-13 26 views
6

Với một số vui lòng giúp đỡ từ StackOverflow, tôi đã có Unity Framework để tạo ra phụ thuộc xích của tôi, trong đó có một khung Entity DataContext đối tượng:khuôn khổ Unity - tạo & xử lý Entity Framework DataContexts tại thời điểm thích hợp

using (IUnityContainer container = new UnityContainer()) 
{ 
    container.RegisterType<IMeterView, Meter>(); 
    container.RegisterType<IUnitOfWork, CommunergySQLiteEntities>(new ContainerControlledLifetimeManager()); 
    container.RegisterType<IRepositoryFactory, SQLiteRepositoryFactory>(); 
    container.RegisterType<IRepositoryFactory, WCFRepositoryFactory>("Uploader"); 
    container.Configure<InjectedMembers>() 
     .ConfigureInjectionFor<CommunergySQLiteEntities>(
      new InjectionConstructor(connectionString)); 

    MeterPresenter meterPresenter = container.Resolve<MeterPresenter>(); 

này hoạt động thực sự tốt trong việc tạo đối tượng Trình bày của tôi và hiển thị chế độ xem có liên quan, tôi thực sự hài lòng.

Tuy nhiên, vấn đề tôi đang gặp phải hiện nay là trong quá trình tạo và xử lý đối tượng Entity Framework (và tôi nghi ngờ điều này sẽ xảy ra với bất kỳ đối tượng IDisposable nào). Sử dụng Unity như thế này, các đối tượng SQL EF "CommunergySQLiteEntities" được tạo ra ngay lập tức, như tôi đã thêm giao diện của nó, IUnitOfWork đến constructor của MeterPresenter

public MeterPresenter(IMeterView view, IUnitOfWork unitOfWork, IRepositoryFactory cacheRepository) 
    { 
     this.mView = view; 
     this.unitOfWork = unitOfWork; 
     this.cacheRepository = cacheRepository; 
     this.Initialize(); 
    } 

tôi cảm thấy một chút khó chịu về điều này vào thời điểm đó, vì tôi không muốn giữ một kết nối cơ sở dữ liệu mở, nhưng tôi không thể nhìn thấy bất kỳ cách nào khác bằng cách sử dụng tiêm phụ thuộc Unity. Chắc chắn, khi tôi thực sự cố gắng sử dụng các DataContext, tôi nhận được lỗi này:

((System.Data.Objects.ObjectContext)(unitOfWork)).Connection 
    '((System.Data.Objects.ObjectContext)(unitOfWork)).Connection' 
threw an exception of type 'System.ObjectDisposedException' 
System.Data.Common.DbConnection {System.ObjectDisposedException} 

sự hiểu biết của tôi về các nguyên tắc của IoC là bạn thiết lập tất cả các phụ thuộc của bạn ở phía trên, giải quyết đối tượng của bạn và lập tức bạn đi . Tuy nhiên, trong trường hợp này, một số đối tượng con, ví dụ datacontext, không cần khởi tạo tại thời điểm đối tượng Presenter cha được tạo (như bạn sẽ truyền chúng trong hàm tạo), nhưng Trình bày không cần biết về loại sử dụng cho IUnitOfWork khi nó muốn nói chuyện với cơ sở dữ liệu.

Lý tưởng nhất, tôi muốn một cái gì đó như thế này bên trong trình bày giải quyết của tôi:

using(IUnitOfWork unitOfWork = new NewInstanceInjectedUnitOfWorkType()) 
{ 
    //do unitOfWork stuff 
} 

Vì vậy, trình bày hiểu biết những gì IUnitOfWork thực hiện sử dụng để tạo và xử lý ngay lập tức, tốt nhất từ ​​cuộc gọi RegisterType gốc. Tôi có phải đặt một thùng chứa Unity khác bên trong Người trình bày của tôi, với nguy cơ tạo ra sự phụ thuộc mới không?

Điều này có thể thực sự rõ ràng đối với một guru IoC, nhưng tôi thực sự đánh giá cao một con trỏ đi đúng hướng.

Trả lời

4

Bạn không cần phải lo lắng về việc khởi tạo một ObjectContext cùng lúc đó người dẫn chương trình được tạo ra - một ObjectContext không giữ mở một kết nối cơ sở dữ liệu. Thay vào đó, nó sẽ mở ra các kết nối khi cần thiết, khi nó thực sự cần nói chuyện với cơ sở dữ liệu, tức là khi bạn thực hiện một truy vấn hoặc cam kết thay đổi, và đóng kết nối lại ngay sau khi nó kết thúc. Nó sẽ chỉ giữ kết nối mở nếu bạn mở nó một cách rõ ràng, đó là một điều hiếm có khi làm với Entity Framework.

Nếu bạn đang nhận được ObjectDisposedException bằng cách sử dụng ContainerControlledLifetimeManager thì điều đó có nghĩa là vùng chứa của bạn đang bị xử lý trước người trình bày, đây là lỗi thiết kế.Nó không hoàn toàn rõ ràng môi trường của bạn là gì (ASP.NET? Winforms?), Nhưng ContainerControlledLifetimeManager có lẽ không thích hợp ở đây, vì nó hoạt động như một cá thể Singleton. Thông thường, bạn sẽ thực sự muốn tạo một cá thể ngữ cảnh mới khi bạn giải quyết kiểu - có nhiều vấn đề bạn có thể và sẽ chạy vào nếu bạn sử dụng một singleton thay thế.

Vì vậy - tôi sẽ loại bỏ số ContainerControlledLifetimeManager tại đây và cũng đảm bảo rằng vùng chứa của bạn không bị xử lý quá sớm; thực tế là nó nằm trong khối using cho biết đây có thể là nguyên nhân của số ObjectDisposedException của bạn. (Bạn vẫn cần phải vứt bỏ các container cuối cùng tất nhiên - nó chỉ là bạn đang có thể làm một cái gì đó giống như tạo ra một hình thức vô dụng, mà vẫn còn sống lâu sau khi kiểm soát lá phạm vi using).

+0

Xin chào Aaron - bạn và Michael đều đúng, đó là một trình quản lý ContainerControlledLifetimeManager nhỏ bé mà tôi đã treo xung quanh từ một phiên bản trước. Tôi đã gỡ bỏ nó, quay trở lại để có IUnitOfWork trong constructor Presenter, và nó có vẻ đủ hạnh phúc. cảm ơn tất cả mọi người, rất giáo dục ... – TobyEvans

+0

và lý do tôi đã có nó ở nơi đầu tiên là câu hỏi này: http://stackoverflow.com/questions/2412563/unity-framework-reusing-instance Tuy nhiên, tôi 've hiện nay đã thay đổi thiết kế của tôi sử dụng một nhà máy để tạo ra Repositories của tôi, và phương pháp nhà máy lấy IUnitOfWork như tham số: var localCache = cacheRepository.CreateRealtimeRepository (unitOfWork) vì vậy tôi không cần phải có ContainerControlledLifetimeManager Như tôi đã nói, rất giáo dục ... – TobyEvans

2

Tại sao bạn không chỉ xóa IUnitOfWork khỏi hàm tạo và thay vào đó hãy tiêm vùng chứa thống nhất? Vì vậy, bạn sẽ có flexiblity để gọi container.Resolve<IUnitOfWork>() bất cứ nơi nào trong mã của bạn khi thích hợp.

Ví dụ:

using(IUnitOfWork unitOfWork = container.Resolve<IUnitOfWork>()) 
{ 
    //do unitOfWork stuff 
} 

Đừng quên để thiết lập thời gian tồn tại của các ví dụ để duy nhất.

Michael

+1

Icky. Các thành phần không nên phụ thuộc vào vùng chứa, chúng phải phụ thuộc vào phụ thuộc thực tế của chúng. – Aaronaught

+0

đôi khi nó rất hữu ích để tiêm các container, bạn vẫn có decoupling;) –

+0

Trong thực tế, một số dự án mà tôi đã làm việc trên chúng tôi đã có một tài sản tĩnh đại diện cho IUnityContainer rằng nó có thể được tiêu thụ ngay cả khi không tiêm. –

0

Tôi biết câu hỏi này là cũ, nhưng tôi vẫn muốn cung cấp cho tôi 2 xu ở đây.

Bạn có thể đăng ký đơn giản trừu tượng UnitOfWork của mình, nhưng yêu cầu Func<IUnitOfWork> trong lớp của bạn thay vì một phiên bản. Đây là một tính năng gọn gàng của Unity thực sự, có thể giải quyết các đại biểu tạo đối tượng của bạn thay vì nhận được đối tượng ngay lập tức.

Bằng cách này, bạn có thể làm như bạn muốn, tức là kiểm soát phạm vi của đơn vị công việc bên trong phương pháp của bạn.

Nói tóm lại:

container.RegisterType<IUnitOfWork, CommunergySQLiteEntities>(); 

... 

public MeterPresenter(IMeterView view, Func<IUnitOfWork> unitOfWorkFactory, IRepositoryFactory cacheRepository) 
{ 
    this.mView = view; 
    this.unitOfWorkFactory = unitOfWorkFactory; 
    this.cacheRepository = cacheRepository; 
    this.Initialize(); 
} 

... 

using(IUnitOfWork unitOfWork = unitOfWorkFactory()) 
{ 
    //do unitOfWork stuff 
} 

Tôi đã sử dụng điều này một vài lần rồi và Cá nhân tôi khuyên bạn nên nó, vì bạn vẫn có toàn quyền kiểm soát tất cả mọi thứ, bao gồm cả chế giễu cho kiểm tra đơn vị, trong khi vẫn không ghép mã của bạn với bất kỳ điều gì ngoài phụ thuộc của nó.

Bạn có thể tạo giao diện IUnitOfWorkFactory và chèn thay vào đó nếu bạn cần logic phức tạp hơn, nhưng số lượng đại biểu Func<T> đủ trong hầu hết các trường hợp.

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