动态切换数据库(EF框架)

         文章简略:本文测试项目为Silverlight+EF+RIA Service动态切换数据库的问题



通常,Ado.net EntityFramework的数据库连接字符串ConnectionString是存在实体框架所在的类库项目中的配置文件中(.config)的,类似这样:

<connectionStrings>
     <add name="{EFName}Entities" connectionString="metadata=res://*/Model1.csdl|
     res://*/Model1.ssdl|res://*/Model1.msl;provider=System.Data.SqlClient;
     provider connection string=&quot;data source={IP};
     initial catalog=Northwind;persist security info=True;user id=sa;
     password={PWD};multipleactiveresultsets=True;App=EntityFramework&quot;"
     providerName="System.Data.EntityClient" />
</connectionStrings>

EntityConnection的ConnectionString和以前的Ado.net的ConnectionString是有一些的区别的。有2个新元素,解释下:

             metatata: 指定Entity Framework数据模型的路径,包括CSDL(概念架构定义语言)、SSDL(存储架构定义语言)、MSL(映射规范语言)的路径

             provider: 数据库provider,例如SQLServer的默认Ado.net:System.Data.SqlClient,也可以是其它的数据库例如Oracle、SQLite、MySql等

需要注意的是:EF框架默认寻找的数据库连接数据库字符串不一定是当期类库或者项目,严谨的说法是,解决方案下的启动项目所在的配置文件中寻找连接

这是用配置的方式指定ConnectionString,也可以用代码动态指定Entity framework的数据库连接,用SqlConnectionStringBuilder手动创建:

public partial class EFNameEntities
 {
         partial void OnContextCreated()
         {
             SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder
             ((EntityConnection)Connection).StoreConnection.ConnectionString);
             builder.IntegratedSecurity = false;
             builder.UserID = "sa";
             builder.Password = "";
             builder.InitialCatalog = "";
             ((EntityConnection)Connection).StoreConnection.ConnectionString = builder.ConnectionString;
         }
 }

这样ConnectionString就写死了,不能满足实际的开发需求,所以还是要用Web.config配置的方式。

下面我们做一个测试,我们在Web.config添加一个数据库连接方式用来测试,关键词叫“MyTestDBConn”

<connectionStrings>
     <add name="EFNameEntities" connectionString="metadata=res://*/Model1.csdl|res:
    //*/Model1.ssdl|res://*/Model1.msl;provider=System.Data.SqlClient;
    provider connection string=&quot;data source=192.168.1.88;
    initial catalog=Northwind;persist security info=True;user id=sa;password={};
    multipleactiveresultsets=True;App=EntityFramework&quot;"
    providerName="System.Data.EntityClient" />
     <add name="MyTestDBConn" connectionString="metadata=res://*/Model1.csdl|res:
    //*/Model1.ssdl|res://*/Model1.msl;provider=System.Data.SqlClient;
    provider connection string=&quot;data source=192.168.1.100;
    initial catalog=Northwind_AAA;persist security info=True;
    user id=sa;password={PWD};multipleactiveresultsets=True;App=EntityFramework&quot;"
    providerName="System.Data.EntityClient" />
</connectionStrings>
在Domain Service中改变EntityFramework的ConnectionString,这儿指定用"MyTestDBConn":
using System.Configuration;

[EnableClientAccess()]
public class TestDomainService : LinqToEntitiesDomainService<TestEntities>
{
  protected override TestEntities CreateObjectContext()
  {
    string connection = ConfigurationManager.ConnectionStrings["MyTestDBConn"].ConnectionString;
    return new TestEntities(connection);
  }
}

这样的确可以在DomainService切换Entity Framework数据库连接ConnectionString,但我如果有好多DomainService怎么办,岂不是每一个都要这样来一下子?第二 个问题是,如何把"MyTestDBConn"作为一个参数传进来?

先解决第一个问题,我们写一个DomainService的基类,继承原来的基类:LinqToEntitiesDomainService,然后让每一个DomainService继承这个新的基类即可

using System;
 using System.Data.Objects;
 using System.ServiceModel.DomainServices.EntityFramework;
 using System.ServiceModel.DomainServices.Server;
 using System.Configuration;

 public abstract class DomainServiceBase<TContext> : LinqToEntitiesDomainService<TContext>
       where TContext : ObjectContext, new()
     {
         protected override TContext CreateObjectContext()
         {
             string connection = ConfigurationManager.ConnectionStrings["MyTestDBConn"].ConnectionString;

             Type contextType = typeof(TContext);
             object objContext = Activator.CreateInstance(contextType, connection);

             return objContext as TContext;
         }

         protected override void OnError(DomainServiceErrorInfo errorInfo)
         {
             // Deal with errors
         }
 }
 //接着,让你的DomainService继承这个基类,不要继承LinqToEntitiesDomainService了
 EnableClientAccess()]
 public class TestDomainService : DomainServiceBase<TestEntities>
 {
             //...
 }

