How to override AutoMapper's config on single property? - c#

I'm trying to use AutoMapper 12.0.0 to fix wrong string property values. In the example below I have class with some string properties and for all properties except one I need to perform .Trim('*'). And for the remaining one I need to perform .ToUpper() instead of .Trim('*').
The problem is that I don't know how to tell AutoMapper not to use CreateMap<string, string>() mapping configuration on single property and use another rule instead. It just always applies both rules. And if I add .Ignore() or .Condition(_ => false), it just ignores property at all and puts null instead.
Really don't want to set up mapping for each property manually, because in classes in my real project there are just too many properties, and special rules are needed just for few of them.
Have any ideas how to achieve that in AutoMapper?
using AutoMapper;
var data = new MyClass
{
String1 = "***trim that***",
String2 = "***trim also that***",
String3 = "***and trim this***",
SpecialString = "***don't trim, but make in uppercase***"
};
var mapper = new Mapper(new MapperConfiguration(cfg=>cfg.AddProfile<MappingProfile>()));
var result = mapper.Map<MyClass>(data);
// [trim that] - ok
Console.WriteLine($"[{result.String1}]");
// [trim also that] - ok
Console.WriteLine($"[{result.String2}]");
// [and trim this] - ok
Console.WriteLine($"[{result.String3}]");
// [DON'T TRIM, BUT MAKE IN UPPERCASE] - Not ok, should be [***DON'T TRIM, BUT MAKE IN UPPERCASE***]
Console.WriteLine($"[{result.SpecialString}]");
public class MyClass
{
public string String1 { get; set; }
public string String2 { get; set; }
public string String3 { get; set; }
public string SpecialString { get; set; }
}
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<string, string>().ConvertUsing<TrimTypeConverter>();
CreateMap<MyClass, MyClass>()
.ForMember(x => x.SpecialString, expr => expr.ConvertUsing(new ToUpperValueConverter()));
}
}
public class ToUpperValueConverter : IValueConverter<string, string>
{
public string Convert(string sourceMember, ResolutionContext context) => sourceMember?.ToUpper();
}
public class TrimTypeConverter : ITypeConverter<string, string>
{
public string Convert(string source, string destination, ResolutionContext context) => source?.Trim('*');
}

