C#Mongodb驱动程序:将操作转换为UpdateDefinition

时间:2016-10-31 14:02:15

标签: c# mongodb mongodb-.net-driver

我正在尝试推广对我的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中保留它时。这样我就可以将它们转换为动作并将它们应用到我的集合或我选择的其他任何数据库中。

我希望有人有个主意,因为我在这里没有选择。

1 个答案:

答案 0 :(得分:2)

我不知道你是如何使用Action委托执行此操作的,但是如果你想重现像单个AggregateRoot上的多个并发写入的Read-Through,Write-Through缓存之类的EntityFramework,你可以试试这样的事情:

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;
    }
}

通过此实现,您可以直接在实现中与Aggregate(UpdateDefinition<T> component)基类的AggregateRoot<T>方法进行交互(注意在setter上使用它--BsonSerializer也使用setter)。 Repository<T>将消耗更改。

如果要使域模型与存储实现尽可能地分离,则可以构建等效的MongoActionWrapper<TCollection>作为聚合的包装器。这些包装器可以使用聚合方法和Aggregate(UpdateDefinition<T> component)来保持更新定义与聚合所做的更改同步。