第二个问题是如何把"MyTestDBConn"这个ConnectionString关键词作为参数传进来?这个问题有点难。上面说的都是在 服务器端指定连接到哪个数据库服务器,如何实现在客户端用户登录的时候,有个下拉框选择数据库服务器,然后在Silverlight客户端把这个参数传递 到服务器端DomainService并连接到指定的数据库服务器?

如果用上面的思路,就是要在DomainService的构造函数上动手,加一个参数string databaseKey,但是这个有点罗嗦。为什么?因为DomainService不是一般的class,它是WCF Service,在DomainService里面的每一个方法就是一个定义了request和response的contract的WCF (Web Service),只不过WCF Ria Services隐藏了这些调用WCF的复杂性,让你感觉在客户端就可以简单的调用DomainService。所以,如果你要给 DomainService的构造函数传一个参数,就要研究一下如何给WCF Service的构造函数传递一个参数的方法,可以用IInstanceProvider和ServiceBehavior来实现,代码见这篇文章。

这里我用另外一个Session的方法来实现更简单,每个客户端session不同,所以在客户端用户登录的时候,有个下拉框选择数据库服务 器,然后在Silverlight客户端把这个参数传递到服务器端DomainService并让Entity framework连接到指定的数据库服务器是可以用Session来实现的。具体方法是在DomainService里加一个SetDatabase函 数设置数据库,客户端把参数传给服务端,并保存到Session。然后重载CreateObjectContext ()函数并根据Session的值连接到指定的数据库。最后在SetDatabase这个函数的Callback时读取真正的数据。代码:

在DomainService里面加个SetDatabase()方法:

using System.Web;

 [EnableClientAccess()]
 public class TestDomainService : LinqToEntitiesDomainService<TestEntities>
 {
    public void SetDatabase(string Database)
    {
            HttpContext.Current.Session["UserDatabase"] = Database;
    }
 }

在DomainService里面重载CreateObjectContext()方法:
using System.Web;

[EnableClientAccess()]
public class TestDomainService : LinqToEntitiesDomainService<TestEntities>
{
   protected override TestEntities CreateObjectContext()
   {
        if (HttpContext.Current.Session["UserDatabase"] != null)
        {
                string connection = ConfigurationManager.ConnectionStrings[HttpContext.Current.Session["UserDatabase"].ToString()].ConnectionString;
                return new TestEntities(connection);
        }
        else
             return base.CreateObjectContext();
   }
}

前台调用xaml.cs:
public partial class MainPage : UserControl
{
        private DomainService1 s = new DomainService1();

        public MainPage()
        {
            InitializeComponent();

            //使用MyTestDBConn作为ConnectionString,加载数据到DataGrid
            LoadData("MyTestDBConn");
        }

         private void button1_Click(object sender, RoutedEventArgs e)
         {
             //使用Web.config中另外一个ConnectionString,加载数据到DataGrid
             LoadData("YourEntities");
         }

         //EF连接指定的ConnectionString
         //Param: database - web.config中ConnectionString的Name关键词
         private void LoadData(string database)
         {
             this.busyIndicator1.IsBusy = true;
             //****SetDatabase callback的时候读取数据*********
             s.SetDatabase(database, (InvokeOperation) =>
             {
                 //GetCompanies改为你自己的方法
                 s.Load(s.GetCompaniesQuery(), (t) =>
                 {
                     dataGrid1.ItemsSource = t.Entities;
                     this.busyIndicator1.IsBusy = false;
                 }, true);
             }, null);
         }
  }

可以另外按照上面DomainServiceBase的方法写一个基类避免每个DomainService重写一遍。

结束

不知道大家有没有更好的方法实现客户端传参到服务端动态切换数据库呢?另:由于Entity framework的模型是由链接到本地数据库映射生成的,所以不提供源码下载了,按照本文的步骤一步步做就能运行。如果对 Silverlight+Wcf Ria service+EF还不明白的,可以看博客园其他的博文。

参考文章:http://social.msdn.microsoft.com/Forums/en-US/wcf/thread/8f18aed8-8e34-48ea-b8be-6c29ac3b4f41

时间: 2024-10-16 03:21:37

动态切换数据库(EF框架)的相关文章

Mybatis 动态切换数据库

mybatis介绍: 每一个Mybatis的应用程序都以一个SqlSessionFactory对象的实例为核心.SqlSessionFactory对象实例可以通过SqlSessionFactoryBuilder对象获得.SqlSessionFactoryBuilder对象可以从XML配置文件或从Configuration类的习惯准备的实例中构建SqlSessionFactory对象. 从XML文件中构建SqlSessionFactory的实例非常简单.这里建议使用类的路径的资源文件来配置,这样我

