首页 新闻 会员 周边 捐助

程序端的数据读写分离

0
悬赏园豆:200 [已解决问题] 解决于 2014-07-02 09:27

项目中使用的entity framework 

领导说,现在要做读写分离,现在确定下来的是只能在程序端做。

求EF 怎么实现读写分离?

 

这几天想了很多的方案了,但是实现起来感觉都有问题..

1、拦截ef操作,区分是读或者是写以后对应调整链接字符串。但是我不知道怎么能拦截的到。

2、如果在底层多映射出来EF的话,那就会区分出来读的EF和写的EF,但是这两个EF的域模型又是一样,且不能是一个名字,这样感觉很2。但是又不知道,怎么样才能两个EF公用一个域模型。

 

大家有什么方法么?

问题补充:

还有没有大神有别的方案?

Sky.Grain的主页 Sky.Grain | 菜鸟二级 | 园豆:308
提问于:2014-06-30 12:58
< >
分享
最佳答案
0

当然是提供两套应用程序服务接口。

收获园豆:150
Launcher | 高人七级 |园豆:45050 | 2014-06-30 15:33

能详细说下么?

Sky.Grain | 园豆:308 (菜鸟二级) | 2014-06-30 15:35

@Sky.Grain: 你可以看下 CQRS 的方式,它主要分为 Command Facade 和 Query Facade,当然我希望你不要过于死板的看待这个架构,以免你认为过于复杂,由于良好的分层结构,每一层可以实现也可以不实现,可以这样实现,也可以那样实现。但重点在于 Command Facade 和 Query Facade 上,支撑它们的实现是怎样的不重要,重要的是调用方可以被严格的区分为 Command 和 Query。比如:

ISkyCommand {

    void Add(Grain value);

}

 

ISkyQuery {

   IEnumable<Grain> Find(string Id);

}

Launcher | 园豆:45050 (高人七级) | 2014-06-30 15:51

@Launcher: 大概了解了CQRS的内容,但是现在对于一点比较钻牛角尖了。

比如说我有两个EF,一个wirte一个read,他们都有一个表为sky。

比较没理解的是这个问题,从数据库角度来理解的话,这两个EF 相当于两个库,而其中的每一个SKY相当于库中的表。但是从程序角度来想,我是有两套的EF,而且这两套EF里面有相同的域模型的耦合。

那从程序的角度看,应该如何来解决这个耦合问题,还是我对于这个问题的理解有偏差?

 

ps:求大神QQ,小弟求教一些问题

Sky.Grain | 园豆:308 (菜鸟二级) | 2014-07-01 09:14

@Sky.Grain: 就一个 EF 就够了,可供 ISkyCommand 和 ISkyQuery 调用。应用只能调用 ISkyCommand 和 ISkyQuery。不存在什么耦合问题,也没有什么相当于两个库。我假设以后你们在数据库上做了读写分离,那么你就需要两个 EF 模型,一个供 ISkyCommand 调用,另一个供 ISkyQuery 调用。由于接口不变,更改的只是实现类。

Launcher | 园豆:45050 (高人七级) | 2014-07-01 13:41

@Launcher: 现在问题就这里,两套EF 但是就有两套基本一致或几乎相同的域模型,觉得应该只有一套域模型就可以了。但是因为项目是dbfirst,所以这里很困扰。

Sky.Grain | 园豆:308 (菜鸟二级) | 2014-07-01 15:40

@Sky.Grain: 用一套,还是用两套,都没关系。甚至我就只用 ADO.NET,难道就不能实现 ISkyCommand 了吗?

Launcher | 园豆:45050 (高人七级) | 2014-07-01 15:52

@Launcher: 能实现的,不管用什么数据库都或什么方式都可以的。只是只看下面两个除了链接字符串不同,别的什么都相同的EF,觉得很不应该这样。额 似乎跟我的提问有些跑题了.

Sky.Grain | 园豆:308 (菜鸟二级) | 2014-07-01 15:58

@Sky.Grain: 就一个 EF 就够了,可供 ISkyCommand 和 ISkyQuery 调用 —— 这是我的原话,也就是你可以用一个,一也可以用两个,我没有说你必须要用几个。既然我都这么说了,你又对一套,还是两套的问题很困扰,请问,你到底困扰的是啥?

Launcher | 园豆:45050 (高人七级) | 2014-07-01 16:01

@Launcher: 因为读写要链接到不同的数据库去,就是链接字符串不一样。然后项目的是dbfirst,没想出来怎么去依据程序的读写,去改变这个链接字符串。

就很2 的在这个地方迷茫了...

Sky.Grain | 园豆:308 (菜鸟二级) | 2014-07-01 16:05

@Sky.Grain: 说了半天,你已经到了这一步了“我假设以后你们在数据库上做了读写分离”(还是我的原话),我就不跟你讲 CQRS 中 Command 和 Query 中模型的区别了(虽然它们针对的是同一个数据库架构),我就告诉你如何在创建 DbContext 时修改连接字符串的方法。

