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中。

我希望有人有一个主意,因为我已经没有选择了。

c# Mongodb驱动程序:将Action转换为UpdateDefinition

我不确定你是如何做到这一点与行动委托,但如果你想重现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)来保持更新定义与对聚合所做的更改同步。