I'm trying to implement a BaseRepository like this:
public interface IRepository<T>
{
Task<T> Update(T entity, IEnumerable<UpdateFieldDefinition> update);
}
public class BaseRepository<T> : IRepository<T> where T : BaseEntity{
}
With BaseEntity a simple class that is used for all the entities stored in mongo.
Since the Repository wil be called inside the core project by the interface, and the core project must not know that the implementation of the DB is MongoDb, I used an object to pass to Update
called UpdateFieldDefinition
defined like this:
public class UpdateFieldDefinition
{
public UpdateFieldDefinition(string propertyName, object propertyValue)
{
PropertyName = propertyName;
PropertyValue = propertyValue;
}
public string PropertyName { get; set; }
public object PropertyValue { get; set; }
}
So, when I need to update a Role, i'll do something like this:
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);
And the implementation of Update is this
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;
}
But this is not working, because when it meet the "ListValue" which is a list, but inside the UpdateFieldDefinition is stored as Object, the system is trying to convert it to a string, giving this error : Cannot deserialize a 'List<String>' from BsonType 'String'.
Is there any way to handle this? The code given is a simplified version of the problem, since I created an extension method for BaseEntity that will create the list of UpdateFieldDefinition using reflection on all properties of the type T.
//EDIT1 adding the example of how i retrieve for an entity the list of 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)));
}
}