ORM についての簡単な話
4 年前に学校で Ling to Sql に初めて触れたときのことを思い出します。その時、SQL ステートメントを手動で書かないことがいかに便利であるかをすぐに理解しました。 EF、Dapper、Hibernate、ServiceStack.OrmLiteなどの多くのORMフレームワークと接触しました。もちろん、各 ORM には独自の長所と短所があります。また、SqlSugar、Chloe.ORM、CYQ.Data など、自分が作成した ORM をオープンソース化している優秀な人々も数多くいます。これらのオープンソース ORM の使用法については話さないでください。私はこれらの偉大なマスターのソース コードをダウンロードして研究しましたが、少なくともオープンソースの精神という点では賞賛に値すると思います。
すべての ORM の究極のポイントは利便性であり、これによりプログラマーの反復作業が軽減されます。もちろん、プロジェクト全体で手書き SQL を使用するのは本当に多忙すぎると思います。 SQL の手書きを推奨していないわけではありませんが、最も基本的な追加、削除、変更、クエリをすべて手書きで行う場合、テストされるのは能力ではなく耐久力だと個人的には思います。手書き SQL は制御性が高く、パフォーマンスが高いと言う人もいます。私が言いたいのは、ORM でもそれを行うことができるかどうかです。
ORM の利点は非常に明白であり、開発に便利ですが、おそらくこの利点のせいで、多くの怠惰なプログラマーが SQL ステートメントの書き方を徐々に忘れてしまうのではないかと思います。私は、EF を使用して SQL を記述する多くのプログラマーの友人に会いました。個人的には、ビューやストアド プロシージャはまだ使用したくないと思っています。そうしないと、ある日、他人のプログラムに「exec xxxx」と書かれているのを見て、「ああ、どこかで見たような…」と突然思ってしまいます。そこで私が言いたいのは、「やはり行動すべき時は行動しなければならない」ということです。
Entity Frameworkについての簡単な話
Entity FrameworkはMicrosoftのORMフレームワークであり、継続的な改善と強化により、他のORMと比較して使用される割合は依然として高いと思います。私が現在最もよく使っているのは EF と Dapper です。実際、EF は長い年月を経て、成熟度やパフォーマンスが大幅に向上しており、EF の拡張機能を提供している開発者もたくさんいます。 エンティティフレームワークの拡張など。さらに、.net 開発者として、このプロジェクトは非常に多用途であり、多くの情報があり、この分野における Microsoft のアップデートも非常に強力です。ただし、リリースされたばかりの EF Core にはいくつかの落とし穴もあります。結局のところ、これは今後ますます改善されると思います。
Entity Frameworkには、コードファースト、データベースファースト、モデルファーストの3つの開発モードが用意されています。現在最も一般的に使用されているのは code first です。これら 3 つのモードの簡単な使い方と違いについては、この記事を参照してください。
何人かの友人が、EF のパフォーマンスが悪く、生成される SQL ステートメントが醜いと言っているのを聞きました。これを言う前に、コードをチェックするか、EF の記事をもっと読むべきだと思います。他人の欠点を責める前に、まず自分が穴を掘っていないかどうかを確認する必要があります。 EF やその他の ORM が使いにくいと本当に感じている場合は、自分で ORM を作成してください。私はかつて同僚と Dapper を使用して一般的な ORM を拡張しましたが、これは学習と使用の利便性を損なうものでした。
Entity Framework Universal Data Layer Class
EF ユニバーサル データ レイヤーの親クラス メソッドはここで提供されています。実際、インターネット上の多くの人が自分のプロジェクトで EF ユニバーサル データ レイヤーの親クラス メソッドを提供しているため、ここで提供されているものは次のとおりです。良い選択とは、誰もが学び、使用するのに便利な普遍的なクラスであるとしか言えません。具体的なコードは次のとおりです。データ層の一般操作クラス
public class DbContextFactory {public DbContext GetDbContext() {string key = typeof(DBContext.DbContextFactory).Name + "XJHDbContext"; DbContext dbContext = CallContext.GetData(key) as DbContext;if (dbContext == null) { dbContext = new XJHDbContext(); CallContext.SetData(key, dbContext); }return dbContext; } }DbBase