SkyCommand {

     DbContext ctx = new DbContext("用于命令模式的数据库名称或连接字符串");

}

 

QueryCommand {

     DbContext ctx = new DbContext("用于查询模式的数据库名称或连接字符串");

}

Launcher | 园豆:45050 (高人七级) | 2014-07-01 16:14

@Launcher: 求给一些CQRS的资料,额,虽然我昨天问过了谷歌和度娘。

链接这里我在想想办法,因为又回到之前的问题了。

项目是dbfirst,new DbContext()这里是不行的 

public partial class MyContext : DbContext
{
  public MyContext (): base("name=sky")
  {
  }

}

外面调用我需要new MyContext () 

而这个new MyContext ()  是dbfist 的edmx文件生成出来的,而为了在有一个读库Context话就要在有一个edmx,在有一个edmx就又会有一套域模型....

 

如果现在项目是codefirst的话,我是知道怎么处理这个的。

 

ps:感谢大神不厌其烦

Sky.Grain | 园豆:308 (菜鸟二级) | 2014-07-01 16:48

@Sky.Grain: 没告诉你都调用 ISkyCommand 和 ISkyQuery 吗?不允许直接调用 MyContext。

public partial class MyContext

 MyContext(string xxxx): base(xxxx);

SkyCommand {

     MyContext ctx = new MyContext("用于命令模式的数据库名称或连接字符串");

}

我就纳闷了,管你 db-first 还是 code-first,你给的代码都是从 DbContext 继承的,咋就拐不过弯儿呢?

 

QueryCommand {

     MyContext ctx = new MyContext("用于查询模式的数据库名称或连接字符串");

}

Launcher | 园豆:45050 (高人七级) | 2014-07-01 17:14

@Sky.Grain: 你咋有跟我饶弯弯呢?

“有一个读库Context话就要在有一个edmx,在有一个edmx就又会有一套域模型”

你到底是想用一个EF模型,还是两个,还是多个?咋能定下来不,别变来变去的。

Launcher | 园豆:45050 (高人七级) | 2014-07-01 17:18
其他回答(8)
0

我曾经也想过这个问题,没有找到好的办法。有一个不成熟的想法是,根据不同的方法名前辍来区分,比如find*,get*都是读的。do*或是update都是写的。

angelshelter | 园豆:9914 (大侠五级) | 2014-06-30 14:31

是要映射出来2套EF 只是写 方法的时候 手动去调用不同的么?

支持(0) 反对(0) Sky.Grain | 园豆:308 (菜鸟二级) | 2014-06-30 14:33

@Sky.Grain: 一套吧,有代理的话,在方法执行前,根据不同的类别(读/写)传不同的dao

支持(0) 反对(0) angelshelter | 园豆:9914 (大侠五级) | 2014-06-30 14:35
0

学习,mark下。坐等楼主研究出来结果,给于分享。

大楚打码人 | 园豆:4313 (老鸟四级) | 2014-06-30 15:10
0

弄个两个代理类,私有实例化DbContext,然后一个暴露Add、Edit、Delete方法,调用DbContext对应的方法,另一个暴露where,find,include,load等方法,调用DbContext对应的方法

happydaily | 园豆:260 (菜鸟二级) | 2014-06-30 16:24
0

Mark....坐等结果

羽商宫 | 园豆:2490 (老鸟四级) | 2014-06-30 17:24
0

读写分离,应该是指数据库层面的吧?目的是为了提高查询的速度。

kimi_gyj | 园豆:192 (初学一级) | 2014-06-30 18:34

是应该这样,但是领导不要这样,想给出两个数据库链接 然后用程序实现。

表示很无奈

支持(0) 反对(0) Sky.Grain | 园豆:308 (菜鸟二级) | 2014-06-30 19:13
0

你创建2个context

一个用于读一个用于写

读的context 不调用savechange方法

 

不能理解的是 为什么要做这种需求

收获园豆:50
小眼睛老鼠 | 园豆:2731 (老鸟四级) | 2014-07-01 10:06

领导的想法,有时候没办法。

两个context 的话,就是映射出来两套EF?

我有类似的想法,但是不清楚怎么做

支持(0) 反对(0) Sky.Grain | 园豆:308 (菜鸟二级) | 2014-07-01 10:11

然后从程序优化的角度来看

数据库有一种机制

以一个库镜像出另一个库

这样可以做到把2个库分离到不同的数据库服务器

这个时候只有主库能做写的操作

而拷贝库只能做读的操作 大致有1到2秒的延时 如果是给客户看足够了

但是如果使用这种架构 那么也好办 还是2个context  修改context连接主库

而查询context 连接备份库 这样就能做到读写分离了

 

ps:这里有一点 读写库分离 不适用于立即显示效果 换句话说 如果你是后台系统你添加了什么东西立即要更新 那么你适用的库 一定是主库 而不是拷贝库

拷贝库的作用仅仅只是为了数据库负载均衡

