[英]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.