扩展类,实现读写分离
上面的通用类是比较基础简单通用的,适合于单库读写操作。对于EF实现读写分离,之前网上找过类似的参考文章,很多人文章都是使用 DbCommandInterceptor拦截器 来实现,具体的做法是通过拦截到sql语句,然后根据具体条件去判断是走主库还是从库。这种做法不是不行,只是个人感觉不是很好扩展,而且要在拦截器里面做限制判断。
其实说白了EF本身就是一个读写分离的orm。用过EF的人知道,EF提供访问数据库的是 DbContext 这个对象,所以想实现读写分离的就很简单了,只要在程序中使用两个不同的DbContext对象,一个负责读,一个负责写就好了。
所以在上面提供的通用封装类中稍微做下修改,修改如下DbContextFactory中获取DbContext的方法,实现一个读的DbContext和一个写的DbContext对象的获取。
这里要注意下,对于读的DbContext来说,要做下设置
1.使用 Database.SetInitializer(new NullDatabaseInitializer
2.重写 SaveChanges 方法,对应从库来说,只提供读取的功能,所以防止误操作,这里禁用掉SaveChanges方法,一般需要使用从读的保存方法,就对外抛出异常。
代码如下:
支持读写分离的 DbContextFactory 类
public class DbContextFactory {public DbContext GetWriteDbContext(){string key = typeof(DbContextFactory).Name + "WriteDbContext"; DbContext dbContext = CallContext.GetData(key) as DbContext;if (dbContext == null) { dbContext = new WriteDbContext(); CallContext.SetData(key, dbContext); }return dbContext; }public DbContext GetReadDbContext(){string key = typeof(DbContextFactory).Name + "ReadDbContext"; DbContext dbContext = CallContext.GetData(key) as DbContext;if (dbContext == null) { dbContext = new ReadDbContext(); CallContext.SetData(key, dbContext); }return dbContext; } }
对应的 DbBase 类也做下修改,主要将上面的Db对象改作 MasterDb 和 SlaveDb 对象,并且把上面的读写方法坐下调整,修改后如下:
支持读写分离的 DbBase类


public class DbBase {//是否读写分离(可以配置在配置文件中)private static readonly bool IsReadWriteSeparation = true;#region EF上下文对象(主库)protected DbContext MasterDb => _masterDb.Value;private readonly Lazy<dbcontext> _masterDb = new Lazy<dbcontext>(() => new DbContextFactory().GetWriteDbContext());#endregion EF上下文对象(主库)#region EF上下文对象(从库)protected DbContext SlaveDb => IsReadWriteSeparation ? _slaveDb.Value : _masterDb.Value;private readonly Lazy<dbcontext> _slaveDb = new Lazy<dbcontext>(() => new DbContextFactory().GetReadDbContext());#endregion EF上下文对象(从库)#region 自定义其他方法/// <summary>/// 执行存储过程或自定义sql语句--返回集合(自定义返回类型)/// </summary>/// <param>/// <param>/// <param>/// <returns></returns>public List<tmodel> Query<tmodel>(string sql, List<sqlparameter> parms, CommandType cmdType = CommandType.Text) {//存储过程(exec getActionUrlId @name,@ID)if (cmdType == CommandType.StoredProcedure) { StringBuilder paraNames = new StringBuilder();foreach (var sqlPara in parms) { paraNames.Append($" @{sqlPara},"); } sql = paraNames.Length > 0 ? $"exec {sql} {paraNames.ToString().Trim(',')}" : $"exec {sql} "; }var list = SlaveDb.Database.SqlQuery<tmodel>(sql, parms.ToArray());var enityList = list.ToList();return enityList; }/// <summary>/// 自定义语句和存储过程的增删改--返回影响的行数/// </summary>/// <param>/// <param>/// <param>/// <returns></returns>public int Execute(string sql, List<sqlparameter> parms, CommandType cmdType = CommandType.Text) {//存储过程(exec getActionUrlId @name,@ID)if (cmdType == CommandType.StoredProcedure) { StringBuilder paraNames = new StringBuilder();foreach (var sqlPara in parms) { paraNames.Append($" @{sqlPara},"); } sql = paraNames.Length > 0 ?$"exec {sql} {paraNames.ToString().Trim(',')}" : $"exec {sql} "; }int ret = MasterDb.Database.ExecuteSqlCommand(sql, parms.ToArray());return ret; }#endregion 自定义其他方法}/// <summary>/// mssql数据库 数据层 父类/// </summary>/// <typeparam></typeparam>public class DbBase<t> : DbBase where T : class, new() {#region INSERT/// <summary>/// 新增 实体/// </summary>/// <param>/// <returns></returns>public void Insert(T model) { MasterDb.Set<t>().Add(model); }/// <summary>/// 普通批量插入/// </summary>/// <param>public void InsertRange(List<t> datas) { MasterDb.Set<t>().AddRange(datas); }#endregion INSERT#region DELETE/// <summary>/// 根据模型删除/// </summary>/// <param>包含要删除id的对象/// <returns></returns>public void Delete(T model) { MasterDb.Set<t>().Attach(model); MasterDb.Set<t>().Remove(model); }/// <summary>/// 删除/// </summary>/// <param>public void Delete(Expression<func>> whereLambda) { MasterDb.Set<t>().Where(whereLambda).Delete(); }#endregion DELETE#region UPDATE/// <summary>/// 单个对象指定列修改/// </summary>/// <param>要修改的实体对象/// <param>要修改的 属性 名称/// <param>/// <returns></returns>public void Update(T model, List<string> proNames, bool isProUpdate = true) {//将 对象 添加到 EF中MasterDb.Set<t>().Attach(model);var setEntry = ((IObjectContextAdapter)MasterDb).ObjectContext.ObjectStateManager.GetObjectStateEntry(model);//指定列修改if (isProUpdate) {foreach (string proName in proNames) { setEntry.SetModifiedProperty(proName); } }//忽略类修改else{ Type t = typeof(T); List<propertyinfo> proInfos = t.GetProperties(BindingFlags.Instance | BindingFlags.Public).ToList();foreach (var item in proInfos) {string proName = item.Name;if (proNames.Contains(proName)) {continue; } setEntry.SetModifiedProperty(proName); } } }/// <summary>/// 单个对象修改/// </summary>/// <param>/// <returns></returns>public void Update(T model) { DbEntityEntry entry = MasterDb.Entry<t>(model); MasterDb.Set<t>().Attach(model); entry.State = EntityState.Modified; }/// <summary>/// 批量修改/// </summary>/// <param>/// <param>public void Update(Expression<func>> whereLambda, Expression<func>> updateExpression) { MasterDb.Set<t>().Where(whereLambda).Update(updateExpression); }/// <summary>/// 批量修改/// </summary>/// <param>/// <returns></returns>public void UpdateAll(List<t> models) {foreach (var model in models) { DbEntityEntry entry = MasterDb.Entry(model); entry.State = EntityState.Modified; } }/// <summary>/// 批量统一修改/// </summary>/// <param>要修改的实体对象/// <param>查询条件/// <param>/// <returns></returns>public void Update(T model, Expression<func>> whereLambda, params string[] modifiedProNames) {//查询要修改的数据List<t> listModifing = MasterDb.Set<t>().Where(whereLambda).ToList(); Type t = typeof(T); List<propertyinfo> proInfos = t.GetProperties(BindingFlags.Instance | BindingFlags.Public).ToList(); Dictionary<string> dictPros = new Dictionary<string>(); proInfos.ForEach(p =>{if (modifiedProNames.Contains(p.Name)) { dictPros.Add(p.Name, p); } });if (dictPros.Count /// 根据主键查询/// /// <param>/// <returns></returns>public T FindById(dynamic id) {return SlaveDb.Set<t>().Find(id); }/// <summary>/// 获取默认一条数据,没有则为NULL/// </summary>/// <param>/// <returns></returns>public T FirstOrDefault(Expression<func>> whereLambda = null) {if (whereLambda == null) {return SlaveDb.Set<t>().FirstOrDefault(); }return SlaveDb.Set<t>().FirstOrDefault(whereLambda); }/// <summary>/// 获取全部数据/// </summary>/// <returns></returns>public List<t> GetAll(string ordering = null) {return ordering == null? SlaveDb.Set<t>().ToList() : SlaveDb.Set<t>().OrderBy(ordering).ToList(); }/// <summary>/// 带条件查询获取数据/// </summary>/// <param>/// <param>/// <returns></returns>public List<t> GetAll(Expression<func>> whereLambda, string ordering = null) {var iQueryable = SlaveDb.Set<t>().Where(whereLambda);return ordering == null? iQueryable.ToList() : iQueryable.OrderBy(ordering).ToList(); }/// <summary>/// 带条件查询获取数据/// </summary>/// <param>/// <returns></returns>public IQueryable<t> GetAllIQueryable(Expression<func>> whereLambda = null) {return whereLambda == null ? SlaveDb.Set<t>() : SlaveDb.Set<t>().Where(whereLambda); }/// <summary>/// 获取数量/// </summary>/// <param>/// <returns></returns>public int GetCount(Expression<func>> whereLambd = null) {return whereLambd == null ? SlaveDb.Set<t>().Count() : SlaveDb.Set<t>().Where(whereLambd).Count(); }/// <summary>/// 判断对象是否存在/// </summary>/// <param>/// <returns></returns>public bool Any(Expression<func>> whereLambd) {return SlaveDb.Set<t>().Where(whereLambd).Any(); }/// <summary>/// 分页查询/// </summary>/// <param>当前页码/// <param>每页大小/// <param>总条数/// <param>排序条件(一定要有)/// <param>查询添加(可有,可无)/// <param>是否是Order排序/// <returns></returns>public List<t> Page<tkey>(int pageIndex, int pageSize, out int rows, Expression<func>> orderBy, Expression<func>> whereLambda = null, bool isOrder = true) { IQueryable<t> data = isOrder ?SlaveDb.Set<t>().OrderBy(orderBy) : SlaveDb.Set<t>().OrderByDescending(orderBy);if (whereLambda != null) { data = data.Where(whereLambda); } rows = data.Count();return data.PageBy((pageIndex - 1) * pageSize, pageSize).ToList(); }/// <summary>/// 分页查询/// </summary>/// <param>当前页码/// <param>每页大小/// <param>总条数/// <param>排序条件(一定要有)/// <param>查询添加(可有,可无)/// <returns></returns>public List<t> Page(int pageIndex, int pageSize, out int rows, string ordering, Expression<func>> whereLambda = null) {// 分页 一定注意: Skip 之前一定要 OrderByvar data = SlaveDb.Set<t>().OrderBy(ordering);if (whereLambda != null) { data = data.Where(whereLambda); } rows = data.Count();return data.PageBy((pageIndex - 1) * pageSize, pageSize).ToList(); }/// <summary>/// 查询转换/// </summary>/// <typeparam></typeparam>/// <param>/// <returns></returns>public List<tdto> Select<tdto>(Expression<func>> whereLambda) {return SlaveDb.Set<t>().Where(whereLambda).Select<tdto>().ToList(); }#endregion SELECT#region ORTHER/// <summary>/// 执行存储过程或自定义sql语句--返回集合/// </summary>/// <param>/// <param>/// <param>/// <returns></returns>public List<t> Query(string sql, List<sqlparameter> parms, CommandType cmdType = CommandType.Text) {return Query<t>(sql, parms, cmdType); }/// <summary>/// 提交保存/// </summary>/// <returns></returns>public int SaveChanges() {return MasterDb.SaveChanges(); }/// <summary>/// 回滚/// </summary>public void RollBackChanges() {var items = MasterDb.ChangeTracker.Entries().ToList(); items.ForEach(o => o.State = EntityState.Unchanged); }#endregion ORTHER}</t></sqlparameter></t></tdto></t></func></tdto></tdto></t></func></t></t></t></t></func></func></tkey></t></t></func></t></t></func></t></t></func></t></t></func></t></t></t></t></t></t></func></t></string></string></propertyinfo></t></t></func></t></t></func></func></t></t></propertyinfo></t></string></t></func></t></t></t></t></t></t></sqlparameter></tmodel></sqlparameter></tmodel></tmodel></dbcontext></dbcontext></dbcontext></dbcontext>
这样简单的读写分离就实现了,实现逻辑也比较清晰,方便扩展。
进一步改造,实现多从库读取
一般做读写分离,都会做一主多从,特别对读取量比较大的项目,这样多库读取就能减轻读库的压力。所以对于上面的方法,做下改造。
上面可以看到,主库和从库都是通过 DbContextFactory 这个类来获取的,在GetReadDbContext 方法中每次都是获取 ReadDbContext 这个对象。那么对于多个从库的情况下,每次读取到底要去哪个库读取数据呢?这里就是一个算法规则的问题了,或者说是策略吧,如果使用过nginx的朋友就知道,nginx本身内部在实现负载均衡的时候提供了多种策略,比如轮询,加权轮询,ip_hash等策略。其实上面获取同一个ReadDbContext 的方法也算一种策略,叫单一策略,每次都获取单一的对象。
多从库的情况下,我们简单的来实现另一种获取策略,随机策略,每次都随机获取到一个从库的对象,这种是最简单的策略,当然,正式使用的话大家可以发挥自己的创造力,写出多了的算法策略。
首先,定义一个策略接口,方便策略的扩展和切换,代码如下:
IReadDbStrategy 接口
/// <summary> /// 从数据库获取策略接口 /// </summary> public interface IReadDbStrategy { /// <summary> /// 获取读库 /// </summary> /// <returns></returns> DbContext GetDbContext(); }
单从库情况下,定义一个单一策略,代码如下:
单一策略
/// <summary> /// 单一策略 /// </summary> public class SingleStrategy : IReadDbStrategy { public DbContext GetDbContext() { return new ReadDbContext(); } }
多从库情况下,定义一个随机策略,代码如下:
随机策略
/// <summary>/// 随机策略/// </summary>public class RandomStrategy : IReadDbStrategy {//所有读库类型public static List<type> DbTypes; static RandomStrategy() { LoadDbs(); } //加载所有的读库类型static void LoadDbs() { DbTypes = new List<type>();var assembly = Assembly.GetExecutingAssembly();var types = assembly.GetTypes();foreach (var type in types) {if (type.BaseType == typeof(BaseReadDbContext)) { DbTypes.Add(type); } } } public DbContext GetDbContext() {int randomIndex = new Random().Next(0, DbTypes.Count);var dbType = DbTypes[randomIndex];var dbContext = Activator.CreateInstance(dbType) as DbContext;return dbContext; } }</type></type>
这样,所有从库我们都基于策略去获取,扩展也比较方便。修改下 DbContextFactory 类的 GetReadDbContext 方法,通过策略接口来获取,代码如下:
支持一主多从的 DbContextFactory 类
public class DbContextFactory {//todo:这里可以自己通过注入的方式来实现,就会更加灵活private static readonly IReadDbStrategy ReadDbStrategy = new RandomStrategy();public DbContext GetWriteDbContext() {string key = typeof(DbContextFactory).Name + "WriteDbContext"; DbContext dbContext = CallContext.GetData(key) as DbContext;if (dbContext == null) { dbContext = new WriteDbContext(); CallContext.SetData(key, dbContext); }return dbContext; }public DbContext GetReadDbContext() {string key = typeof(DbContextFactory).Name + "ReadDbContext"; DbContext dbContext = CallContext.GetData(key) as DbContext;if (dbContext == null) { dbContext = ReadDbStrategy.GetDbContext();CallContext.SetData(key, dbContext); }return dbContext; } }
这样简单的一主多从也实现了。
参考文章
源码分享
所有的代码提供给大家的更多的是一种思路和学习的参考,如果有什么不足的地方也欢迎大家批评指正,如果觉得对你有帮助,不要吝啬你的鼠标,帮忙点个星,点个赞吧。
以上がEF 一般データ層カプセル化クラスの例の詳細な説明の概要の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

C#と.NETは、Web、デスクトップ、モバイル開発に適しています。 1)Web開発では、ASP.Netcoreがクロスプラットフォーム開発をサポートしています。 2)デスクトップ開発では、さまざまなニーズに適したWPFとWINFORMSを使用します。 3)モバイル開発は、Xamarinを介したクロスプラットフォームアプリケーションを実現します。

C#.NETエコシステムは、開発者がアプリケーションを効率的に構築できるようにするための豊富なフレームワークとライブラリを提供します。 1.ASP.NETCOREは、高性能Webアプリケーションの構築に使用されます。2.EntityFrameWorkCoreは、データベース操作に使用されます。これらのツールの使用とベストプラクティスを理解することにより、開発者はアプリケーションの品質とパフォーマンスを向上させることができます。

c#.netアプリをAzureまたはAWSに展開する方法は?答えは、AzureAppServiceとAwselasticBeanStalkを使用することです。 1。Azureでは、AzureAppServiceとAzurePipelinesを使用して展開を自動化します。 2。AWSでは、Amazon ElasticBeanstalkとAwslambdaを使用して、展開とサーバーレス計算を実装します。

C#と.NETの組み合わせにより、開発者に強力なプログラミング環境を提供します。 1)C#は、多型と非同期プログラミングをサポートします。2).NETは、クロスプラットフォーム機能と同時処理メカニズムを提供し、デスクトップ、Web、モバイルアプリケーション開発で広く使用されています。

.NetFrameworkはソフトウェアフレームワークであり、C#はプログラミング言語です。 1..netframeworkは、デスクトップ、Web、モバイルアプリケーションの開発をサポートするライブラリとサービスを提供します。 2.C#は.NetFrameWork用に設計されており、最新のプログラミング機能をサポートしています。 3..NetFrameworkはCLRを介してコード実行を管理し、C#コードはILにコンパイルされ、CLRによって実行されます。 4. .NetFrameWorkを使用してアプリケーションをすばやく開発し、C#はLINQなどの高度な関数を提供します。 5.一般的なエラーには、タイプ変換と非同期プログラミングデッドロックが含まれます。 VisualStudioツールは、デバッグに必要です。

C#は、Microsoftが開発した最新のオブジェクト指向プログラミング言語であり、.NETはMicrosoftが提供する開発フレームワークです。 C#は、CのパフォーマンスとJavaのシンプルさを組み合わせており、さまざまなアプリケーションの構築に適しています。 .NETフレームワークは、複数の言語をサポートし、ガベージコレクションメカニズムを提供し、メモリ管理を簡素化します。

C#と.NETランタイムは密接に連携して、開発者に効率的で強力なプラットフォームの開発機能に力を与えます。 1)C#は、.NETフレームワークとシームレスに統合するように設計されたタイプセーフおよびオブジェクト指向のプログラミング言語です。 2).NETランタイムは、C#コードの実行を管理し、ガベージコレクション、タイプの安全性、その他のサービスを提供し、効率的でクロスプラットフォームの操作を保証します。

C#.NET開発を開始するには、次のことが必要です。1。C#の基本的な知識と.NETフレームワークのコア概念を理解する。 2。変数、データ型、制御構造、関数、クラスの基本概念をマスターします。 3。LINQや非同期プログラミングなど、C#の高度な機能を学習します。 4.一般的なエラーのためのデバッグテクニックとパフォーマンス最適化方法に精通してください。これらの手順を使用すると、C#.NETの世界に徐々に浸透し、効率的なアプリケーションを書き込むことができます。


ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

Video Face Swap
完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

人気の記事

ホットツール

SublimeText3 英語版
推奨: Win バージョン、コードプロンプトをサポート!

ZendStudio 13.5.1 Mac
強力な PHP 統合開発環境

MinGW - Minimalist GNU for Windows
このプロジェクトは osdn.net/projects/mingw に移行中です。引き続きそこでフォローしていただけます。 MinGW: GNU Compiler Collection (GCC) のネイティブ Windows ポートであり、ネイティブ Windows アプリケーションを構築するための自由に配布可能なインポート ライブラリとヘッダー ファイルであり、C99 機能をサポートする MSVC ランタイムの拡張機能が含まれています。すべての MinGW ソフトウェアは 64 ビット Windows プラットフォームで実行できます。

SAP NetWeaver Server Adapter for Eclipse
Eclipse を SAP NetWeaver アプリケーション サーバーと統合します。

AtomエディタMac版ダウンロード
最も人気のあるオープンソースエディター

ホットトピック









