2010-12-14 11 views
8

Tôi nghĩ tôi sẽ nhận được câu hỏi này ngoài kia trong khi tôi tự giải quyết một giải pháp.Quản lý nhiều cơ sở dữ liệu với NHibernate và Autofac

Sau khi đã xây dựng phần lớn ứng dụng, tôi có yêu cầu phút cuối để hỗ trợ đọc/ghi vào cơ sở dữ liệu bổ sung (tổng cộng 2, không biết người khác). Tôi đã xây dựng ứng dụng bằng NHibernate, với Autofac cung cấp các thành phần DI/IoC. FWIW, điều này nằm trong một ứng dụng ASP.NET MVC 2.

Tôi có một lớp lưu trữ chung có phiên NHibernate. Về mặt lý thuyết, tôi có thể tiếp tục sử dụng kho lưu trữ chung này (IRepository<>) cho cơ sở dữ liệu thứ hai miễn là phiên được truyền cho nó được sinh ra từ một SessionFactory thích hợp, đúng không?

Vâng, khi ứng dụng bắt đầu, Autofac sẽ làm việc đó. Liên quan đến phiên và SessionFactory, tôi có một mô-đun cho biết: Thao

builder.Register(c => c.Resolve<ISessionFactory>().OpenSession()) 
    .InstancePerMatchingLifetimeScope(WebLifetime.Request) 
    .OnActivated(e => 
    { 
     e.Context.Resolve<TransactionManager>().CurrentTransaction = ((ISession)e.Instance).BeginTransaction(); 
    }); 

builder.Register(c => ConfigureNHibernate()) 
    .SingleInstance(); 

nơi ConfigureNHibernate(), trả về SessionFactory cơ sở, trông giống như:

private ISessionFactory ConfigureNHibernate() 
{ 
    Configuration cfg = new Configuration().Configure(); 
    cfg.AddAssembly(typeof(Entity).Assembly); 
    return cfg.Configure().BuildSessionFactory(); 
} 

Hiện nay, đây là giới hạn chỉ một cơ sở dữ liệu. Trong bất kỳ kịch bản NHIB nào khác, tôi có khả năng sẽ chuyển các phiên bản của SessionFactories riêng biệt thành một băm và truy xuất chúng khi cần thiết. Tôi không muốn phải thiết kế lại toàn bộ công việc vì chúng tôi khá gần với bản phát hành chính. Vì vậy, tôi đoán tôi cần phải sửa đổi ít nhất các phương pháp trên để tôi có thể cấu hình độc lập hai SessionFactories. Vùng màu xám của tôi là cách tôi sẽ đi về việc chỉ định Nhà máy chính xác được sử dụng với một kho lưu trữ cụ thể (hoặc ít nhất là đối với các thực thể cụ thể cho cơ sở dữ liệu thứ hai đó).

Bất kỳ ai có kinh nghiệm với tình huống này trong khi sử dụng vùng chứa IoC và NHibernate theo cách này?

EDIT Tôi đã stubbed ra một phương pháp GetSessionFactory mà phải mất một đường dẫn tập tin cấu hình, kiểm tra sự tồn tại của một SessionFactory khớp trong HttpRuntime.Cache, tạo ra một trường hợp mới nếu ta không tồn tại, và trả về SessionFactory đó. Bây giờ tôi vẫn cần phải búa ra làm thế nào để nói với Autofac như thế nào và khi nào để xác định một đường dẫn cấu hình thích hợp. Phương pháp mới trông giống như (mượn rất nhiều từ bài 2006 của Billy here):

