Я пытаюсь реализовать BaseRepository следующим образом:
public interface IRepository<T>
{
Task<T> Update(T entity, IEnumerable<UpdateFieldDefinition> update);
}
public class BaseRepository<T> : IRepository<T> where T : BaseEntity{
}
С BaseEntity - простым классом, который используется для всех сущностей, хранящихся в монго.Поскольку репозиторий будет вызываться из основного проекта через интерфейс, а основной проект не должен знать, что реализация БД - 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);
И реализация обновления будет такой:
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 хранится как объект, система пытается преобразовать его в строку, выдав эту ошибку: Cannot deserialize a 'List<String>' from BsonType 'String'.
Есть ли способ справиться с этим?Данный код является упрощенной версией проблемы, так как я создал метод расширения для BaseEntity, который создаст список UpdateFieldDefinition, используя отражение во всех свойствах типа T.
// EDIT1, добавив пример того, какя получаю для сущности список UpdateFieldDefinitions
public static class EntityHelper
{
public static IEnumerable<UpdateFieldDefinition> GetUpdateDefinition<T>(this T entity)
{
return entity.GetType().GetProperties()
.Where(x =>
{
switch (x.Name)
{
case nameof(BaseEntity.Id):
case nameof(BaseEntity.CreationDate):
case nameof(BaseEntity.LastModifiedDate):
case nameof(BaseEntity.CreatorId):
case nameof(BaseEntity.LastModifierUserId):
return false;
default:
return true;
}
})
.Select(x => new UpdateFieldDefinition(x.Name, x.GetValue(entity)));
}
}