c# Mongodb驱动程序:将Action转换为UpdateDefinition
本文关键字:转换 UpdateDefinition Action Mongodb 驱动程序 | 更新日期: 2023-09-27 18:15:44
我试图概括更新到我的mongodb,所以我可以更新我的数据存储不可知论。使用EntityFramework,这是非常容易的,因为它有变化跟踪,因此我可以分离业务逻辑和数据库特定的操作。当涉及到MongoDb时,它并不那么容易,因为我必须创建这些UpdateDefinitions,才能对文档进行更新。
一个简单的模拟方法是使用ReplaceOneAsync
,但这不是一个选择,因为不止一方可以同时写我的集合。例如,我在我的应用程序中使用它作为聊天系统。
我想做这样的事情:
public class MongoActionWrapper<TCollection>
{
public MongoActionWrapper(Action<TCollection> updateAction)
{
UpdateAction = updateAction;
}
public Action<TCollection> UpdateAction { get; }
}
然后我有我的MongoRepository:
public abstract class BaseMongoRepository<TAggregate, TCollection> : IRepository<TAggregate> where TAggregate : BaseAggregateRoot, IMongoAggregate<TCollection>
{
private readonly IMongoCollection<TCollection> _mongoCollection;
protected BaseMongoRepository(IMongoCollection<TCollection> mongoCollection)
{
_mongoCollection = mongoCollection;
}
public async void SaveAsync(TAggregate aggregate)
{
var state = aggregate.GetState();
foreach (var action in state.ActionsToExecuteOnCollection)
{
await _mongoCollection.UpdateOneAsync<TCollection>(aggregate.GetSelector(), action.UpdateAction);
}
}
}
我想在集合上执行操作,而不是UpdateDefinition。或者以某种方式将我的Action转换为UpdateDefinition。
这将使我能够在列表(或任何其他集合,我想要存储我的数据)上应用相同的更新,这样我就不会被迫留在mongodb。
到目前为止,我唯一的解决方案是使我的UpdateAction
更像一个描述更新的对象,然后翻译这些对象,当我想在mongodb中持久化它时。通过这种方式,我可以将它们转换为Actions并将它们应用到我的集合或我选择的任何其他DB中。
我希望有人有一个主意,因为我已经没有选择了。
我不确定你是如何做到这一点与行动委托,但如果你想重现EntityFramework,如Read-Through, Write-Through缓存与多个并发写在单个AggregateRoot,你可以尝试这样的东西:
public abstract class AggregateRoot<T> where T : AggregateRoot<T>
{
internal UpdateDefinition<T> UpdateDefinition { get; internal set; }
internal void Aggregate(UpdateDefinition<T> component)
{
if (component == null)
{
throw new ArgumentNullException("component");
}
if (this.UpdateDefinition == null)
{
this.UpdateDefinition = component;
}
else
{
this.UpdateDefinition = Builders<T>.Update.Combine
(this.UpdateDefinition, component);
}
}
}
,然后在存储库基类中:
public class Repository<T> where T : AggregateRoot<T>
{
private ConcurrentDictionary<ObjectId, T> cache
= new ConcurrentDictionary<ObjectId, T>();
private IMongoCollection<T> collection;
public Repository(string connectionString, string databaseName, string collectionName)
{
collection = new MongoClient(connectionString)
.GetDatabase(databaseName)
.GetCollection<T>(collectionName);
}
public T Find(ObjectId aggregateRootId)
{
return cache.GetOrAdd(
key: aggregateRootId,
valueFactory: key => findById(key));
}
private T findById(ObjectId aggregateRootId)
{
return collection
.Find(Builders<T>.Filter
.Eq(x => x.Id, aggregateRootId))
.SingleOrDefault();
}
public void SaveChanges()
{
var writeModels = generateWriteModels();
collection.BulkWrite(writeModels);
}
private IEnumerable<WriteModel<T>> generateWriteModels()
{
List<WriteModel<T>> writeModels = new List<WriteModel<T>>();
foreach (var cached in cache)
{
if (cached.Value != null)
{
if (cached.Value.UpdateDefinition != null)
{
writeModels.Add(new UpdateOneModel<T>(
filter: Builders<T>.Filter.Eq(x => x.Id, cached.Value.Id),
update: cached.Value.UpdateDefinition){ IsUpsert = true });
cached.Value.UpdateDefinition = null;
}
}
}
return writeModels;
}
}
有了这个实现,你可以直接在实现中与AggregateRoot<T>
基类的Aggregate(UpdateDefinition<T> component)
方法交互(注意不要在setter上使用它- BsonSerializer也使用setter)。更改将被Repository<T>
使用。
如果你想让你的领域模型尽可能地与存储实现解耦,你可以在聚合上构建一个相当于MongoActionWrapper<TCollection>
的包装器。这些包装器可以使用聚合方法和Aggregate(UpdateDefinition<T> component)
来保持更新定义与对聚合所做的更改同步。