基于AbstractRoutingDataSource的动态切换数据库

当项目发展到一定阶段,就需要对数据库进行一定的优化.一般会对数据库进行横向和纵向切库分表,但是这样的问题就来了,在我们操作数据库时,需要根据切分规则提前获得我们需要的数据库的连接,这明显会加重程序员的负担. 比如我们将"用户信息数据库"按照用户注册的年月来分库,在用户注册的时候,为用户分配一个以yyyyMM开头的唯一标示,以方便我们能快速定位到切分后的子数据库.那么问题来了,我们在项目中,如何动态且方便的获得我们需要的数据源呢?Spring提供了一个解决方案,那就是基于Abstract

动态切换数据库源码解析

动态切库可用于SaaS环境,多租户环境 所以浏览器的每次请求都有可能是不同租户,需要动态切换数据库来支持业务场景. 又所以每次请求都需要识别是哪个租户,这里我们用到了ThreadLocal,以此来保存线程的本地变量,携带上租户的一些信息.而租户的信息可以从Session或Token中获取,或者是url路径参数 准备好以上条件信息 我们开始秀吧 创建 DruidDynamicDataSource 继承自 AbstractRoutingDataSource org.springframework.j

EntityFramework For Mysql 动态切换数据源

1.简介 在工作中遇到一个问题.项目有三个数据库(三个数据库表结构一样),用户可以选择使用哪个数据库.其实就是动态切换数据库连接. 2.EntityFramework For Mysql 先来简单的介绍下mysql使用EntityFramework来操作数据库. 直接上代码: (1).先建个项目,安装mysql,entityframework相关包 (2).建立实体和对应的数据库表 (3).编写数据库连接字符串,编写context实体 这样就可以来使用Entityframework来访问mysq

java 动态操作数据库

问题描述:比如项目现在要使用在南京的8的区,这时这8个区分别建了一个数据库,但是只有一个项目,每个区的用户都使用这个项目进行登录 问题难点:如何验证登录人属于哪个区,然后确认之后,如何进行数据库的切换: 问题思路:除了8个数据库之外,在建一个数据库:数据库中包含的几张表:储存登录用户的信息等,直接上图理解 一.数据库的建立 h_right : h_role:红色表示登录人  所属哪一区 h_role_right:  分配显示的菜单 h_role_sysuser:用于分配区编号 h_sysuser

spring框架中多数据源创建加载并且实现动态切换的配置实例代码

原文:spring框架中多数据源创建加载并且实现动态切换的配置实例代码 源代码下载地址:http://www.zuidaima.com/share/1774074130205696.htm 在我们的项目中遇到这样一个问题:我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库.我们以往在spring和hibernate框架中总是配置一个数据源,因而sessionFactory的dataSource属性总是指向这个数据源并且恒定不变,所有DAO在使用sessionFa

C# CodeFirst(EF框架)代码优先创建数据库

namespace WebEF.Model{ public class ModelContext:DbContext //继承DBcontext 来自EF框架 { public ModelContext() : base("name=配置文件名") { //读取配置文件 /*配置文件的设置格式 <connectionStrings> <add name="配置文件名" connectionString="Data Source=(. .l

ASP.NET MVC+EF框架+EasyUI实现权限管理系列(2)-数据库访问层的设计Demo

原文:ASP.NET MVC+EF框架+EasyUI实现权限管理系列(2)-数据库访问层的设计Demo ASP.NET MVC+EF框架+EasyUI实现权限管系列 (开篇) (1)框架搭建 前言:这篇博客我们继续来实现我的权限系列,这个博客一段时间也没有写了,重点是我在想还写不写,最终我决定还是写下去,因为我们是为了学习,当别人提出意见的时候,我们可以参考和采纳,但是我们不一定非要采纳,上几篇博客大家都说用CodeFirst来实现,是啊,现在基本很少有人用我的这种方法来实现了,都是用CodeF

使用T4模板为EF框架添加实体根据数据库自动生成字段注释的功能

在以往的开发过程当中,我会经常选择EF框架作为底层数据结构,EF为我们提供了很好的ado.net数据访问机制,他覆盖了数据链接,linq等多方面内容,而且当我们使用数据库优先或者code first的时候都体现出明显的优势. 一键生成实体,免除手写model的烦恼. 实时更新数据结构,使数据库与model保持高度一致. 提供多种底层数据的访问方法. 优雅的语法,对于我这种喜爱偷懒的程序猿是一大福音. 然而entity framework在vs中生成的.edmx文件,会导致摘要(说明)为空的bug