项目中使用的entity framework
领导说,现在要做读写分离,现在确定下来的是只能在程序端做。
求EF 怎么实现读写分离?
这几天想了很多的方案了,但是实现起来感觉都有问题..
1、拦截ef操作,区分是读或者是写以后对应调整链接字符串。但是我不知道怎么能拦截的到。
2、如果在底层多映射出来EF的话,那就会区分出来读的EF和写的EF,但是这两个EF的域模型又是一样,且不能是一个名字,这样感觉很2。但是又不知道,怎么样才能两个EF公用一个域模型。
大家有什么方法么?
还有没有大神有别的方案?
当然是提供两套应用程序服务接口。
能详细说下么?
@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: 大概了解了CQRS的内容,但是现在对于一点比较钻牛角尖了。
比如说我有两个EF,一个wirte一个read,他们都有一个表为sky。
比较没理解的是这个问题,从数据库角度来理解的话,这两个EF 相当于两个库,而其中的每一个SKY相当于库中的表。但是从程序角度来想,我是有两套的EF,而且这两套EF里面有相同的域模型的耦合。
那从程序的角度看,应该如何来解决这个耦合问题,还是我对于这个问题的理解有偏差?
ps:求大神QQ,小弟求教一些问题
@Sky.Grain: 就一个 EF 就够了,可供 ISkyCommand 和 ISkyQuery 调用。应用只能调用 ISkyCommand 和 ISkyQuery。不存在什么耦合问题,也没有什么相当于两个库。我假设以后你们在数据库上做了读写分离,那么你就需要两个 EF 模型,一个供 ISkyCommand 调用,另一个供 ISkyQuery 调用。由于接口不变,更改的只是实现类。
@Launcher: 现在问题就这里,两套EF 但是就有两套基本一致或几乎相同的域模型,觉得应该只有一套域模型就可以了。但是因为项目是dbfirst,所以这里很困扰。
@Sky.Grain: 用一套,还是用两套,都没关系。甚至我就只用 ADO.NET,难道就不能实现 ISkyCommand 了吗?
@Launcher: 能实现的,不管用什么数据库都或什么方式都可以的。只是只看下面两个除了链接字符串不同,别的什么都相同的EF,觉得很不应该这样。额 似乎跟我的提问有些跑题了.
@Sky.Grain: 就一个 EF 就够了,可供 ISkyCommand 和 ISkyQuery 调用 —— 这是我的原话,也就是你可以用一个,一也可以用两个,我没有说你必须要用几个。既然我都这么说了,你又对一套,还是两套的问题很困扰,请问,你到底困扰的是啥?
@Launcher: 因为读写要链接到不同的数据库去,就是链接字符串不一样。然后项目的是dbfirst,没想出来怎么去依据程序的读写,去改变这个链接字符串。
就很2 的在这个地方迷茫了...
@Sky.Grain: 说了半天,你已经到了这一步了“我假设以后你们在数据库上做了读写分离”(还是我的原话),我就不跟你讲 CQRS 中 Command 和 Query 中模型的区别了(虽然它们针对的是同一个数据库架构),我就告诉你如何在创建 DbContext 时修改连接字符串的方法。
SkyCommand {
DbContext ctx = new DbContext("用于命令模式的数据库名称或连接字符串");
}
QueryCommand {
DbContext ctx = new DbContext("用于查询模式的数据库名称或连接字符串");
}
@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: 没告诉你都调用 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("用于查询模式的数据库名称或连接字符串");
}
@Sky.Grain: 你咋有跟我饶弯弯呢?
“有一个读库Context话就要在有一个edmx,在有一个edmx就又会有一套域模型”
你到底是想用一个EF模型,还是两个,还是多个?咋能定下来不,别变来变去的。
我曾经也想过这个问题,没有找到好的办法。有一个不成熟的想法是,根据不同的方法名前辍来区分,比如find*,get*都是读的。do*或是update都是写的。
是要映射出来2套EF 只是写 方法的时候 手动去调用不同的么?
@Sky.Grain: 一套吧,有代理的话,在方法执行前,根据不同的类别(读/写)传不同的dao
学习,mark下。坐等楼主研究出来结果,给于分享。
弄个两个代理类,私有实例化DbContext,然后一个暴露Add、Edit、Delete方法,调用DbContext对应的方法,另一个暴露where,find,include,load等方法,调用DbContext对应的方法
Mark....坐等结果
读写分离,应该是指数据库层面的吧?目的是为了提高查询的速度。
是应该这样,但是领导不要这样,想给出两个数据库链接 然后用程序实现。
表示很无奈
你创建2个context
一个用于读一个用于写
读的context 不调用savechange方法
不能理解的是 为什么要做这种需求
领导的想法,有时候没办法。
两个context 的话,就是映射出来两套EF?
我有类似的想法,但是不清楚怎么做
然后从程序优化的角度来看
数据库有一种机制
以一个库镜像出另一个库
这样可以做到把2个库分离到不同的数据库服务器
这个时候只有主库能做写的操作
而拷贝库只能做读的操作 大致有1到2秒的延时 如果是给客户看足够了
但是如果使用这种架构 那么也好办 还是2个context 修改context连接主库
而查询context 连接备份库 这样就能做到读写分离了
ps:这里有一点 读写库分离 不适用于立即显示效果 换句话说 如果你是后台系统你添加了什么东西立即要更新 那么你适用的库 一定是主库 而不是拷贝库
拷贝库的作用仅仅只是为了数据库负载均衡
使用这种库的场景有限 及后台更新了 外部客户要看 而且高访问 这种时候就应该访问查询库 因为慢1到2秒不是个事
@小眼睛老鼠: 哈哈。我明白的说的意思,这些话也转告过和我一起开发但是负责数据库端的同事。
没办法呀,毕竟有些是领导的意思。而且貌似我们虽然是分库了,但是只有一个服务器,我汗颜了,个人觉的这没多大用处。
现在我比较纠结的是怎么去实现这两个context 因为从EF映射的话 或多出来一套一样的域模型,这点表示很无解。要不就是一个context去然后换链接字符串,但是这样需要上层能很好的区别读写操作..
@Sky.Grain: 难道你不是codefirst??
@小眼睛老鼠: 如果是的话就好弄了... 悲催的项目是Database First
@Sky.Grain: so 对于这种有好的方案么?
@Sky.Grain:
我没用过dbfirst 主要是dbcontext的 连接字符串 读的连到一个库 写的连到一个库就可以了
那个模板只是一个结构吧 应该是可以动态修改连接字符串的吧 那个模板不是一个静态实例吧???
@Sky.Grain:
你把你创建 context的代码贴出来 我看看
@小眼睛老鼠: 给个联系方式细说~
@Sky.Grain:
qq:2240921114
@小眼睛老鼠: 已经加了 审核过一下
哈喽, 这个问题解决了吗? 我现在也在弄这个读写分离的方案, 能分享下你怎么实现的吗?
基于 EntityFramework 6 的读写分离方案,实施起来是很简单的
1、先建立起数据库的主从复制/备份机制,这个交由dbms来完成,不赘述;
2、在第 1 步的基础上,我们就有了 master 和 slave 数据库了;
3、在 EF 中,通过命令拦截器,可以在 EF 分别执行增删改和查询时改变 Connection 的 ConnectionString;具体的做法是写一个 DbCommandInterceptor 的子类,然后通过 DbInterception.Add 添加至程序上下文;
4、注意,在继承实现 DbCommandInterceptor 以调整连接字符串时,需要自行处理连接状态不同时的异常问题;
基于以上方案,是不需要两套 DbContext 的,一套即可;而且不用调整现有的任何数据处理代码;
当然,前提条件肯定是你的所有数据访问都是通过 EF 来完成的,哪怕是通过 DbContext.Database.SqlQuery/ExecuteSqlCommand 也是可以的。
但是有一种特殊情况,是基于EF读写分离可能会导致数据一致性错误的,就是在数据访问时(同时含增删改和查询操作)进行了事务的处理。
在这种情况下,如果所用数据库的事务机制及其ado.net provider不支持DbTransaction自动提升为分布式事务,将会导致数据一致性错误;并且在这种情况下,调用事务操作,必须用 TransactionScope而非 DbTransaction;
不过欣慰的是,目前的主流数据库及其ado.net provider,包括mssql、mysql、oracle,都支持数据库事务自动提升为分布式事务的。也就是说这个问题在大多数数据库场景下,都是不必担心的。
太棒了,我去找点资料看,不好意思了,因为很早已经结贴,没办法给你分。