ASP.NET 6 使用工作单元操作 MongoDB
<p>大家好,我是Edison。</p><p>最近工作中需要用到MongoDB的事务操作,因此参考了一些资料封装了一个小的组件,提供基础的CRUD Repository基类 和 UnitOfWork工作单元模式。今天,就来简单介绍一下这个小组件。</p>
<h1><strong><strong>关于MongoDB的事务</strong></strong></h1>
<p>MongoDB在4.2版本开始全面支持了多文档事务,至今已过了四年了,虽然我们可能没有在项目中用MongoDB来替代传统关系型数据库如MySQL/SQL Server,但是不能否认MongoDB已经在事务能力上愈发成熟了。</p>
<p><img src="https://img2023.cnblogs.com/blog/381412/202307/381412-20230716112726622-1436634897.png" alt="" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></p>
<p>在MongoDB中,所谓的事务主要指的是多个文档的事务,其使用方式和传统关系型数据库差不多。但我们需要注意的是:<strong>多文档事务只能应用在副本集 或 mongos 节点上</strong>。如果你只是一个单点的mongo实例,是无法进行多文档事务实践的。</p>
<p><strong>画外音:</strong>如果你对MongoDB感兴趣,不妨看看我的这个系列博客:《MongoDB入门到实践学习之旅》</p>
<p>那么,如何快速进行事务操作呢?</p>
<h2><strong><strong>在Mongo Shell中进行事务</strong></strong></h2>
<p>下面演示了如何通过Mongo Shell来进行一个多文档操作的事务提交:</p>
<div class="cnblogs_code">
<pre>var session =<span style="color: rgba(0, 0, 0, 1)"> db.getMongo().startSession();
session.startTransaction({readConcern: { level: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">majority</span><span style="color: rgba(128, 0, 0, 1)">'</span> },writeConcern: { <span style="color: rgba(0, 0, 255, 1)">w</span>: <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">majority</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)"> }});
var coll1 </span>= session.getDatabase(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">students</span><span style="color: rgba(128, 0, 0, 1)">'</span>).getCollection(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">teams</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">);
coll1.update({name: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">yzw-football-team</span><span style="color: rgba(128, 0, 0, 1)">'</span>}, {$set: {members: <span style="color: rgba(128, 0, 128, 1)">20</span><span style="color: rgba(0, 0, 0, 1)">}});
var coll2 </span>= session.getDatabase(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">students</span><span style="color: rgba(128, 0, 0, 1)">'</span>).getCollection(<span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">records</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">);
coll1.update({name: </span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">Edison</span><span style="color: rgba(128, 0, 0, 1)">'</span>}, {$set: {gender: <span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(128, 0, 0, 1)">Female</span><span style="color: rgba(128, 0, 0, 1)">'</span><span style="color: rgba(0, 0, 0, 1)">}});
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 成功提交事务</span>
<span style="color: rgba(0, 0, 0, 1)">session.commitTransaction();
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 失败事务回滚</span>
session.abortTransaction();</pre>
</div>
<h2><strong><strong>在.NET应用中进行事务</strong></strong></h2>
<p>下面展示了在.NET应用中通过MongoDB Driver来进行事务的示例:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">using</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> clientSession =<span style="color: rgba(0, 0, 0, 1)"> mongoClient.StartSession())
{
</span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">
{
</span><span style="color: rgba(0, 0, 255, 1)">var</span> contacts = clientSession.Client.GetDatabase(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">testDB</span><span style="color: rgba(128, 0, 0, 1)">"</span>).GetCollection<Contact>(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">contacts</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
contacts.ReplaceOne(contact </span>=> contact.Id == <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">1234455</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, contact);
</span><span style="color: rgba(0, 0, 255, 1)">var</span> books = clientSession.Client.GetDatabase(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">testDB</span><span style="color: rgba(128, 0, 0, 1)">"</span>).GetCollection<Book>(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">books</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
books.DeleteOne(book </span>=> book.Id == <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">1234455</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
clientSession.CommitTransaction();
}
</span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception ex)
{
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> to do some logging</span>
<span style="color: rgba(0, 0, 0, 1)"> clientSession.AbortTransaction();
}
}</span></pre>
</div>
<p>在大部分的实际应用中,我们通常都习惯使用数据仓储(Repository)的模式来进行CRUD,同时也习惯用工作单元(UnitOfWork)模式来进行协调多个Repository进行事务提交。那么,如何在自己的项目中实现这个呢?</p>
<p>参考了一些资料后,自己实现了一个基础小组件,暂且叫它:<em>EDT.MongoProxy</em>吧,我们来看看它是如何实现的。</p>
<h1><strong><strong>单例的MongoClient</strong></strong></h1>
<p>基于MongoDB的最佳时间,对于MongoClient最好设置为<strong>单例注入</strong>,因为在MongoDB.Driver中MongoClient已经被设计为线程安全可以被多线程共享,这样可还以避免反复实例化MongoClient带来的开销,避免在极端情况下的性能低下。</p>
<p>这里暂且设计一个MongoDbConnection类,用于包裹这个MongoClient,然后将其以单例模式注入IoC容器中。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> MongoDbConnection : IMongoDbConnection
{
</span><span style="color: rgba(0, 0, 255, 1)">public</span> IMongoClient DatabaseClient { <span style="color: rgba(0, 0, 255, 1)">get</span><span style="color: rgba(0, 0, 0, 1)">; }
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">string</span> DatabaseName { <span style="color: rgba(0, 0, 255, 1)">get</span><span style="color: rgba(0, 0, 0, 1)">; }
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> MongoDbConnection(MongoDatabaseConfigs configs, IConfiguration configuration)
{
DatabaseClient </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> MongoClient(configs.GetMongoClientSettings(configuration));
DatabaseName </span>=<span style="color: rgba(0, 0, 0, 1)"> configs.DatabaseName;
}
}</span></pre>
</div>
<p>其中,这个MongoDatabaseConfigs类主要是获取appsettings中的配置,用以生成MongoClient的对应Settings。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">* Config Example
"MongoDatabaseConfigs": {
"Servers": "xxx01.edisontalk.net,xxx02.edisontalk.net,xxx03.edisontalk.net",
"Port": 27017,
"ReplicaSetName": "edt-replica",
"DatabaseName": "EDT_Practices",
"AuthDatabaseName": "admin",
"ApplicationName": "Todo",
"UserName": "service_testdev",
"Password": "xxxxxxxxxxxxxxxxxxxxxxxx",
"UseTLS": true,
"AllowInsecureTLS": true,
"SslCertificatePath": "/etc/pki/tls/certs/EDT_CA.cer",
"UseEncryption": true
}
*</span><span style="color: rgba(0, 128, 0, 1)">*/</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> MongoDatabaseConfigs
{
</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">const</span> <span style="color: rgba(0, 0, 255, 1)">string</span> DEFAULT_AUTH_DB = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">admin</span><span style="color: rgba(128, 0, 0, 1)">"</span>; <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Default AuthDB: admin</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">string</span> Servers { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span> Port { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span>; } = <span style="color: rgba(128, 0, 128, 1)">27017</span>; <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Default Port: 27017</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">string</span> ReplicaSetName { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">string</span> DatabaseName { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">string</span> DefaultCollectionName { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span>; } = <span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">.Empty;
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">string</span> ApplicationName { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">string</span> UserName { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">string</span> Password { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">string</span> AuthDatabaseName { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span>; } = DEFAULT_AUTH_DB; <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Default AuthDB: admin</span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">string</span> CustomProperties { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span>; } = <span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">.Empty;
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">bool</span> UseTLS { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span>; } = <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">bool</span> AllowInsecureTLS { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span>; } = <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">string</span> SslCertificatePath { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span>; } = <span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">.Empty;
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">bool</span> StoreCertificateInKeyStore { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span>; } = <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">public</span> MongoClientSettings GetMongoClientSettings(IConfiguration configuration = <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
{
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">.IsNullOrWhiteSpace(Servers))
</span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> ArgumentNullException(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Mongo Servers Configuration is Missing!</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">string</span>.IsNullOrWhiteSpace(UserName) || <span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">.IsNullOrWhiteSpace(Password))
</span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> ArgumentNullException(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Mongo Account Configuration is Missing!</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Base Configuration</span>
MongoClientSettings settings = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> MongoClientSettings
{
ApplicationName </span>=<span style="color: rgba(0, 0, 0, 1)"> ApplicationName,
ReplicaSetName </span>=<span style="color: rgba(0, 0, 0, 1)"> ReplicaSetName
};
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Credential</span>
settings.Credential =<span style="color: rgba(0, 0, 0, 1)"> MongoCredential.CreateCredential(AuthDatabaseName, UserName, Password);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Servers</span>
<span style="color: rgba(0, 0, 255, 1)">var</span> mongoServers = Servers.Split(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">,</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">, StringSplitOptions.RemoveEmptyEntries).ToList();
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (mongoServers.Count == <span style="color: rgba(128, 0, 128, 1)">1</span>) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Standalone</span>
<span style="color: rgba(0, 0, 0, 1)"> {
settings.Server </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> MongoServerAddress(mongoServers.First(), Port);
settings.DirectConnection </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
}
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (mongoServers.Count > <span style="color: rgba(128, 0, 128, 1)">1</span>) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Cluster</span>
<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 0, 255, 1)">var</span> mongoServerAddresses = <span style="color: rgba(0, 0, 255, 1)">new</span> List<MongoServerAddress><span style="color: rgba(0, 0, 0, 1)">();
</span><span style="color: rgba(0, 0, 255, 1)">foreach</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> mongoServer <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> mongoServers)
{
</span><span style="color: rgba(0, 0, 255, 1)">var</span> mongoServerAddress = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> MongoServerAddress(mongoServer, Port);
mongoServerAddresses.Add(mongoServerAddress);
}
settings.Servers </span>=<span style="color: rgba(0, 0, 0, 1)"> mongoServerAddresses;
settings.DirectConnection </span>= <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> SSL</span>
<span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (UseTLS)
{
settings.UseTls </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
settings.AllowInsecureTls </span>=<span style="color: rgba(0, 0, 0, 1)"> AllowInsecureTLS;
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">.IsNullOrWhiteSpace(SslCertificatePath))
</span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> ArgumentNullException(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">SslCertificatePath is Missing!</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
</span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (StoreCertificateInKeyStore)
{
</span><span style="color: rgba(0, 0, 255, 1)">var</span> localTrustStore = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> X509Store(StoreName.Root);
</span><span style="color: rgba(0, 0, 255, 1)">var</span> certificateCollection = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> X509Certificate2Collection();
certificateCollection.Import(SslCertificatePath);
</span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">
{
localTrustStore.Open(OpenFlags.ReadWrite);
localTrustStore.AddRange(certificateCollection);
}
</span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception ex)
{
</span><span style="color: rgba(0, 0, 255, 1)">throw</span><span style="color: rgba(0, 0, 0, 1)">;
}
</span><span style="color: rgba(0, 0, 255, 1)">finally</span><span style="color: rgba(0, 0, 0, 1)">
{
localTrustStore.Close();
}
}
</span><span style="color: rgba(0, 0, 255, 1)">var</span> certs = <span style="color: rgba(0, 0, 255, 1)">new</span> List<X509Certificate> { <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> X509Certificate2(SslCertificatePath) };
settings.SslSettings </span>= <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> SslSettings();
settings.SslSettings.ClientCertificates </span>=<span style="color: rgba(0, 0, 0, 1)"> certs;
settings.SslSettings.EnabledSslProtocols </span>=<span style="color: rgba(0, 0, 0, 1)"> System.Security.Authentication.SslProtocols.Tls13;
}
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> settings;
}
}</span></pre>
</div>
<h1><strong><strong>核心部分:MongoDbContext</strong></strong></h1>
<p>这里我们主要仿照DbContext的设计,设计一个MongoDbContext,它从IoC容器中获取到单例的MongoClient,封装了事务的开启和提交,简化了应用代码的编写。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> MongoDbContext : IMongoDbContext
{
</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span><span style="color: rgba(0, 0, 0, 1)"> IMongoDatabase _database;
</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span><span style="color: rgba(0, 0, 0, 1)"> IMongoClient _mongoClient;
</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span> IList<Func<IClientSessionHandle, Task>><span style="color: rgba(0, 0, 0, 1)"> _commands
</span>= <span style="color: rgba(0, 0, 255, 1)">new</span> List<Func<IClientSessionHandle, Task>><span style="color: rgba(0, 0, 0, 1)">();
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> MongoDbContext(IMongoDbConnection dbClient)
{
_mongoClient </span>=<span style="color: rgba(0, 0, 0, 1)"> dbClient.DatabaseClient;
_database </span>=<span style="color: rgba(0, 0, 0, 1)"> _mongoClient.GetDatabase(dbClient.DatabaseName);
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> AddCommand(Func<IClientSessionHandle, Task><span style="color: rgba(0, 0, 0, 1)"> func)
{
_commands.Add(func);
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task AddCommandAsync(Func<IClientSessionHandle, Task><span style="color: rgba(0, 0, 0, 1)"> func)
{
_commands.Add(func);
</span><span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> Task.CompletedTask;
}
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><summary></span>
<span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)"> NOTES: Only works in Cluster mode
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"></summary></span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> Commit(IClientSessionHandle session)
{
</span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">
{
session.StartTransaction();
</span><span style="color: rgba(0, 0, 255, 1)">foreach</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> command <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> _commands)
{
command(session);
}
session.CommitTransaction();
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> _commands.Count;
}
</span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception ex)
{
session.AbortTransaction();
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">;
}
</span><span style="color: rgba(0, 0, 255, 1)">finally</span><span style="color: rgba(0, 0, 0, 1)">
{
_commands.Clear();
}
}
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><summary></span>
<span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)"> NOTES: Only works in Cluster mode
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"></summary></span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task<<span style="color: rgba(0, 0, 255, 1)">int</span>><span style="color: rgba(0, 0, 0, 1)"> CommitAsync(IClientSessionHandle session)
{
</span><span style="color: rgba(0, 0, 255, 1)">try</span><span style="color: rgba(0, 0, 0, 1)">
{
session.StartTransaction();
</span><span style="color: rgba(0, 0, 255, 1)">foreach</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> command <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> _commands)
{
</span><span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> command(session);
}
</span><span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> session.CommitTransactionAsync();
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> _commands.Count;
}
</span><span style="color: rgba(0, 0, 255, 1)">catch</span><span style="color: rgba(0, 0, 0, 1)"> (Exception ex)
{
</span><span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> session.AbortTransactionAsync();
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">;
}
</span><span style="color: rgba(0, 0, 255, 1)">finally</span><span style="color: rgba(0, 0, 0, 1)">
{
_commands.Clear();
}
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> IClientSessionHandle StartSession()
{
</span><span style="color: rgba(0, 0, 255, 1)">var</span> session =<span style="color: rgba(0, 0, 0, 1)"> _mongoClient.StartSession();
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> session;
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task<IClientSessionHandle><span style="color: rgba(0, 0, 0, 1)"> StartSessionAsync()
{
</span><span style="color: rgba(0, 0, 255, 1)">var</span> session = <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> _mongoClient.StartSessionAsync();
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> session;
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> IMongoCollection<T> GetCollection<T>(<span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> name)
{
</span><span style="color: rgba(0, 0, 255, 1)">return</span> _database.GetCollection<T><span style="color: rgba(0, 0, 0, 1)">(name);
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> Dispose()
{
GC.SuppressFinalize(</span><span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">);
}
}</span></pre>
</div>
<h1><strong><strong>数据仓储:MongoRepositoryBase</strong></strong></h1>
<p>在实际项目中,我们都希望有一个基础的RepositoryBase类,将CRUD的方法都封装了,我们实际中就只需要创建一个对应的Repository集成这个RepositoryBase就行了,无需再重复编写CRUD的方法。那么,也就有了这个MongoRepositoryBase类:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span> MongoRepositoryBase<TEntity> : IMongoRepositoryBase<TEntity>
<span style="color: rgba(0, 0, 255, 1)">where</span> TEntity : MongoEntityBase, <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)">()
{
</span><span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span><span style="color: rgba(0, 0, 0, 1)"> IMongoDbContext _dbContext;
</span><span style="color: rgba(0, 0, 255, 1)">protected</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span> IMongoCollection<TEntity><span style="color: rgba(0, 0, 0, 1)"> _dbSet;
</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span> <span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)"> _collectionName;
</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">const</span> <span style="color: rgba(0, 0, 255, 1)">string</span> _keyField = <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">_id</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> MongoRepositoryBase(IMongoDbContext mongoDbContext)
{
_dbContext </span>=<span style="color: rgba(0, 0, 0, 1)"> mongoDbContext;
_collectionName </span>= <span style="color: rgba(0, 0, 255, 1)">typeof</span>(TEntity).GetAttributeValue((TableAttribute m) =><span style="color: rgba(0, 0, 0, 1)"> m.Name)
</span>?? <span style="color: rgba(0, 0, 255, 1)">typeof</span><span style="color: rgba(0, 0, 0, 1)">(TEntity).Name;
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">string</span><span style="color: rgba(0, 0, 0, 1)">.IsNullOrWhiteSpace(_collectionName))
</span><span style="color: rgba(0, 0, 255, 1)">throw</span> <span style="color: rgba(0, 0, 255, 1)">new</span> ArgumentNullException(<span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Mongo DatabaseName can't be NULL! Please set the attribute Table in your entity class.</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">);
_dbSet </span>= mongoDbContext.GetCollection<TEntity><span style="color: rgba(0, 0, 0, 1)">(_collectionName);
}
</span><span style="color: rgba(0, 0, 255, 1)">#region</span> Create Part
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task AddAsync(TEntity entity, IClientSessionHandle session = <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
{
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (session == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> _dbSet.InsertOneAsync(entity);
</span><span style="color: rgba(0, 0, 255, 1)">else</span>
<span style="color: rgba(0, 0, 255, 1)">await</span> _dbContext.AddCommandAsync(<span style="color: rgba(0, 0, 255, 1)">async</span> (session) => <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> _dbSet.InsertOneAsync(entity));
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task AddManyAsync(IEnumerable<TEntity> entityList, IClientSessionHandle session = <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
{
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (session == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> _dbSet.InsertManyAsync(entityList);
</span><span style="color: rgba(0, 0, 255, 1)">else</span>
<span style="color: rgba(0, 0, 255, 1)">await</span> _dbContext.AddCommandAsync(<span style="color: rgba(0, 0, 255, 1)">async</span> (session) => <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> _dbSet.InsertManyAsync(entityList));
}
</span><span style="color: rgba(0, 0, 255, 1)">#endregion</span><span style="color: rgba(0, 0, 0, 1)">
# region Delete Part
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task DeleteAsync(<span style="color: rgba(0, 0, 255, 1)">string</span> id, IClientSessionHandle session = <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
{
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (session == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">await</span> _dbSet.DeleteOneAsync(Builders<TEntity>.Filter.Eq(_keyField, <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ObjectId(id)));
</span><span style="color: rgba(0, 0, 255, 1)">else</span>
<span style="color: rgba(0, 0, 255, 1)">await</span> _dbContext.AddCommandAsync(<span style="color: rgba(0, 0, 255, 1)">async</span> (session) => <span style="color: rgba(0, 0, 255, 1)">await</span> _dbSet.DeleteOneAsync(Builders<TEntity>.Filter.Eq(_keyField, <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ObjectId(id))));
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task DeleteAsync(Expression<Func<TEntity, <span style="color: rgba(0, 0, 255, 1)">bool</span>>> expression, IClientSessionHandle session = <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
{
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (session == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> _dbSet.DeleteOneAsync(expression);
</span><span style="color: rgba(0, 0, 255, 1)">else</span>
<span style="color: rgba(0, 0, 255, 1)">await</span> _dbContext.AddCommandAsync(<span style="color: rgba(0, 0, 255, 1)">async</span> (session) => <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> _dbSet.DeleteOneAsync(expression));
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task<DeleteResult> DeleteManyAsync(FilterDefinition<TEntity> filter, IClientSessionHandle session = <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
{
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (session == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> _dbSet.DeleteManyAsync(filter);
</span><span style="color: rgba(0, 0, 255, 1)">await</span> _dbContext.AddCommandAsync(<span style="color: rgba(0, 0, 255, 1)">async</span> (session) => <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> _dbSet.DeleteManyAsync(filter));
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span> DeleteResult.Acknowledged(<span style="color: rgba(128, 0, 128, 1)">10</span><span style="color: rgba(0, 0, 0, 1)">);
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task<DeleteResult> DeleteManyAsync(Expression<Func<TEntity, <span style="color: rgba(0, 0, 255, 1)">bool</span>>> expression, IClientSessionHandle session = <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
{
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (session == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> _dbSet.DeleteManyAsync(expression);
</span><span style="color: rgba(0, 0, 255, 1)">await</span> _dbContext.AddCommandAsync(<span style="color: rgba(0, 0, 255, 1)">async</span> (session) => <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> _dbSet.DeleteManyAsync(expression));
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span> DeleteResult.Acknowledged(<span style="color: rgba(128, 0, 128, 1)">10</span><span style="color: rgba(0, 0, 0, 1)">);
}
</span><span style="color: rgba(0, 0, 255, 1)">#endregion</span>
<span style="color: rgba(0, 0, 255, 1)">#region</span> Update Part
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task UpdateAsync(TEntity entity, IClientSessionHandle session = <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
{
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (session == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">await</span> _dbSet.ReplaceOneAsync(item => item.Id ==<span style="color: rgba(0, 0, 0, 1)"> entity.Id, entity);
</span><span style="color: rgba(0, 0, 255, 1)">else</span>
<span style="color: rgba(0, 0, 255, 1)">await</span> _dbContext.AddCommandAsync(<span style="color: rgba(0, 0, 255, 1)">async</span> (session) => <span style="color: rgba(0, 0, 255, 1)">await</span> _dbSet.ReplaceOneAsync(item => item.Id ==<span style="color: rgba(0, 0, 0, 1)"> entity.Id, entity));
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task UpdateAsync(Expression<Func<TEntity, <span style="color: rgba(0, 0, 255, 1)">bool</span>>> expression, Expression<Action<TEntity>> entity, IClientSessionHandle session = <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
{
</span><span style="color: rgba(0, 0, 255, 1)">var</span> fieldList = <span style="color: rgba(0, 0, 255, 1)">new</span> List<UpdateDefinition<TEntity>><span style="color: rgba(0, 0, 0, 1)">();
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (entity.Body <span style="color: rgba(0, 0, 255, 1)">is</span><span style="color: rgba(0, 0, 0, 1)"> MemberInitExpression param)
{
</span><span style="color: rgba(0, 0, 255, 1)">foreach</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> item <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> param.Bindings)
{
</span><span style="color: rgba(0, 0, 255, 1)">var</span> propertyName =<span style="color: rgba(0, 0, 0, 1)"> item.Member.Name;
</span><span style="color: rgba(0, 0, 255, 1)">object</span> propertyValue = <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (item <span style="color: rgba(0, 0, 255, 1)">is</span> not MemberAssignment memberAssignment) <span style="color: rgba(0, 0, 255, 1)">continue</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (memberAssignment.Expression.NodeType ==<span style="color: rgba(0, 0, 0, 1)"> ExpressionType.Constant)
{
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (memberAssignment.Expression <span style="color: rgba(0, 0, 255, 1)">is</span><span style="color: rgba(0, 0, 0, 1)"> ConstantExpression constantExpression)
propertyValue </span>=<span style="color: rgba(0, 0, 0, 1)"> constantExpression.Value;
}
</span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">
{
propertyValue </span>= Expression.Lambda(memberAssignment.Expression, <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">).Compile().DynamicInvoke();
}
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (propertyName !=<span style="color: rgba(0, 0, 0, 1)"> _keyField)
{
fieldList.Add(Builders</span><TEntity><span style="color: rgba(0, 0, 0, 1)">.Update.Set(propertyName, propertyValue));
}
}
}
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (session == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">await</span> _dbSet.UpdateOneAsync(expression, Builders<TEntity><span style="color: rgba(0, 0, 0, 1)">.Update.Combine(fieldList));
</span><span style="color: rgba(0, 0, 255, 1)">else</span>
<span style="color: rgba(0, 0, 255, 1)">await</span> _dbContext.AddCommandAsync(<span style="color: rgba(0, 0, 255, 1)">async</span> (session) => <span style="color: rgba(0, 0, 255, 1)">await</span> _dbSet.UpdateOneAsync(expression, Builders<TEntity><span style="color: rgba(0, 0, 0, 1)">.Update.Combine(fieldList)));
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task UpdateAsync(FilterDefinition<TEntity> filter, UpdateDefinition<TEntity> update, IClientSessionHandle session = <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
{
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (session == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> _dbSet.UpdateOneAsync(filter, update);
</span><span style="color: rgba(0, 0, 255, 1)">else</span>
<span style="color: rgba(0, 0, 255, 1)">await</span> _dbContext.AddCommandAsync(<span style="color: rgba(0, 0, 255, 1)">async</span> (session) => <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> _dbSet.UpdateOneAsync(filter, update));
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task UpdateManyAsync(Expression<Func<TEntity, <span style="color: rgba(0, 0, 255, 1)">bool</span>>> expression, UpdateDefinition<TEntity> update, IClientSessionHandle session = <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
{
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (session == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> _dbSet.UpdateManyAsync(expression, update);
</span><span style="color: rgba(0, 0, 255, 1)">else</span>
<span style="color: rgba(0, 0, 255, 1)">await</span> _dbContext.AddCommandAsync(<span style="color: rgba(0, 0, 255, 1)">async</span> (session) => <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> _dbSet.UpdateManyAsync(expression, update));
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task<UpdateResult> UpdateManayAsync(Dictionary<<span style="color: rgba(0, 0, 255, 1)">string</span>, <span style="color: rgba(0, 0, 255, 1)">string</span>> dic, FilterDefinition<TEntity> filter, IClientSessionHandle session = <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
{
</span><span style="color: rgba(0, 0, 255, 1)">var</span> t = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> TEntity();
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Fields to be updated</span>
<span style="color: rgba(0, 0, 255, 1)">var</span> list = <span style="color: rgba(0, 0, 255, 1)">new</span> List<UpdateDefinition<TEntity>><span style="color: rgba(0, 0, 0, 1)">();
</span><span style="color: rgba(0, 0, 255, 1)">foreach</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> item <span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> t.GetType().GetProperties())
{
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (!dic.ContainsKey(item.Name)) <span style="color: rgba(0, 0, 255, 1)">continue</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">var</span> value =<span style="color: rgba(0, 0, 0, 1)"> dic;
list.Add(Builders</span><TEntity><span style="color: rgba(0, 0, 0, 1)">.Update.Set(item.Name, value));
}
</span><span style="color: rgba(0, 0, 255, 1)">var</span> updatefilter = Builders<TEntity><span style="color: rgba(0, 0, 0, 1)">.Update.Combine(list);
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (session == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> _dbSet.UpdateManyAsync(filter, updatefilter);
</span><span style="color: rgba(0, 0, 255, 1)">await</span> _dbContext.AddCommandAsync(<span style="color: rgba(0, 0, 255, 1)">async</span> (session) => <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> _dbSet.UpdateManyAsync(filter, updatefilter));
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">new</span> UpdateResult.Acknowledged(<span style="color: rgba(128, 0, 128, 1)">10</span>, <span style="color: rgba(128, 0, 128, 1)">10</span>, <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">);
}
</span><span style="color: rgba(0, 0, 255, 1)">#endregion</span>
<span style="color: rgba(0, 0, 255, 1)">#region</span> Read Part
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task<TEntity> GetAsync(Expression<Func<TEntity, <span style="color: rgba(0, 0, 255, 1)">bool</span>>> expression, <span style="color: rgba(0, 0, 255, 1)">bool</span> readFromPrimary = <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">)
{
</span><span style="color: rgba(0, 0, 255, 1)">var</span> readPreference =<span style="color: rgba(0, 0, 0, 1)"> GetReadPreference(readFromPrimary);
</span><span style="color: rgba(0, 0, 255, 1)">var</span> queryData = <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> _dbSet.WithReadPreference(readPreference)
.Find(expression)
.FirstOrDefaultAsync();
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> queryData;
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task<TEntity> GetAsync(<span style="color: rgba(0, 0, 255, 1)">string</span> id, <span style="color: rgba(0, 0, 255, 1)">bool</span> readFromPrimary = <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">)
{
</span><span style="color: rgba(0, 0, 255, 1)">var</span> readPreference =<span style="color: rgba(0, 0, 0, 1)"> GetReadPreference(readFromPrimary);
</span><span style="color: rgba(0, 0, 255, 1)">var</span> queryData = <span style="color: rgba(0, 0, 255, 1)">await</span> _dbSet.WithReadPreference(readPreference).FindAsync(Builders<TEntity>.Filter.Eq(_keyField, <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> ObjectId(id)));
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> queryData.FirstOrDefault();
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task<IEnumerable<TEntity>> GetAllAsync(<span style="color: rgba(0, 0, 255, 1)">bool</span> readFromPrimary = <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">)
{
</span><span style="color: rgba(0, 0, 255, 1)">var</span> readPreference =<span style="color: rgba(0, 0, 0, 1)"> GetReadPreference(readFromPrimary);
</span><span style="color: rgba(0, 0, 255, 1)">var</span> queryAllData = <span style="color: rgba(0, 0, 255, 1)">await</span> _dbSet.WithReadPreference(readPreference).FindAsync(Builders<TEntity><span style="color: rgba(0, 0, 0, 1)">.Filter.Empty);
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> queryAllData.ToList();
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task<<span style="color: rgba(0, 0, 255, 1)">long</span>> CountAsync(Expression<Func<TEntity, <span style="color: rgba(0, 0, 255, 1)">bool</span>>> expression, <span style="color: rgba(0, 0, 255, 1)">bool</span> readFromPrimary = <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">)
{
</span><span style="color: rgba(0, 0, 255, 1)">var</span> readPreference =<span style="color: rgba(0, 0, 0, 1)"> GetReadPreference(readFromPrimary);
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> _dbSet.WithReadPreference(readPreference).CountDocumentsAsync(expression);
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task<<span style="color: rgba(0, 0, 255, 1)">long</span>> CountAsync(FilterDefinition<TEntity> filter, <span style="color: rgba(0, 0, 255, 1)">bool</span> readFromPrimary = <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">)
{
</span><span style="color: rgba(0, 0, 255, 1)">var</span> readPreference =<span style="color: rgba(0, 0, 0, 1)"> GetReadPreference(readFromPrimary);
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> _dbSet.WithReadPreference(readPreference).CountDocumentsAsync(filter);
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task<<span style="color: rgba(0, 0, 255, 1)">bool</span>> ExistsAsync(Expression<Func<TEntity, <span style="color: rgba(0, 0, 255, 1)">bool</span>>> predicate, <span style="color: rgba(0, 0, 255, 1)">bool</span> readFromPrimary = <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">)
{
</span><span style="color: rgba(0, 0, 255, 1)">var</span> readPreference =<span style="color: rgba(0, 0, 0, 1)"> GetReadPreference(readFromPrimary);
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> Task.FromResult(_dbSet.WithReadPreference(readPreference).AsQueryable().Any(predicate));
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task<List<TEntity>> FindListAsync(FilterDefinition<TEntity> filter, <span style="color: rgba(0, 0, 255, 1)">string</span>[]? field = <span style="color: rgba(0, 0, 255, 1)">null</span>, SortDefinition<TEntity>? sort = <span style="color: rgba(0, 0, 255, 1)">null</span>, <span style="color: rgba(0, 0, 255, 1)">bool</span> readFromPrimary = <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">)
{
</span><span style="color: rgba(0, 0, 255, 1)">var</span> readPreference =<span style="color: rgba(0, 0, 0, 1)"> GetReadPreference(readFromPrimary);
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (field == <span style="color: rgba(0, 0, 255, 1)">null</span> || field.Length == <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">)
{
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (sort == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> _dbSet.WithReadPreference(readPreference).Find(filter).ToListAsync();
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> _dbSet.WithReadPreference(readPreference).Find(filter).Sort(sort).ToListAsync();
}
</span><span style="color: rgba(0, 0, 255, 1)">var</span> fieldList = <span style="color: rgba(0, 0, 255, 1)">new</span> List<ProjectionDefinition<TEntity>><span style="color: rgba(0, 0, 0, 1)">();
</span><span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span> i = <span style="color: rgba(128, 0, 128, 1)">0</span>; i < field.Length; i++<span style="color: rgba(0, 0, 0, 1)">)
{
fieldList.Add(Builders</span><TEntity><span style="color: rgba(0, 0, 0, 1)">.Projection.Include(field.ToString()));
}
</span><span style="color: rgba(0, 0, 255, 1)">var</span> projection = Builders<TEntity><span style="color: rgba(0, 0, 0, 1)">.Projection.Combine(fieldList);
fieldList</span>?<span style="color: rgba(0, 0, 0, 1)">.Clear();
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (sort == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">await</span> _dbSet.WithReadPreference(readPreference).Find(filter).Project<TEntity><span style="color: rgba(0, 0, 0, 1)">(projection).ToListAsync();
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">await</span> _dbSet.WithReadPreference(readPreference).Find(filter).Sort(sort).Project<TEntity><span style="color: rgba(0, 0, 0, 1)">(projection).ToListAsync();
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task<List<TEntity>> FindListByPageAsync(FilterDefinition<TEntity> filter, <span style="color: rgba(0, 0, 255, 1)">int</span> pageIndex, <span style="color: rgba(0, 0, 255, 1)">int</span> pageSize, <span style="color: rgba(0, 0, 255, 1)">string</span>[]? field = <span style="color: rgba(0, 0, 255, 1)">null</span>, SortDefinition<TEntity>? sort = <span style="color: rgba(0, 0, 255, 1)">null</span>, <span style="color: rgba(0, 0, 255, 1)">bool</span> readFromPrimary = <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">)
{
</span><span style="color: rgba(0, 0, 255, 1)">var</span> readPreference =<span style="color: rgba(0, 0, 0, 1)"> GetReadPreference(readFromPrimary);
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (field == <span style="color: rgba(0, 0, 255, 1)">null</span> || field.Length == <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">)
{
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (sort == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">await</span> _dbSet.WithReadPreference(readPreference).Find(filter).Skip((pageIndex - <span style="color: rgba(128, 0, 128, 1)">1</span>) *<span style="color: rgba(0, 0, 0, 1)"> pageSize).Limit(pageSize).ToListAsync();
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">await</span> _dbSet.WithReadPreference(readPreference).Find(filter).Sort(sort).Skip((pageIndex - <span style="color: rgba(128, 0, 128, 1)">1</span>) *<span style="color: rgba(0, 0, 0, 1)"> pageSize).Limit(pageSize).ToListAsync();
}
</span><span style="color: rgba(0, 0, 255, 1)">var</span> fieldList = <span style="color: rgba(0, 0, 255, 1)">new</span> List<ProjectionDefinition<TEntity>><span style="color: rgba(0, 0, 0, 1)">();
</span><span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">int</span> i = <span style="color: rgba(128, 0, 128, 1)">0</span>; i < field.Length; i++<span style="color: rgba(0, 0, 0, 1)">)
{
fieldList.Add(Builders</span><TEntity><span style="color: rgba(0, 0, 0, 1)">.Projection.Include(field.ToString()));
}
</span><span style="color: rgba(0, 0, 255, 1)">var</span> projection = Builders<TEntity><span style="color: rgba(0, 0, 0, 1)">.Projection.Combine(fieldList);
fieldList</span>?<span style="color: rgba(0, 0, 0, 1)">.Clear();
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (sort == <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">await</span> _dbSet.WithReadPreference(readPreference).Find(filter).Project<TEntity>(projection).Skip((pageIndex - <span style="color: rgba(128, 0, 128, 1)">1</span>) *<span style="color: rgba(0, 0, 0, 1)"> pageSize).Limit(pageSize).ToListAsync();
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">await</span> _dbSet.WithReadPreference(readPreference).Find(filter).Sort(sort).Project<TEntity>(projection).Skip((pageIndex - <span style="color: rgba(128, 0, 128, 1)">1</span>) *<span style="color: rgba(0, 0, 0, 1)"> pageSize).Limit(pageSize).ToListAsync();
}
</span><span style="color: rgba(0, 0, 255, 1)">#endregion</span>
<span style="color: rgba(0, 0, 255, 1)">#region</span> Protected Methods
<span style="color: rgba(0, 0, 255, 1)">protected</span> ReadPreference GetReadPreference(<span style="color: rgba(0, 0, 255, 1)">bool</span><span style="color: rgba(0, 0, 0, 1)"> readFromPrimary)
{
</span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (readFromPrimary)
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> ReadPreference.PrimaryPreferred;
</span><span style="color: rgba(0, 0, 255, 1)">else</span>
<span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> ReadPreference.SecondaryPreferred;
}
</span><span style="color: rgba(0, 0, 255, 1)">#endregion</span><span style="color: rgba(0, 0, 0, 1)">
}</span></pre>
</div>
<h1><strong><strong>工作单元:UnitOfWork</strong></strong></h1>
<p>在实际项目中,在对多个Repository操作之后,我们希望有一个统一的提交操作来实现事务的原子性。因此,我们可以有一个UnitOfWork来作为代理:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> UnitOfWork : IUnitOfWork
{
</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span><span style="color: rgba(0, 0, 0, 1)"> IMongoDbContext _context;
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> UnitOfWork(IMongoDbContext context)
{
_context </span>=<span style="color: rgba(0, 0, 0, 1)"> context;
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">bool</span><span style="color: rgba(0, 0, 0, 1)"> SaveChanges(IClientSessionHandle session)
{
</span><span style="color: rgba(0, 0, 255, 1)">return</span> _context.Commit(session) > <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">;
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task<<span style="color: rgba(0, 0, 255, 1)">bool</span>><span style="color: rgba(0, 0, 0, 1)"> SaveChangesAsync(IClientSessionHandle session)
{
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">await</span> _context.CommitAsync(session) > <span style="color: rgba(128, 0, 128, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">;
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> IClientSessionHandle BeginTransaction()
{
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> _context.StartSession();
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span> Task<IClientSessionHandle><span style="color: rgba(0, 0, 0, 1)"> BeginTransactionAsync()
{
</span><span style="color: rgba(0, 0, 255, 1)">return</span> <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> _context.StartSessionAsync();
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span><span style="color: rgba(0, 0, 0, 1)"> Dispose()
{
_context.Dispose();
}
}</span></pre>
</div>
<h1><strong><strong>封装注入:ServiceCollectionExtensions</strong></strong></h1>
<p>为了便于应用中快速注入,我们可以简单封装一个扩展方法,快速注入相关的核心组成部分:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> ServiceCollectionExtensions
{
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"><summary></span>
<span style="color: rgba(128, 128, 128, 1)">///</span><span style="color: rgba(0, 128, 0, 1)"> MongoDB Config Injection
</span><span style="color: rgba(128, 128, 128, 1)">///</span> <span style="color: rgba(128, 128, 128, 1)"></summary></span>
<span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">static</span> IServiceCollection AddMongoProxy(<span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)"> IServiceCollection services, IConfiguration configuration)
{
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (!<span style="color: rgba(0, 0, 0, 1)">configuration.GetSection(nameof(MongoDatabaseConfigs)).Exists())
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> services;
services.Configure</span><MongoDatabaseConfigs><span style="color: rgba(0, 0, 0, 1)">(configuration.GetSection(nameof(MongoDatabaseConfigs)));
services.AddSingleton(sp </span>=> sp.GetRequiredService<IOptions<MongoDatabaseConfigs>><span style="color: rgba(0, 0, 0, 1)">().Value);
services.AddSingleton</span><IMongoDbConnection, MongoDbConnection><span style="color: rgba(0, 0, 0, 1)">();
services.AddScoped</span><IMongoDbContext, MongoDbContext><span style="color: rgba(0, 0, 0, 1)">();
services.AddScoped</span><IUnitOfWork, UnitOfWork><span style="color: rgba(0, 0, 0, 1)">();
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> services;
}
}</span></pre>
</div>
<h1><strong><strong>如何使用:三步上篮</strong></strong></h1>
<h3>第一步:注入MongoProxy核心部分</h3>
<p>在appsettings中配置MongoDB的连接信息:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">MongoDatabaseConfigs</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">: {
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Servers</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">xxx01.edisontalk.net,xxx02.edisontalk.net,xxx03.edisontalk.net</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Port</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 128, 1)">27017</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">ReplicaSetName</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">edt-replica</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">DatabaseName</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">EDT_Practices</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">UserName</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">xxxxxxxxxxxxx</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">,
</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">Password</span><span style="color: rgba(128, 0, 0, 1)">"</span>: <span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(128, 0, 0, 1)">xxxxxxxxxxxxx</span><span style="color: rgba(128, 0, 0, 1)">"</span><span style="color: rgba(0, 0, 0, 1)">
}</span></pre>
</div>
<p>然后通过扩展方法注入MongoProxy相关部分:</p>
<div class="cnblogs_code">
<pre>builder.Services.AddMongoProxy(builder.Configuration);</pre>
</div>
<h3>第二步:添加Entity 和 Repository</h3>
<p>示例Entity:</p>
<div class="cnblogs_code">
<pre>
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> OrderEntity : MongoEntityBase, IEntity
{
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">string</span> OrderNumber { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
</span><span style="color: rgba(0, 0, 255, 1)">public</span> List<TransmissionEntity> Transmissions { <span style="color: rgba(0, 0, 255, 1)">get</span>; <span style="color: rgba(0, 0, 255, 1)">set</span><span style="color: rgba(0, 0, 0, 1)">; }
}</span></pre>
</div>
<p>示例Repository:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">interface</span> ITodoItemRepository : IMongoRepositoryBase<TodoItem><span style="color: rgba(0, 0, 0, 1)">
{
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span> TodoItemRepository : MongoRepositoryBase<TodoItem><span style="color: rgba(0, 0, 0, 1)">, ITodoItemRepository
{
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> TodoItemRepository(IMongoDbContext mongoDbContext)
: </span><span style="color: rgba(0, 0, 255, 1)">base</span><span style="color: rgba(0, 0, 0, 1)">(mongoDbContext)
{
}
}
services.AddScoped</span><ITodoItemRepository, TodoItemRepository><span style="color: rgba(0, 0, 0, 1)">();
services.AddScoped</span><IOrderRepository, OrderRepository><span style="color: rgba(0, 0, 0, 1)">();
......</span></pre>
</div>
<p>第三步:使用Repository 和 UnitOfWork</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)"># 非事务模式
</span><span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> _taskRepository.AddManyAsync(newTasks);
# 事务模式(借助UnitOfWork工作单元)
</span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">readonly</span><span style="color: rgba(0, 0, 0, 1)"> IUnitOfWork _unitOfWork;
</span><span style="color: rgba(0, 0, 255, 1)">public</span><span style="color: rgba(0, 0, 0, 1)"> OrderService(IUnitOfWork unitOfWork, ......)
{
_unitOfWork </span>=<span style="color: rgba(0, 0, 0, 1)"> unitOfWork;
......
}
</span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">async</span><span style="color: rgba(0, 0, 0, 1)"> Task Example()
{
</span><span style="color: rgba(0, 0, 255, 1)">using</span> <span style="color: rgba(0, 0, 255, 1)">var</span> session = <span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> _unitOfWork.BeginTransactionAsync())
</span><span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> _taskRepository.AddManyAsync(newTasks, session);
</span><span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> _orderRepository.AddAsync(newOrder, session);
</span><span style="color: rgba(0, 0, 255, 1)">await</span><span style="color: rgba(0, 0, 0, 1)"> _unitOfWork.SaveChangesAsync(session);
}</span></pre>
</div>
<h1><strong><strong>小结</strong></strong></h1>
<p>本文介绍了MongoDB事务的基本概念和如何通过.NET操作事务,重点介绍了EDT.MongoProxy这个小组件的设计,让我们可以在ASP.NET 6应用中通过数据仓储(Repository)和工作单元(UnitOfWork)的模式来快速方便地操作MongoDB的事务。</p>
<h1><strong><strong>参考代码</strong></strong></h1>
<p>本文代码并未提供所有的,如需查看,请至下面的代码仓库中查看,也可以点个赞给点鼓励。</p>
<p>GitHub:https://github.com/Coder-EdisonZhou/EDT.MongoProxy</p>
<h1><strong><strong>参考资料</strong></strong></h1>
<p>追逐时光者,《.NET Core MongoDB数据仓储和工作单元实操》 <strong>*本文主要设计参考自这篇文章,值得一读!</strong></p>
<p>TheCodeBuzz,《MongoDB Repository Implementation in .NET Core》:</p>
<p>Bryan Avery, 《ASP.NET Core - MongoDB Repository Pattern & Unit Of Work》: </p>
<p> </p>
<p style="text-align: center"><img src="https://images.cnblogs.com/cnblogs_com/edisonchou/1647700/o_200902144330EdisonTalk-Footer.jpg" alt="" style="width: 65%; border: 1px solid rgba(221, 221, 221, 1); border-radius: 3px; box-shadow: 0 4px 8px rgba(3, 27, 78, 0.12)"></p>
<div id="Copyright">
<p>作者:周旭龙</p>
<p>出处:https://edisonchou.cnblogs.com</p>
<p>本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。</p>
</div>
</div>
<div id="MySignature" role="contentinfo">
<div align="center"><img border="0" src="http://service.t.sina.com.cn/widget/qmd/2068032061/d643d182/10.png"></div><br><br>
来源:https://www.cnblogs.com/edisontalk/p/how_to_use_mongodb_repository_and_unitofwork_in_aspnet6.html
頁:
[1]