代码优先的Entity Framework是否可以与同一框上的SQL Server DB进行跨数据库查询?

问题描述

| 我知道关于Entity Framework在发布到stackoverflow的同一台服务器上进行跨数据库查询的问题很多。大多数情况下,答案似乎是“否”,此链接可追溯到2008年。但是,Entity Framework一直在变化,并且随着CTP5的发布,我想知道答案是否仍然是相同的-您无法做到这一点,或者您可以手动编辑edmx文件来做到这一点,或者您必须使用视图。仅此功能就是我仍然与Linq-to-sql绑定的原因,因为我们在同一服务器上有多个sql Server 2008数据库,并且需要在它们之间进行查询。用数百个“ 0”视图污染数据库是不可行的,并且通过代码优先开发,我没有edmx文件可编辑。我在玩pubs数据库,看看是否可以到达某个地方,但是我被卡住了。有什么建议么?
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration;

namespace DbSchema {
    public class Employee {
        [Key]
        public string ID { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public short JobID { get; set; }
        public Job Job { get; set; }
    }

    public class Job {
        [Key]
        public short ID { get; set; }
        public string Description { get; set; }
    }

    public class PubsRepository : DbContext {
        public DbSet<Employee> Employee { get; set; }
        public DbSet<Job> Job { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder) {
            // employee
            var eeMap = modelBuilder.Entity<Employee>();
            eeMap.ToTable(\"employee\",\"dbo\"); // <-- how do I reference another database?
            eeMap.Property(e => e.ID).HasColumnName(\"emp_id\");
            eeMap.Property(e => e.FirstName).HasColumnName(\"fname\");
            eeMap.Property(e => e.LastName).HasColumnName(\"lname\");
            eeMap.Property(e => e.JobID).HasColumnName(\"job_id\");

            // job
            var jobMap = modelBuilder.Entity<Job>();
            jobMap.Property(j => j.ID).HasColumnName(\"job_id\");
            jobMap.Property(j => j.Description).HasColumnName(\"job_desc\");
        }

        public List<Employee> GetManagers() {
            var qry = this.Employee.Where(x => x.Job.Description.Contains(\"manager\"));
            Debug.WriteLine(qry.ToString());
            return qry.ToList(); // <-- error here when referencing another database!
        }
    }
}
    

解决方法

我认为答案仍然是否,但是有很多解决办法。 否的原因是,EF使用DBContext,并且上下文具有连接字符串,并且连接字符串进入数据库。 有两种解决方法: 对每个数据库使用2个不同的上下文,这意味着将数据带到客户端并将其合并到客户端。 使用数据库上的链接表,通过视图拉取数据,以便EF将其视为来自单个数据库。 在您的代码中,看起来您正在使用2个dbcontexts     ,有两种方法可以做到这一点。 当然,一种方法是在其中一个数据库中进行跨数据库查询的视图中创建一个视图,然后像访问其他任何视图一样从模型中访问视图。 另一个是通过创建
DefiningQuery
在模型本身内创建相同的跨数据库查询视图。这与使用SQLClient的方式最相似。在SQLClient中,您将在T-SQL中将视图创建为SQLCommand的文本,然后执行命令以创建数据读取器或数据表。在这里,您使用相同的T-SQL创建DefiningQuery,然后将其链接到您手动创建的实体。这需要一些工作,但它确实可以满足您的要求。 这是使用ѭ3的链接:http://msdn.microsoft.com/zh-cn/library/cc982038.aspx。 如果您碰巧拥有O'Reilly的Lerman的书“ Programming Entity Framework”,那么在第16章中就有一个很好的例子。 因此,您必须跳过几个步骤才能完成您以前直接使用SQLClient进行的操作,但要获得建模的Entity。     ,答案还是一样。如果要执行跨数据库查询,则必须退回到SQL并在
context.Database
上使用
SqlQuery
。     ,警告!使用DefiningQuerys可能非常慢! 这是一个例子: 如果这是您针对其创建实体的定义查询:
Select
    C.CustomerID,C.FirstName,C.LastName,G.SalesCatetory
From
    CustomerDatabase.dbo.Customers C
    Inner Join MarketingDatabase.dbo.CustomerCategories G on G.CustomerID = C.CustomerID
然后,当您通过CustomerID对Entity进行选择时,SQL跟踪看起来像这样:
Select
[Extent1].[CustomerID] as [CustomerID],[Extent1].[FirstName] as [FirstName],[Extent1].[LastName] as [LastName],[Extent1].[SalesCatetory] as [SalesCatetory]
From (
        Select
            C.CustomerID,G.SalesCatetory
        From
            CustomerDatabase.dbo.Customers C
            Inner Join MarketingDatabase.dbo.CustomerCategories G on G.CustomerID = C.CustomerID
        ) as [Extent1]
Where \'123456\' = [Extent1].[CustomerID]
SQL Server可能会非常缓慢地运行此查询。我有一种情况,比上面的示例稍微复杂一点,在这种情况下,我通过在SQl服务器管理控制台查询窗口中直接添加想要选择的值的where子句,尝试了DefiningQuery文本。它运行不到一秒钟。然后,我从为该DefiningQuery创建的实体中选择相同的值而捕获了SQL Trace,并在SQL Server查询窗口中运行了SQL Trace查询-花了13秒! 因此,我想进行跨数据库查询的唯一真正方法是在服务器上创建一个视图。