private ISessionFactory GetSessionFactory(string sessionFactoryConfigPath) 
    { 
     Configuration cfg = null; 
     var sessionFactory = (ISessionFactory)HttpRuntime.Cache.Get(sessionFactoryConfigPath); 

     if (sessionFactory == null) 
     { 
      if (!File.Exists(sessionFactoryConfigPath)) 
       throw new FileNotFoundException("The nhibernate configuration file at '" + sessionFactoryConfigPath + "' could not be found."); 

      cfg = new Configuration().Configure(sessionFactoryConfigPath); 
      sessionFactory = cfg.BuildSessionFactory(); 

      if (sessionFactory == null) 
      { 
       throw new Exception("cfg.BuildSessionFactory() returned null."); 
      } 

      HttpRuntime.Cache.Add(sessionFactoryConfigPath, sessionFactory, null, DateTime.Now.AddDays(7), TimeSpan.Zero, System.Web.Caching.CacheItemPriority.High, null); 
     } 

     return sessionFactory; 
    } 
+1

Việc lưu trữ nhà máy trong bộ nhớ cache là một ý tưởng tồi. Nó không phải là thứ có thể biến mất và được tái tạo. –

+0

Tôi biết. Trong thực tế, tôi sẽ phải quản lý tuổi thọ của đối tượng được lưu trữ đó. Ví dụ của Billy thực sự đã tiến thêm một bước nữa và tạo ra một lớp SessionManager có phạm vi singleton để xử lý các vấn đề về bộ nhớ đệm. Tôi đã cố gắng để câm nó xuống vì vậy tôi cuối cùng có thể cho phép Autofac quản lý nó thông qua cơ chế phạm vi riêng của nó (rất thanh lịch). – nkirkes

Trả lời

11

Tôi giả định rằng bạn muốn loại khác nhau của các tổ chức để đi vào từng cơ sở dữ liệu; nếu bạn muốn giữ cùng một loại thực thể trong mỗi cơ sở dữ liệu, hãy kiểm tra AutofacContrib.Multitenant.

Hai thành phần có thể giúp với kịch bản này là:

Trước tiên, hãy sử dụng các dịch vụ được đặt tên để tham chiếu đến hai cơ sở dữ liệu khác nhau. Tôi sẽ gọi cho họ "db1""db2 ".Tất cả các thành phần liên quan đến cơ sở dữ liệu, tất cả các con đường lên đến phiên giao dịch, được đăng ký với một cái tên:

builder.Register(c => ConfigureDb1()) 
    .Named<ISessionFactory>("db1") 
    .SingleInstance(); 

builder.Register(c => c.ResolveNamed<ISessionFactory>("db1").OpenSession()) 
    .Named<ISession>("db1") 
    .InstancePerLifetimeScope(); 

// Same for "db2" and so-on. 

Bây giờ, giả sử bạn có một loại NHibernateRepository<T> chấp nhận một ISession như tham số constructor của nó, và rằng bạn có thể viết hàm WhichDatabase(Type entityType) trả về "db1" hoặc "db2" khi được cung cấp loại thực thể.

Chúng tôi sử dụng số ResolvedParameter để chọn động phiên dựa trên loại đối tượng.

builder.RegisterGeneric(typeof(NHibernateRepository<>)) 
    .As(typeof(IRepository<>)) 
    .WithParameter(new ResolvedParameter(
     (pi, c) => pi.ParameterType == typeof(ISession), 
     (pi, c) => c.ResolveNamed<ISession>(
      WhichDatabase(pi.Member.DeclaringType.GetGenericArguments()[0]))); 

(Cảnh báo - biên soạn và thử nghiệm trong Google Chrome;))

Bây giờ, giải quyết IRepository<MyEntity> sẽ chọn phiên thích hợp, và các buổi sẽ tiếp tục được lười biếng khởi tạo và xử lý một cách chính xác bởi Autofac.

Bạn sẽ phải suy nghĩ kỹ về quản lý giao dịch của khóa học.

Hy vọng điều này sẽ xảy ra! NB

+1

Dude, đó là nó. Rất tuyệt vời! Tôi đã có một số tinh chỉnh để làm cho kịch bản của tôi, và đào thêm một chút vào nguồn Autofac cập nhật vì vậy tôi hiểu những gì đang xảy ra, nhưng địa ngục có, đây là lý do tại sao tôi yêu SO! Cảm ơn Nicholas! – nkirkes

+0

Thật tuyệt khi nghe - bạn được chào đón! –