I am not crazy about this solution, but you asked for it so here it is:
One way of going about this would be to create a custom converter for all objects and provide logic for string properties. You can then provide a list of exceptions to modify the behavior:
public class CustomConverter : ITypeConverter<object, object>
{
private IDictionary<Type, IEnumerable<string>> _exceptions;
public CustomConverter(IDictionary<Type, IEnumerable<string>> exceptions)
{
_exceptions = exceptions;
}
public object Convert(object source, object destination, ResolutionContext context)
{
var type = source.GetType();
if (destination == null)
destination = Activator.CreateInstance(type);
foreach (var property in type.GetProperties())
{
var value = property.GetValue(source);
if (property.PropertyType == typeof(string))
{
if(_exceptions.ContainsKey(type) && _exceptions[type].Contains(property.Name))
{
property.SetValue(destination, ((string)value)?.ToUpper());
}
else
{
property.SetValue(destination, ((string)value)?.Trim('*'));
}
}
else
{
//Any other property picked up by reflection.
//Just copy over whatever the value is.
property.SetValue(destination, value);
}
}
return destination;
}
}
Then on the mapping profile:
public MappingProfile()
{
var exceptions = new Dictionary<Type, IEnumerable<string>>()
{
{ typeof(MyClass), new[] { "SpecialString" } }
};
var converter = new CustomConverter(exceptions);
CreateMap<object, object>().ConvertUsing(converter);
}
The result:
[trim that]
[trim also that]
[and trim this]
[***DON'T TRIM, BUT MAKE IN UPPERCASE***]
Please note that this is a simplistic version only so you may need to worry about special cases.

Related

Advice on using the Factory Pattern for converting a collection of model objects to one of DTOs and vice versa

I'm trying to convert a collection of model objects that share a common parent, into one of DTOs. Likewise, I want to reverse the procedure - taking a collection of DTOs with a common parent into one of model objects.
From what I've read, a Factory Pattern seems to be what I'm looking for. I also have a Producer class that handles the conversion between object model and DTO by calling the relevant factory method.
There are a few limitations:
This is an open source library, and I don't want to add methods to existing classes. Otherwise a visitor pattern would have worked. Please correct me if I'm wrong.
Similarly, I don't want to add any additional packages to this project. From what I understand, AutoMapper would have been one of the ways to go about this.
I'm new(ish) to C# and design patterns, so I apologize if I am doing something that doesn't make sense.
Here is some sample code representing what I've tried so far. I used some references from online to get an idea, but something about it doesn't seem right. There was another way mentioned here: Is a switch statement applicable in a factory method? c#, but I'm not sure if that is transferrable to this scenario.
Any critique or suggestions is welcome.
Example Usage
Animal pet1 = new Pigeon("Pidgey", 100, false);
Animal pet2 = new Rattlesnake("Ekans", 20.0, true);
IList<Animal> myPets = new List<Animal>() { pet1, pet2 };
AnimalDTOProducer dtoProducer = new AnimalDTOProducer(new AnimalDTOFactory());
IList<AnimalDTO> myDTOs = new List<AnimalDTO>();
myDTOs = dtoProducer.ConvertAnimalCollection(myPets);
Models
public abstract class Animal
{
public Animal(string name)
{
Name = name;
}
public string Name { get; set; }
// business logic
}
public abstract class Bird : Animal
{
public Bird(string name, int maxAltitude, bool isReal)
: base(name)
{
Name = name;
MaxAltitude = maxAltitude;
IsReal = isReal;
}
public int MaxAltitude { get; set; }
public bool IsReal { get; set; }
// business logic
}
public class Pigeon : Bird
{
public Pigeon(string name, int maxAltitude, bool isReal)
: base(name, maxAltitude, isReal)
{
}
// business logic
}
public abstract class Snake : Animal
{
public Snake(string name, double length, bool isPoisonous)
: base(name)
{
Name = name;
Length = length;
IsPoisonous = isPoisonous;
}
public double Length { get; set; }
public bool IsPoisonous { get; set; }
// business logic
}
public class Rattlesnake : Snake
{
public Rattlesnake(string name, double length, bool isPoisonous)
: base(name, length, isPoisonous)
{
}
// business logic
}
DTOs
public abstract class AnimalDTO { }
public class PigeonDTO : AnimalDTO
{
public string Name { get; set; }
public int MaxAltitude { get; set; }
public bool IsReal { get; set; }
}
public class RattlesnakeDTO : AnimalDTO
{
public string Name { get; set; }
public double Length { get; set; }
public bool IsPoisonous { get; set; }
}
Factories
public interface IFactory { }
public interface IAnimalFactory : IFactory
{
Animal CreateAnimal(AnimalDTO DTO);
}
public interface IAnimalDTOFactory : IFactory
{
AnimalDTO CreateAnimalDTO(Animal animal);
}
public class AnimalFactory : IAnimalFactory
{
public Animal CreateAnimal(AnimalDTO DTO)
{
switch (DTO)
{
case PigeonDTO _:
var pigeonDTO = (PigeonDTO)DTO;
return new Pigeon(pigeonDTO.Name, pigeonDTO.MaxAltitude, pigeonDTO.IsReal);
case RattlesnakeDTO _:
var rattlesnakeDTO = (RattlesnakeDTO)DTO;
return new Rattlesnake(rattlesnakeDTO.Name, rattlesnakeDTO.Length, rattlesnakeDTO.IsPoisonous);
// And many more ...
default:
return null;
}
}
}
public class AnimalDTOFactory : IAnimalDTOFactory
{
public AnimalDTO CreateAnimalDTO(Animal animal)
{
switch (animal)
{
case Pigeon _:
var _pigeon = (Pigeon)animal;
return new PigeonDTO()
{
Name = _pigeon.Name,
MaxAltitude = _pigeon.MaxAltitude,
IsReal = _pigeon.IsReal
};
case Rattlesnake _:
var _rattlesnake = (Rattlesnake)animal;
return new RattlesnakeDTO()
{
Name = _rattlesnake.Name,
Length = _rattlesnake.Length,
IsPoisonous = _rattlesnake.IsPoisonous
};
// And many more ...
default:
return null;
}
}
}
Producers
public interface IProducer { }
public interface IAnimalProducer : IProducer
{
Animal ProduceAnimalFromDTO(AnimalDTO DTO);
}
public interface IAnimalDTOProducer : IProducer
{
AnimalDTO ProduceAnimalDTOFromAnimal(Animal animal);
}
public class AnimalProducer : IAnimalProducer
{
private IAnimalFactory factory;
public AnimalProducer(IAnimalFactory factory)
{
this.factory = factory;
}
public IList<Animal> ConvertAnimalDTOCollection(IList<AnimalDTO> DTOCollection)
{
IList<Animal> result = new List<Animal>();
foreach (AnimalDTO DTO in DTOCollection)
{
var dto = ProduceAnimalFromDTO(DTO);
if (dto != null)
result.Add(dto);
}
return result;
}
public Animal ProduceAnimalFromDTO(AnimalDTO animalDTO)
{
return this.factory.CreateAnimal(animalDTO);
}
}
public class AnimalDTOProducer : IAnimalDTOProducer
{
private IAnimalDTOFactory factory;
public AnimalDTOProducer(IAnimalDTOFactory factory)
{
this.factory = factory;
}
public IList<AnimalDTO> ConvertAnimalCollection(IList<Animal> collection)
{
IList<AnimalDTO> result = new List<AnimalDTO>();
foreach (Animal animal in collection)
{
var _animal = ProduceAnimalDTOFromAnimal(animal);
if (_animal != null)
result.Add(_animal);
}
return result;
}
public AnimalDTO ProduceAnimalDTOFromAnimal(Animal animal)
{
return this.factory.CreateAnimalDTO(animal);
}
}
UPDATE 1
As recommended by sjb-sjb and ChiefTwoPencils in the comments, I eliminated the switch statements from the respective factories. The result looks like this:
public class AnimalFactory : IAnimalFactory
{
public Animal CreateAnimal(AnimalDTO DTO)
{
Type srcType = DTO.GetType();
Type modelType = Type.GetType(Regex.Replace(srcType.FullName, #"(DTO)$", ""));
IList<PropertyInfo> props = new List<PropertyInfo>(srcType.GetProperties());
var propVals = props.Select(prop => prop.GetValue(DTO, null)).ToArray();
Animal animal = (Animal)Activator.CreateInstance(modelType, propVals);
return animal;
}
}
public class AnimalDTOFactory : IAnimalDTOFactory
{
public AnimalDTO CreateAnimalDTO(Animal animal)
{
Type srcType = animal.GetType();
Type dtoType = Type.GetType($"{srcType.FullName}DTO");
AnimalDTO dto = (AnimalDTO)Activator.CreateInstance(dtoType, new object[] { });
foreach (PropertyInfo dtoProperty in dtoType.GetProperties())
{
PropertyInfo srcProperty = srcType.GetProperty(dtoProperty.Name);
if (srcProperty != null)
{
dtoProperty.SetValue(dto, srcProperty.GetValue(animal));
}
}
return dto;
}
}
The one thing I forgot to mention in the original question was that the constructor for the model may have more arguments than the DTO object has properties. That, and the order of arguments may not be the same. I think in pseudo-code, a solution will look something like this:
void AssignParamsToConstructor()
{
// Extract constructer parameters with names into an ordered list
// Match DTO properties with extracted parameters via name and type
// Fill any missing parameters with a default value or null
// Pass the final list of parameters as an array to Activator.CreateInstance method
}
I will be researching on a way to resolve this for the time being, but any pointers will be welcome.
UPDATE 2
Okay, so I found a kind of hacky solution for the previous problem regarding calling the Model constructor with missing or out-of-order arguments.
I created a helper class that creates an ordered argument array based on a combination of the Model constructor arguments and the DTO properties. This array can then be passed to Activator.CreateInstance without causing any issues.
Here is the updated AnimalFactory.CreateAnimal method:
public Animal CreateAnimal(AnimalDTO DTO)
{
Type srcType = DTO.GetType();
Type modelType = Type.GetType(Regex.Replace(srcType.FullName, #"(DTO)$", ""));
object[] propVals = Helpers.GenerateConstructorArgumentValueArray(modelType, DTO);
Animal animal = (Animal)Activator.CreateInstance(modelType, propVals);
return animal;
}
And here is the helper class:
public static class Helpers
{
public static object[] GenerateConstructorArgumentValueArray(Type type, object obj)
{
IList<(string, Type)> ctorArgTypes = new List<(string, Type)>();
IList<(string, object)> propVals = new List<(string, object)>();
// Get constructor arguments
ctorArgTypes = GetConstructorArgumentsAndTypes(type);
// Get object properties
propVals = GetObjectPropertiesAndValues(obj);
// Create args array
IList<object> paramVals = new List<object>();
foreach (var ctorArg in ctorArgTypes)
{
object val;
string _name = ctorArg.Item1.ToLower();
(string, object) _namedProp = propVals.Where(prop => prop.Item1.ToLower() == _name).FirstOrDefault();
if (_namedProp.Item2 != null)
{
val = _namedProp.Item2;
}
else
{
val = ctorArg.Item2.IsValueType ? Activator.CreateInstance(ctorArg.Item2) : null;
}
paramVals.Add(val);
}
return paramVals.ToArray();
}
private static IList<(string, Type)> GetConstructorArgumentsAndTypes(Type type)
{
List<(string, Type)> ctorArgs = new List<(string, Type)>();
TypeInfo typeInfo = type.GetTypeInfo();
ConstructorInfo[] ctors = typeInfo.DeclaredConstructors.ToArray();
ParameterInfo[] ctorParams = ctors[0].GetParameters();
foreach (ParameterInfo info in ctorParams)
{
ctorArgs.Add((info.Name, info.ParameterType));
}
return ctorArgs;
}
private static IList<(string, object)> GetObjectPropertiesAndValues(object obj)
{
List<(string, object)> props = new List<(string, object)>();
PropertyInfo[] propInfo = obj.GetType().GetProperties();
foreach (PropertyInfo info in propInfo)
{
string name = info.Name;
object val = info.GetValue(obj);
props.Add((name, val));
}
return props;
}
}
I'll have to look at this later to see how it can be improved on. For the time being however, it does its job.
I would appreciate any comments or input if you have any. I will keep updating this post until I find an absolute solution.
So I worked out a solution that seems to achieve my original goal.
The reason it was difficult to solve at first was due to the original Factory class having too many responsibilities. It had to map the properties and create a new object. Separating these made it easy to implement the Generic Factory suggested by this post:
https://web.archive.org/web/20140414013728/http://tranxcoder.wordpress.com/2008/07/11/a-generic-factory-in-c
I created a simple mapper that would automatically map Entity and DTO properties. The easier solution is to use an AutoMapper like grandaCoder suggested. My situation required otherwise so a custom mapper was the way to go. I also tried to minimize calls to System.Reflection so the performance wouldn't suffer too much.
The end result is a Factory that can convert between any Entity and DTO object, maps properties between them, and can instantiate an Entity class with no default / empty constructor.
I ended up making a lot more changes to the original post, so I uploaded the end result to github: https://github.com/MoMods/EntityDTOFactory
I am open to any additional ideas / criticisms on the final solution. This is my first time solving this kind of problem, so it's very likely there are some better ideas out there.
Thanks again for the help and suggestions!
The switch statement can be avoided using reflection:
public AnimalDTO ToDTO( Animal src)
{
Type srcType = src.GetType();
Type dtoType = Type.GetType(srcType.Name + "DTO");
AnimalDTO dto = (AnimalDTO)Activator.CreateInstance(dtoType, new object[] { });
foreach (PropertyInfo dtoProperty in dtoType.GetProperties()) {
PropertyInfo srcProperty = srcType.GetProperty(dtoProperty.Name);
if (srcProperty != null) {
dtoProperty.SetValue(dto, srcProperty.GetValue(src));
}
}
return dto;
}
To get a FromDTO method, just reverse the roles of src and dto in ToDTO.
I would not reinvent the wheel on this common scenario.
https://automapper.org/
https://www.nuget.org/packages/automapper/
OR
https://github.com/MapsterMapper/Mapster
https://www.nuget.org/packages/Mapster/
.......
Learn how to use one of these frameworks.
Below is mapster..........."performance" numbers.......which is how I found it (someone told me to lookout for automapper performance)

Generate a strongly-typed proxy that can track changes on property names not values when one property is set to another

Setup:
public class Data
{
public int A { get; set; }
public int B { get; set; }
}
public class Runner
{
public static void Run(Data data)
{
data.A = data.B;
data.A = 1;
}
}
class Program
{
static void Main(string[] args)
{
var data = new Data() { A = 1, B = 2 };
Runner.Run(data);
}
}
Problem: I need to implement change tracking here for property names not values. Inside Runner.Run on the first line data.A = data.B I need to record somehow that "A" was set to "B" (literally property names) and then on the next line data.A = 1 I need to record that "A" was set to constant and say forget about it.
Constrains:
When setting one property to another (e.g. A = B) that needs to be recorded
When setting property to anything else (e.g. A = 1 or A = B * 2) this change needs to be forgotten (e.g. remember A only)
Suppose this is the tracker contract being used:
void RecordChange(string setterName, string getterName);
void UnTrackChange(string setterName);
Question:
I would like to somehow proxy the Data class so it still can be used in the interface code (e.g. Runner - is a whole bunch of a business logic code that uses Data) INCLUDING strong-typing and it can track it's changes without modifying the code (e.g. there is lots of places like 'data.A = data.B').
Is there any way to do it without resorting to I guess some magic involving IL generation?
Already investigated/tried:
PostSharp interceptors/Castle.DynamicProxy with interceptors - these alone cannot help. The most I can get out of it is to have a value of data.B inside setter interceptor but not nameof(data.B).
Compiler services - haven't found anything suitable here - getting the name of caller doesn't really help.
Runtine code generation - smth like proxy inherited from DynamicObject or using Relfection.Emit (TypeBuilder probably) - I lose typings.
Current solution:
Use the Tracker implementation of the abovementioned contract and pass it around into every function down the road. Then instead of writing data.A = data.B use method tracker.SetFrom(x => x.A, x => x.B) - tracker holds a Data instance and so this works. BUT in a real codebase it is easy to miss something and it just makes it way less readable.
It is the closest the solution I've come up with. It isn't perfect as I still need to modify all the contracts/methods in the client code to use a new data model but at least all the logic stays the same.
So I'm open for other answers.
Here's the renewed Data model:
public readonly struct NamedProperty<TValue>
{
public NamedProperty(string name, TValue value)
{
Name = name;
Value = value;
}
public string Name { get; }
public TValue Value { get; }
public static implicit operator TValue (NamedProperty<TValue> obj)
=> obj.Value;
public static implicit operator NamedProperty<TValue>(TValue value)
=> new NamedProperty<TValue>(null, value);
}
public interface ISelfTracker<T>
where T : class, ISelfTracker<T>
{
Tracker<T> Tracker { get; set; }
}
public class NamedData : ISelfTracker<NamedData>
{
public virtual NamedProperty<int> A { get; set; }
public virtual NamedProperty<int> B { get; set; }
public Tracker<NamedData> Tracker { get; set; }
}
Basically I've copy-pasted the original Data model but changed all its properties to be aware of their names.
Then the tracker itself:
public class Tracker<T>
where T : class, ISelfTracker<T>
{
public T Instance { get; }
public T Proxy { get; }
public Tracker(T instance)
{
Instance = instance;
Proxy = new ProxyGenerator().CreateClassProxyWithTarget<T>(Instance, new TrackingNamedProxyInterceptor<T>(this));
Proxy.Tracker = this;
}
public void RecordChange(string setterName, string getterName)
{
}
public void UnTrackChange(string setterName)
{
}
}
The interceptor for Castle.DynamicProxy:
public class TrackingNamedProxyInterceptor<T> : IInterceptor
where T : class, ISelfTracker<T>
{
private const string SetterPrefix = "set_";
private const string GetterPrefix = "get_";
private readonly Tracker<T> _tracker;
public TrackingNamedProxyInterceptor(Tracker<T> proxy)
{
_tracker = proxy;
}
public void Intercept(IInvocation invocation)
{
if (IsSetMethod(invocation.Method))
{
string propertyName = GetPropertyName(invocation.Method);
dynamic value = invocation.Arguments[0];
var propertyType = value.GetType();
if (IsOfGenericType(propertyType, typeof(NamedProperty<>)))
{
if (value.Name == null)
{
_tracker.UnTrackChange(propertyName);
}
else
{
_tracker.RecordChange(propertyName, value.Name);
}
var args = new[] { propertyName, value.Value };
invocation.Arguments[0] = Activator.CreateInstance(propertyType, args);
}
}
invocation.Proceed();
}
private string GetPropertyName(MethodInfo method)
=> method.Name.Replace(SetterPrefix, string.Empty).Replace(GetterPrefix, string.Empty);
private bool IsSetMethod(MethodInfo method)
=> method.IsSpecialName && method.Name.StartsWith(SetterPrefix);
private bool IsOfGenericType(Type type, Type openGenericType)
=> type.IsGenericType && type.GetGenericTypeDefinition() == openGenericType;
}
And the modified entry point:
static void Main(string[] args)
{
var data = new Data() { A = 1, B = 2 };
NamedData namedData = Map(data);
var proxy = new Tracker<NamedData>(namedData).Proxy;
Runner.Run(proxy);
Console.ReadLine();
}
The Map() function actually maps Data to NamedData filling in property names.

How to persist a list of strings with Entity Framework Core?

Let us suppose that we have one class which looks like the following:
public class Entity
{
public IList<string> SomeListOfValues { get; set; }
// Other code
}
Now, suppose we want to persist this using EF Core Code First and that we are using a RDMBS like SQL Server.
One possible approach is obviously to create a wraper class Wraper which wraps the string:
public class Wraper
{
public int Id { get; set; }
public string Value { get; set; }
}
And to refactor the class so that it now depends on a list of Wraper objects. In that case EF would generate a table for Entity, a table for Wraper and stablish a "one-to-many" relation: for each entity there is a bunch of wrapers.
Although this works, I don't quite like the approach because we are changing a very simple model because of persistence concerns. Indeed, thinking just about the domain model, and the code, without the persistence, the Wraper class is quite meaningless there.
Is there any other way persist one entity with a list of strings to a RDBMS using EF Core Code First other than creating a wraper class? Of course, in the end the same thing must be done: another table must be created to hold the strings and a "one-to-many" relationship must be in place. I just want to do this with EF Core without needing to code the wraper class in the domain model.
This can be achieved in a much more simple way starting with Entity Framework Core 2.1. EF now supports Value Conversions to specifically address scenarios like this where a property needs to be mapped to a different type for storage.
To persist a collection of strings, you could setup your DbContext in the following way:
protected override void OnModelCreating(ModelBuilder builder)
{
var splitStringConverter = new ValueConverter<IEnumerable<string>, string>(v => string.Join(";", v), v => v.Split(new[] { ';' }));
builder.Entity<Entity>()
.Property(nameof(Entity.SomeListOfValues))
.HasConversion(splitStringConverter);
}
Note that this solution does not litter your business class with DB concerns.
Needless to say that this solution, one would have to make sure that the strings cannot contains the delimiter. But of course, any custom logic could be used to make the conversion (e.g. conversion from/to JSON).
Another interesting fact is that null values are not passed into the conversion routine but rather handled by the framework itself. So one does not need to worry about null checks inside the conversion routine. However, the whole property becomes null if the database contains a NULL value.
What about Value Comparers?
Creating a migration using this converter leads to the following warning:
The property 'Entity.SomeListOfValues' is a collection or enumeration type with a value converter but with no value comparer. Set a value comparer to ensure the collection/enumeration elements are compared correctly.
Setting the correct comparer for the suggested converter depends on the semantics of your list. For example, if you do not care about the order of its elements, you can use the following comparer:
new ValueComparer<IEnumerable<string>>(
(c1, c2) => new HashSet<string>(c1!).SetEquals(new HashSet<string>(c2!)),
c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())),
c => c.ToList()
);
Using this comparer, a reordered list with the same elements would not be detected as changed an thus a roundtrip to the database can be avoided. For more information on the topic of Value Comparers, consider the docs.
UPDATE EF CORE 6.0
In order to benefit from Entity Framework Core 6.0 Compiled Models, we can use the generic overload of HasConversion. So the full picture becomes:
builder.Entity<Foo>()
.Property(nameof(Foo.Bar))
.HasConversion<SemicolonSplitStringConverter, SplitStringComparer>();
...
public class SplitStringComparer : ValueComparer<IEnumerable<string>>
{
public SplitStringComparer() : base(
(c1, c2) => new HashSet<string>(c1!).SetEquals(new HashSet<string>(c2!)),
c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v.GetHashCode())))
{
}
}
public abstract class SplitStringConverter : ValueConverter<IEnumerable<string>, string>
{
protected SplitStringConverter(char delimiter) : base(
v => string.Join(delimiter.ToString(), v),
v => v.Split(new[] { delimiter }, StringSplitOptions.RemoveEmptyEntries))
{
}
}
public class SemicolonSplitStringConverter : SplitStringConverter
{
public SemicolonSplitStringConverter() : base(';')
{
}
}
You could use the ever useful AutoMapper in your repository to achieve this while keeping things neat.
Something like:
MyEntity.cs
public class MyEntity
{
public int Id { get; set; }
public string SerializedListOfStrings { get; set; }
}
MyEntityDto.cs
public class MyEntityDto
{
public int Id { get; set; }
public IList<string> ListOfStrings { get; set; }
}
Set up the AutoMapper mapping configuration in your Startup.cs:
Mapper.Initialize(cfg => cfg.CreateMap<MyEntity, MyEntityDto>()
.ForMember(x => x.ListOfStrings, opt => opt.MapFrom(src => src.SerializedListOfStrings.Split(';'))));
Mapper.Initialize(cfg => cfg.CreateMap<MyEntityDto, MyEntity>()
.ForMember(x => x.SerializedListOfStrings, opt => opt.MapFrom(src => string.Join(";", src.ListOfStrings))));
Finally, use the mapping in MyEntityRepository.cs so that your business logic doesnt have to know or care about how the List is handled for persistence:
public class MyEntityRepository
{
private readonly AppDbContext dbContext;
public MyEntityRepository(AppDbContext context)
{
dbContext = context;
}
public MyEntityDto Create()
{
var newEntity = new MyEntity();
dbContext.MyEntities.Add(newEntity);
var newEntityDto = Mapper.Map<MyEntityDto>(newEntity);
return newEntityDto;
}
public MyEntityDto Find(int id)
{
var myEntity = dbContext.MyEntities.Find(id);
if (myEntity == null)
return null;
var myEntityDto = Mapper.Map<MyEntityDto>(myEntity);
return myEntityDto;
}
public MyEntityDto Save(MyEntityDto myEntityDto)
{
var myEntity = Mapper.Map<MyEntity>(myEntityDto);
dbContext.MyEntities.Save(myEntity);
return Mapper.Map<MyEntityDto>(myEntity);
}
}
You are right, you do not want to litter your domain model with persistence concerns. The truth is, if you use your same model for your domain and persistence, you will not be able to avoid the issue. Especially using Entity Framework.
The solution is, build your domain model without thinking about the database at all. Then build a separate layer which is responsible for the translation. Something along the lines of the 'Repository' pattern.
Of course, now you have twice the work. So it is up to you to find the right balance between keeping your model clean and doing the extra work. Hint: The extra work is worth it in bigger applications.
This might be late, but you can never tell who it might help.
See my solution based on the previous answer
First, you are going to need this reference using System.Collections.ObjectModel;
Then extend the ObservableCollection<T> and add an implicit operator overload for a standard list
public class ListObservableCollection<T> : ObservableCollection<T>
{
public ListObservableCollection() : base()
{
}
public ListObservableCollection(IEnumerable<T> collection) : base(collection)
{
}
public ListObservableCollection(List<T> list) : base(list)
{
}
public static implicit operator ListObservableCollection<T>(List<T> val)
{
return new ListObservableCollection<T>(val);
}
}
Then create an abstract EntityString class (This is where the good stuff happens)
public abstract class EntityString
{
[NotMapped]
Dictionary<string, ListObservableCollection<string>> loc = new Dictionary<string, ListObservableCollection<string>>();
protected ListObservableCollection<string> Getter(ref string backingFeild, [CallerMemberName] string propertyName = null)
{
var file = backingFeild;
if ((!loc.ContainsKey(propertyName)) && (!string.IsNullOrEmpty(file)))
{
loc[propertyName] = GetValue(file);
loc[propertyName].CollectionChanged += (a, e) => SetValue(file, loc[propertyName]);
}
return loc[propertyName];
}
protected void Setter(ref string backingFeild, ref ListObservableCollection<string> value, [CallerMemberName] string propertyName = null)
{
var file = backingFeild;
loc[propertyName] = value;
SetValue(file, value);
loc[propertyName].CollectionChanged += (a, e) => SetValue(file, loc[propertyName]);
}
private List<string> GetValue(string data)
{
if (string.IsNullOrEmpty(data)) return new List<string>();
return data.Split(';').ToList();
}
private string SetValue(string backingStore, ICollection<string> value)
{
return string.Join(";", value);
}
}
Then use it like so
public class Categorey : EntityString
{
public string Id { get; set; }
public string Name { get; set; }
private string descriptions = string.Empty;
public ListObservableCollection<string> AllowedDescriptions
{
get
{
return Getter(ref descriptions);
}
set
{
Setter(ref descriptions, ref value);
}
}
public DateTime Date { get; set; }
}
Extending on the already accepted answer of adding a ValueConverter within the OnModelCreating; you can have this map out for all entities rather than just explicit ones, and you can support storing delimiting characters:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
foreach (var entity in modelBuilder.Model.GetEntityTypes())
{
foreach (var property in entity.ClrType.GetProperties())
{
if (property.PropertyType == typeof(List<string>))
{
modelBuilder.Entity(entity.Name)
.Property(property.Name)
.HasConversion(new ValueConverter<List<string>, string>(v => JsonConvert.SerializeObject(v), v => JsonConvert.DeserializeObject<List<string>>(v)));
}
}
}
}
So the end result is a serialized array of strings in the database. This approach can also work on other serializable types as well (Dictionary<string, string>, simple DTO or POCO objects...
There is a purist deep down somewhere in me that is mad about persisting seralized data into a database, but I have grown to ignore it every once and a while.
I implemented a possible solution by creating a new StringBackedList class, where the actual list content is backed by a string. It works by updating the backing string whenever the list is modified, using Newtonsoft.Json as the serializer (because I already use that in my project, but any would work).
You use the list like this:
public class Entity
{
// that's what stored in the DB, and shouldn't be accessed directly
public string SomeListOfValuesStr { get; set; }
[NotMapped]
public StringBackedList<string> SomeListOfValues
{
get
{
// this can't be created in the ctor, because the DB isn't read yet
if (_someListOfValues == null)
{
// the backing property is passed 'by reference'
_someListOfValues = new StringBackedList<string>(() => this.SomeListOfValuesStr);
}
return _someListOfValues;
}
}
private StringBackedList<string> _someListOfValues;
}
Here's the implementation of the StringBackedList class. For ease of use, the backing property is passed by reference, using this solution.
using Newtonsoft.Json;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
namespace Model
{
public class StringBackedList<T> : IList<T>
{
private readonly Accessor<string> _backingStringAccessor;
private readonly IList<T> _backingList;
public StringBackedList(Expression<Func<string>> expr)
{
_backingStringAccessor = new Accessor<string>(expr);
var initialValue = _backingStringAccessor.Get();
if (initialValue == null)
_backingList = new List<T>();
else
_backingList = JsonConvert.DeserializeObject<IList<T>>(initialValue);
}
public T this[int index] {
get => _backingList[index];
set { _backingList[index] = value; Store(); }
}
public int Count => _backingList.Count;
public bool IsReadOnly => _backingList.IsReadOnly;
public void Add(T item)
{
_backingList.Add(item);
Store();
}
public void Clear()
{
_backingList.Clear();
Store();
}
public bool Contains(T item)
{
return _backingList.Contains(item);
}
public void CopyTo(T[] array, int arrayIndex)
{
_backingList.CopyTo(array, arrayIndex);
}
public IEnumerator<T> GetEnumerator()
{
return _backingList.GetEnumerator();
}
public int IndexOf(T item)
{
return _backingList.IndexOf(item);
}
public void Insert(int index, T item)
{
_backingList.Insert(index, item);
Store();
}
public bool Remove(T item)
{
var res = _backingList.Remove(item);
if (res)
Store();
return res;
}
public void RemoveAt(int index)
{
_backingList.RemoveAt(index);
Store();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _backingList.GetEnumerator();
}
public void Store()
{
_backingStringAccessor.Set(JsonConvert.SerializeObject(_backingList));
}
}
// this class comes from https://stackoverflow.com/a/43498938/2698119
public class Accessor<T>
{
private Action<T> Setter;
private Func<T> Getter;
public Accessor(Expression<Func<T>> expr)
{
var memberExpression = (MemberExpression)expr.Body;
var instanceExpression = memberExpression.Expression;
var parameter = Expression.Parameter(typeof(T));
if (memberExpression.Member is PropertyInfo propertyInfo)
{
Setter = Expression.Lambda<Action<T>>(Expression.Call(instanceExpression, propertyInfo.GetSetMethod(), parameter), parameter).Compile();
Getter = Expression.Lambda<Func<T>>(Expression.Call(instanceExpression, propertyInfo.GetGetMethod())).Compile();
}
else if (memberExpression.Member is FieldInfo fieldInfo)
{
Setter = Expression.Lambda<Action<T>>(Expression.Assign(memberExpression, parameter), parameter).Compile();
Getter = Expression.Lambda<Func<T>>(Expression.Field(instanceExpression, fieldInfo)).Compile();
}
}
public void Set(T value) => Setter(value);
public T Get() => Getter();
}
}
Caveats: the backing string is only updated when the list itself is modified. Updating a list element via direct access (e.g. via the list indexer) requires a manual call to the Store() method.
I have found a trick and I think this is a very usefull workaround to solve this kind of the problem:
public class User
{
public long UserId { get; set; }
public string Name { get; set; }
private string _stringArrayCore = string.Empty;
// Warnning: do not use this in Bussines Model
public string StringArrayCore
{
get
{
return _stringArrayCore;
}
set
{
_stringArrayCore = value;
}
}
[NotMapped]
public ICollection<string> StringArray
{
get
{
var splitString = _stringArrayCore.Split(';');
var stringArray = new Collection<string>();
foreach (var s in splitString)
{
stringArray.Add(s);
}
return stringArray;
}
set
{
_stringArrayCore = string.Join(";", value);
}
}
}
How to use:
// Write user
using (var userDbContext = new UserSystemDbContext())
{
var user = new User { Name = "User", StringArray = new Collection<string>() { "Bassam1", "Bassam2" } };
userDbContext.Users.Add(user);
userDbContext.SaveChanges();
}
// Read User
using (var userDbContext = new UserSystemDbContext())
{
var user = userDbContext.Users.ToList().Last();
foreach (var userArray in user.StringArray)
{
Console.WriteLine(userArray);
}
}
in the database
Table Users:
UserId | Name | StringArrayCore
1 | User | Bassam1;Bassam2

Automapper - Add to List conditionally

The situation is -
public class One
{
public string S1 { get;set; }
public string S2 { get;set; }
public string S3 { get;set; }
public string S4 { get;set; }
}
public class Two
{
public List<string> List1 { get;set; }
}
Now what I want is to populate the list inside Two with the non-null property values of One.
Is there anyway / work-around to achieve this using AutoMapper ?
In this case you can create your own custom value resolver:
public class CustomResolver : ValueResolver<One, List<string>>
{
protected override List<string> ResolveCore(One source)
{
var result = new List<string>();
//your logic
if (!string.IsNullOrWhiteSpace(source.S1))
result.Add(source.S1);
if (!string.IsNullOrWhiteSpace(source.S2))
result.Add(source.S2);
if (!string.IsNullOrWhiteSpace(source.S3))
result.Add(source.S3);
if (!string.IsNullOrWhiteSpace(source.S4))
result.Add(source.S4);
return result;
}
}
Mapping configuration:
Mapper.CreateMap<One, Two>()
.ForMember(dest => dest.List1, opt => opt.ResolveUsing<CustomResolver>());
In this way you are able to configure mappings for specific properties
If that is the only mapping you want to do between the classes, you can use a custom type converter: this then takes complete charge of the mapping between the objects.
public class OneTwoTypeResolver : TypeConverter<One, Two>
{
protected override Two ConvertCore(One source)
{
Two two = new Two {List1 = new List<string>()};
if (source.S1 != null)
two.List1.Add(source.S1);
if (source.S2 != null)
two.List1.Add(source.S2);
if (source.S3 != null)
two.List1.Add(source.S3);
if (source.S4 != null)
two.List1.Add(source.S4);
return two;
}
}
You tell AutoMapper to use this class when mapping between the classes:
Mapper.CreateMap<One, Two>().ConvertUsing<OneTwoTypeResolver>();
Then this test will pass:
public void Test()
{
One one = new One {S2 = "OneTwoThreeFour"};
Two two = Mapper.Map<One, Two>(one);
Assert.AreEqual(1, two.List1.Count);
Assert.AreEqual("OneTwoThreeFour", two.List1.Single());
}

How to access anonymous method from generic list?

I've been working on a library to generate fake data using Faker.NET. The problem I'm having is that I don't know how to access an anonymous method that I'm passing to the constructor of my DataGenerator child classes.
The issue is that in order to create a list of generics I had to create base class DataGenerator but I cannot pull my Func<T> member up because that base class is not generic so no Tavailable. However, my DataGenerator<T> class does expose the Generator property which is my anonymous method but I haven't found a way to access it while iterating my list of data generators.
Any advice will be highly appreciated.
This is what I have so far:
public class Employee
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Guid EmpUid { get; set; }
}
// Define other methods and classes here
public abstract class DataGenerator
{
public abstract int GetWeight(string matchingProperty);
public abstract Type Type { get;}
}
public abstract class DataGenerator<T> : DataGenerator
{
public readonly string[] Tags;
public readonly Func<T> Generator;
protected DataGenerator(Func<T> generator, params string[] tags)
{
Tags = tags;
//How to access this?
Generator = generator;
}
public override int GetWeight(string matchingProperty)
{
int sum = (from tag in Tags
where matchingProperty.ToLowerInvariant().Contains(tag.ToLowerInvariant())
select 1).Sum();
return sum;
}
public override Type Type {
get { return typeof(T); }
}
}
public class StringDataGenerator : DataGenerator<string>
{
public StringDataGenerator(Func<string> generator, params string[] tags) : base(generator, tags)
{
}
}
public class GuidDataGenerator : DataGenerator<Guid>
{
public GuidDataGenerator(Func<Guid> generator, params string[] tags)
: base(generator, tags)
{
}
}
And I'm testing it here:
private static void Main(string[] args)
{
var dataGeneratorList = new List<DataGenerator>
{
new StringDataGenerator(Name.First, "first", "name"),
new StringDataGenerator(Name.Last, "last", "name"),
new GuidDataGenerator(Guid.NewGuid, "uid", "id")
};
var writeProperties = typeof (Employee).GetProperties().Where(p => p.CanWrite);
foreach (var property in writeProperties)
{
foreach (var dataGenerator in dataGeneratorList)
{
if (property.PropertyType == dataGenerator.Type)
{
var weigth = dataGenerator.GetWeight(property.Name);
//How to access generator here???
var testValue = dataGenerator.Generator.Invoke();
}
}
}
}
As you tagged, given your current setup, reflection is probably your only option.
var func = dataGenerator.GetType().GetField("Generator").GetValue(dataGenerator);
var testValue = func.GetType().GetMethod("Invoke").Invoke(func, null);
I'm not sure anyone could call this super nice, and it won't be super fast, but it's probably sufficient for anything you need fake data in, I suppose.
For good measure, here's it in action.
Your question is actually a bit more complicated than it may seem at face-value. A nice way of handling this if you only ever use it in object form is just to add an abstract Generate method to the base, non-generic class:
public abstract object Generate();
Then override it in your generic one:
public override object Generate()
{
return this.Generator();
}
Of course, this return an object, which isn't nice in a generic class. But at least it avoids reflection.
Another solution to avoid this reflection nonsense might be the use of covariance, although that will, unfortunately, break for value types, like Guid.
public interface IDataGenerator<out T>
{
int GetWeight(string matchingProperty);
Type Type { get;}
T Generate();
}
public abstract class DataGenerator<T> : IDataGenerator<T>
{
public readonly string[] Tags;
public readonly Func<T> Generator;
protected DataGenerator(Func<T> generator, params string[] tags)
{
Tags = tags;
//How to access this?
Generator = generator;
}
public T Generate(){
return this.Generator();
}
. . .
}
That then turns into a preferable,
private static void Main(string[] args)
{
var dataGeneratorList = new List<IDataGenerator<object>>
{
new StringDataGenerator(Name.First, "first", "name"),
new StringDataGenerator(Name.Last, "last", "name")
// But this line doesn't work
// new GuidDataGenerator(Guid.NewGuid, "uid", "id")
};
var writeProperties = typeof (Employee).GetProperties().Where(p => p.CanWrite);
foreach (var property in writeProperties)
{
foreach (var dataGenerator in dataGeneratorList)
{
if (property.PropertyType == dataGenerator.Type)
{
var weigth = dataGenerator.GetWeight(property.Name);
var testValue = dataGenerator.Generate();
}
}
}
}

Categories