使用这种库的场景有限 及后台更新了 外部客户要看 而且高访问 这种时候就应该访问查询库 因为慢1到2秒不是个事

支持(0) 反对(0) 小眼睛老鼠 | 园豆:2731 (老鸟四级) | 2014-07-01 10:12

@小眼睛老鼠: 哈哈。我明白的说的意思,这些话也转告过和我一起开发但是负责数据库端的同事。

没办法呀,毕竟有些是领导的意思。而且貌似我们虽然是分库了,但是只有一个服务器,我汗颜了,个人觉的这没多大用处。

现在我比较纠结的是怎么去实现这两个context 因为从EF映射的话 或多出来一套一样的域模型,这点表示很无解。要不就是一个context去然后换链接字符串,但是这样需要上层能很好的区别读写操作..

支持(0) 反对(0) Sky.Grain | 园豆:308 (菜鸟二级) | 2014-07-01 11:47

@Sky.Grain: 难道你不是codefirst??

支持(0) 反对(0) 小眼睛老鼠 | 园豆:2731 (老鸟四级) | 2014-07-01 11:52

@小眼睛老鼠: 如果是的话就好弄了... 悲催的项目是Database First

支持(0) 反对(0) Sky.Grain | 园豆:308 (菜鸟二级) | 2014-07-01 13:21

@Sky.Grain: so 对于这种有好的方案么?

支持(0) 反对(0) Sky.Grain | 园豆:308 (菜鸟二级) | 2014-07-01 13:21

@Sky.Grain: 

我没用过dbfirst 主要是dbcontext的 连接字符串 读的连到一个库 写的连到一个库就可以了

那个模板只是一个结构吧 应该是可以动态修改连接字符串的吧 那个模板不是一个静态实例吧???

支持(0) 反对(0) 小眼睛老鼠 | 园豆:2731 (老鸟四级) | 2014-07-01 13:35

@Sky.Grain: 

你把你创建 context的代码贴出来 我看看

支持(0) 反对(0) 小眼睛老鼠 | 园豆:2731 (老鸟四级) | 2014-07-01 13:37

@小眼睛老鼠: 给个联系方式细说~

支持(0) 反对(0) Sky.Grain | 园豆:308 (菜鸟二级) | 2014-07-01 13:44

@Sky.Grain: 

qq:2240921114

支持(0) 反对(0) 小眼睛老鼠 | 园豆:2731 (老鸟四级) | 2014-07-01 13:46

@小眼睛老鼠: 已经加了 审核过一下

支持(0) 反对(0) Sky.Grain | 园豆:308 (菜鸟二级) | 2014-07-01 15:58
0

哈喽, 这个问题解决了吗? 我现在也在弄这个读写分离的方案, 能分享下你怎么实现的吗?

junjie008 | 园豆:202 (菜鸟二级) | 2015-03-24 17:43
0

基于 EntityFramework 6 的读写分离方案,实施起来是很简单的

1、先建立起数据库的主从复制/备份机制,这个交由dbms来完成,不赘述;

2、在第 1 步的基础上,我们就有了 master 和 slave 数据库了;

3、在 EF 中,通过命令拦截器,可以在 EF 分别执行增删改和查询时改变 Connection 的 ConnectionString;具体的做法是写一个 DbCommandInterceptor 的子类,然后通过 DbInterception.Add 添加至程序上下文;

4、注意,在继承实现 DbCommandInterceptor 以调整连接字符串时,需要自行处理连接状态不同时的异常问题;

枫桥流云 | 园豆:209 (菜鸟二级) | 2015-03-25 16:25

基于以上方案,是不需要两套 DbContext 的,一套即可;而且不用调整现有的任何数据处理代码;

当然,前提条件肯定是你的所有数据访问都是通过 EF 来完成的,哪怕是通过 DbContext.Database.SqlQuery/ExecuteSqlCommand 也是可以的。

支持(0) 反对(0) 枫桥流云 | 园豆:209 (菜鸟二级) | 2015-03-25 16:27

但是有一种特殊情况,是基于EF读写分离可能会导致数据一致性错误的,就是在数据访问时(同时含增删改和查询操作)进行了事务的处理。

在这种情况下,如果所用数据库的事务机制及其ado.net provider不支持DbTransaction自动提升为分布式事务,将会导致数据一致性错误;并且在这种情况下,调用事务操作,必须用 TransactionScope而非 DbTransaction;

不过欣慰的是,目前的主流数据库及其ado.net provider,包括mssql、mysql、oracle,都支持数据库事务自动提升为分布式事务的。也就是说这个问题在大多数数据库场景下,都是不必担心的。

支持(0) 反对(0) 枫桥流云 | 园豆:209 (菜鸟二级) | 2015-03-25 16:31

太棒了,我去找点资料看,不好意思了,因为很早已经结贴,没办法给你分。

支持(0) 反对(0) Sky.Grain | 园豆:308 (菜鸟二级) | 2015-03-25 16:39
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册