为MongoDb存储库创建通用更新方法

时间:2018-10-13 14:08:50

标签: c# mongodb asp.net-core mongodb-.net-driver system.reflection

我正在尝试实现这样的BaseRepository:

public interface IRepository<T>
{    
   Task<T> Update(T entity, IEnumerable<UpdateFieldDefinition> update);
}
public class BaseRepository<T> : IRepository<T> where T : BaseEntity{
}

有了BaseEntity,这是一个简单的类,用于存储在mongo中的所有实体。 由于将通过接口在核心项目内部调用资源库,并且核心项目必须不知道DB的实现是MongoDb,因此我使用了一个对象来传递给Update,该对象称为UpdateFieldDefinition的定义如下:这个:

public class UpdateFieldDefinition
    {
        public UpdateFieldDefinition(string propertyName, object propertyValue)
        {
            PropertyName = propertyName;
            PropertyValue = propertyValue;
        }
        public string PropertyName { get; set; }
        public object PropertyValue { get; set; }
    }

因此,当我需要更新角色时,我将执行以下操作:

    var updateFields = new List<UpdateFieldDefinition>();
    var newNameValue = "Test";
    var newListValue = new List<string> { "1", "2" };
    updateFields.Add(new UpdateFieldDefinition("Name", newNameValue));
    updateFields.Add(new UpdateFieldDefinition("ListValues", newListValue));
    var testObj = await testRepository.Update(updateEntity, updateFields);

Update的实现是这个

public async Task<T> Update(T entity, IEnumerable<UpdateFieldDefinition> updateFieldDefinitions)
{
    var builder = new UpdateDefinitionBuilder<T>();
    var options = new FindOneAndUpdateOptions<T>
    {
        ReturnDocument = ReturnDocument.After,
        IsUpsert = false
    };
    //setting the fields to update based on what has been set from outside based on the Implemented BaseEntity T
    var updates = updateFieldDefinitions
        .Select(updateFieldDefinition =>
            builder.Set(updateFieldDefinition.PropertyName, updateFieldDefinition.PropertyValue))
        .ToList();
    //add update for LastModifiedDate => 
    updates.Add(builder.Set(x => x.LastModifiedDate, DateTime.Now));

    var filter = Builders<T>.Filter.Eq(en => en.Id, entity.Id);
    var updateCmd = builder.Combine(updates);
    var result = await DbContext.GetCollection<T>().FindOneAndUpdateAsync(filter, updateCmd, options);
    return result;
}

但是这不起作用,因为当它遇到作为列表的“ ListValue”时,但是在UpdateFieldDefinition内存储为Object时,系统正在尝试将其转换为字符串,从而出现以下错误:{{1} }

有什么办法可以解决这个问题?给出的代码是问题的简化版本,因为我为BaseEntity创建了扩展方法,该方法将通过对T类型的所有属性的反射来创建UpdateFieldDefinition的列表。

// EDIT1 添加了我如何为实体检索UpdateFieldDefinitions列表的示例

Cannot deserialize a 'List<String>' from BsonType 'String'.

1 个答案:

答案 0 :(得分:0)

    public static UpdateDefinition<T> GetUpdateDefinition<T>(this T entity)
            {
                var updateFieldDefinitions = entity.GetType().GetProperties()
                    .Where(x => !new string[] { "Id", "_id" }.Contains(x.Name))
                    .Select(x => new UpdateFieldDefinition(x.Name, x.GetValue(entity)));
    
                var builder = new UpdateDefinitionBuilder<T>();
                var updates = updateFieldDefinitions.Select(TField => builder.Set(TField.Name, TField.Value));
                return builder.Combine(updates);
            }
    

...

    public async static Task<T> UpsertAsync<T>(Expression<Func<T, bool>> expression, T entity)
            {
                var collection = Database.GetCollection<T>(typeof(T).Name);
    
                var updates = entity.GetUpdateDefinition();
    
                var options = new FindOneAndUpdateOptions<T>
                {
                    ReturnDocument = ReturnDocument.After,
                    IsUpsert = true
                };
    
                return await collection.FindOneAndUpdateAsync(expression, updates, options);
            }