[英]Entity Framework MVC and multiple databases
我已經將一個使用存儲庫模式的MVC應用程序與Entity Framework放在一起,並且一切都進行得很順利-但是我遇到了一個障礙,我不確定如何繼續。
我有幾十個具有相同架構的數據庫,我希望能夠在運行時選擇一個或多個數據庫。 例如,假設我從一個用戶數據庫(尚未建立)開始。 該用戶具有與之關聯的連接字符串信息(可能不止一個)。 用戶“登錄”后, 我希望輸入到我的視圖中的Enumerables包含用戶有權訪問的所有數據庫中的匹配數據。
這是我現在擁有的示例:
實體:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel.DataAnnotations.Schema;
namespace Dashboard.Domain.Entities
{
public class Flight
{
public Guid Id { get; set; }
public string CarrierCode { get; set; }
public string FlightNo { get; set; }
public string MarketingCarrierCode { get; set; }
public string MarketingFlightNo { get; set; }
public string Type { get; set; }
public string TailNo { get; set; }
public string OriginIATA { get; set; }
...
}
}
數據庫上下文:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Data.Entity;
using Dashboard.Domain.Entities;
namespace Dashboard.Domain.Concrete
{
public class EFDbContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Passenger>().ToTable("PAX");
}
public DbSet<Flight> Flights { get; set; }
public DbSet<Passenger> PAX { get; set; }
public DbSet<Airport> Airports { get; set; }
}
}
飛行庫界面:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Dashboard.Domain.Entities;
namespace Dashboard.Domain.Abstract
{
public interface IFlightRepository
{
IQueryable<Flight> Flights { get; }
}
}
EF飛行資料庫:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Dashboard.Domain.Abstract;
using Dashboard.Domain.Entities;
namespace Dashboard.Domain.Concrete
{
public class EFFlightRepository : IFlightRepository
{
private EFDbContext context = new EFDbContext();
public IQueryable<Flight> Flights
{
get { return context.Flights; }
}
}
}
控制器:
public class FlightController : Controller
{
private IFlightRepository fRepository;
private IPaxRepository pRepository;
private IAirportRepository aRepository;
public int PageSize = 10;
public FlightController(IFlightRepository flightRepository, IPaxRepository paxRepository, IAirportRepository airportRepository)
{
this.fRepository = flightRepository;
this.pRepository = paxRepository;
this.aRepository = airportRepository;
}
public ViewResult List(byte status = 1, int page = 1)
{ ...
我希望這些存儲庫包含指定的所有連接字符串中的所有數據,但是我不知道從哪里開始。 EF從web.config獲取我的連接字符串,但是我需要能夠以某種方式動態設置它,而且我需要將多個數據庫的數據放入存儲庫中。
這可能嗎? 我應該提到該站點是只讀的,因此我不需要將更改寫回到數據庫中。
我更改了代碼,以便可以將連接字符串傳遞給EF信息庫的構造函數,但是當我嘗試合並來自兩個不同上下文的IQueryable時,如下所示:
public class EFFlightRepository : IFlightRepository
{
private EFDbContext context1 = new EFDbContext(connectionstring1);
private EFDbContext context2 = new EFDbContext(connectionstring2);
private IQueryable<Flight> context;
public EFFlightRepository()
{
context = (IQueryable<Flight>)context1.Flights.Union(context2.Flights);
}
public IQueryable<Flight> Flights
{
get { return context;}
}
}
我得到這個例外:
指定的LINQ表達式包含對與不同上下文關聯的查詢的引用。
如何合並它們,以便像運行一組數據一樣運行LINQ查詢?
很難給出詳細的解決方案,因為它實際上取決於您的軟件設計選擇,但是我認為可能的解決方案包括以下幾方面:
1)一種方法/類,該方法/類使用DbContext構造函數創建DbContext對象的集合,該構造函數具有與Willian Werlang所述的連接字符串或連接字符串名稱(是相同的構造函數):
new DbContext("DB1");
2)您的存儲庫應該能夠接受DbContext的列表,而不是單個列表。 例如,可以將其注入其構造函數。
3)檢索方法應遍歷存儲庫並加載(分離時急於加載)相關對象。
4)可以使用以下代碼將檢索到的對象與其DbContext分離:
dbContext.Entry(entity).State = EntityState.Detached;
這不是必需的,但可能是一個考慮因素,因為您將返回不同數據源的混合。
5)檢索/分離的對象應添加到返回的List <>中,否則您可以使用IEnumerable <>逐個返回的結果作為返回類型。
在這種情況下,無法返回IQueryable,但將結果作為IEnumerable。
飛行庫的簡單檢索方法示例如下:
public IEnumerable<Flight> GetFlights() {
// dbContexts is an IEnumerable<DbContext> that was injected in the constructor
foreach (var ctx in dbContexts) {
foreach (var flight in ctx.Flights) {
yield return flight;
}
}
}
您可以在web.config上設置多個數據庫,但使用不同的名稱,因此DbContext可以接收所需的數據庫名稱作為參數,例如:
new DbContext("DB1");
這樣,您可以選擇從哪個數據庫中獲取數據,但是我認為僅使用onde dbContext不能同時從多個數據庫中獲取數據。
我的解決方案是將我的存儲庫類更改為采用連接字符串參數,如下所示:
namespace Dashboard.Domain.Concrete
{
public class EFFlightRepository : IFlightRepository
{
private EFDbContext context;
public IQueryable<Flight> Flights
{
get { return context.Flights;}
}
public EFFlightRepository(string connectionString)
{
context = new EFDbContext(connectionString);
}
}
}
然后創建一個工廠類(使用Ninject.Extensions.Factory)在創建存儲庫時傳遞參數 ( 如何使用Ninject沿依賴關系鏈向下傳遞參數 ):
namespace Dashboard.Domain.Factories
{
public interface IFlightRepoFactory
{
IFlightRepository CreateRepo(string connectionString);
}
}
我還有另一個Factory類,它基於字符串列表(用於饋送到各個存儲庫類的連接字符串)生成一個存儲庫列表。
namespace Dashboard.Domain.Factories
{
public interface IRepoCollectionFactory
{
IRepositoryCollection CreateCollection(List<string> connectionStrings);
}
}
然后,在我的控制器類中,遍歷Collection Factory生成的Collection,運行需要在每組存儲庫上運行的任何查詢,然后合並結果。
最終,這給了我一個列表,其中包含每個存儲庫中每個查詢的所有數據。
public FlightController(IRepoCollectionFactory repoCollectionFactory)
{
this.repoCollectionFactory = repoCollectionFactory;
this.collection = repoCollectionFactory.CreateCollection(new List<string> {
// each connection string for each database here
});
}
Ninject類中的綁定:
private void AddBindings()
{
ninjectKernel.Bind<IFlightRepoFactory>().ToFactory();
ninjectKernel.Bind<IAirportRepoFactory>().ToFactory();
ninjectKernel.Bind<IPaxRepoFactory>().ToFactory();
ninjectKernel.Bind<IRepoFactory>().ToFactory();
ninjectKernel.Bind<IRepoCollectionFactory>().ToFactory();
ninjectKernel.Bind<IRepositories>().To<EFRepositories>();
ninjectKernel.Bind<IRepositoryCollection>().To<EFRepositoryCollection>();
ninjectKernel.Bind<IFlightRepository>().To<EFFlightRepository>();
ninjectKernel.Bind<IPaxRepository>().To<EFPaxRepository>();
ninjectKernel.Bind<IAirportRepository>().To<EFAirportRepository>();
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.