繁体   English   中英

实体框架MVC和多个数据库

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM