MongoDB mapping C# - c#

How can you map Value object that has different data type in constructor than property type.
public class Information : ValueObject
{
private const string Delimiter = ",";
public Information(string value)
{
Value = SetValueAsReadOnlyList(value);
}
public ReadOnlyCollection<int> Value { get; }
private ReadOnlyCollection<int> SetValueAsReadOnlyList(string value)
{
var collection = value
.Split(Delimiter)
.Select(x =>
{
if(int.Parse(x, out var result))
{
return result;
}
throw new ParseStringToIntDomainException(x);
}).ToList();
return collection.AsReadOnly();
}
}
Mongo map will look like this, which is not working because x.Value is of type string and constructor is expecting ReadOnlyCollection
public class InformationMap : IBsonClassMap
{
public void Map()
{
if (BsonClassMap.IsClassMapRegistered(typeof(Information)))
{
return;
}
BsonClassMap.RegisterClassMap<Information>(map =>
{
map.MapCreator(x => new Information(x.Value));
map.MapProperty(x => x.Value);
});
}
}
I don't think it's possible. I can wrap Value to another nested data type with it's own mapping. But I would need to define transformation inside InformationMap class.

Related

AutoMapper Mapping one property to two properties by condition

There are two classes
class A
{
public string ProductionDivision { get; set; }
}
class B
{
private object _productionDivision;
public enum ProductionDivisionOneofCase
{
None = 0,
IsNullproductionDivision = 15,
ProductionDivisionValue = 16,
}
private ProductionDivisionOneofCase _productionDivisionCase = ProductionDivisionOneofCase.None;
public bool IsNullProductionDivision
{
get { return _productionDivisionCase == ProductionDivisionOneofCase.IsNullproductionDivision ? (bool)_productionDivision : false; }
set
{
_productionDivision = value;
_productionDivisionCase = ProductionDivisionOneofCase.IsNullproductionDivision;
}
}
public string ProductionDivisionValue
{
get { return _productionDivisionCase == ProductionDivisionOneofCase.ProductionDivisionValue ? (string)_productionDivision : ""; }
set
{
_productionDivision = value;
_productionDivisionCase = ProductionDivisionOneofCase.ProductionDivisionValue;
}
}
}
I would like to map the ProductionDivision property to one of properties of class B depending on the condition - null(map to IsNullProductionDivision) or not null(map to ProductionDivisionValue) of the source property value. I can achieve it like as below.
CreateMap<A, B>()
.ForMember(g => g.IsNullProductionDivision, m =>
{
m.PreCondition(s => s.ProductionDivision == null);
m.MapFrom(b => true);
})
.ForMember(g => g.ProductionDivisionValue, m =>
{
m.PreCondition(s => s.ProductionDivision != null);
m.MapFrom(b => b.ProductionDivision);
});
If the value of {source property name} is null then the value of IsNull{source property name} is true.
Else if the value of {source property name} is not null then the value of {source property name}Value is the value of {source property name}.
I have many properties that respond this mapping rule. So, I don't want to write mapping rule for each properties like above. I want to configurate a rule for such mapping globally.
How can I configure AutoMapper so that it can handle such complex mapping?
I have found solution. The solution is pretty simple and clear. It turns out as follow:
Full code:
public class Program
{
class A
{
public string ProductionDivision { get; set; }
}
class B
{
private object _productionDivision;
public enum ProductionDivisionOneofCase
{
None = 0,
IsNullproductionDivision = 15,
ProductionDivisionValue = 16,
}
private ProductionDivisionOneofCase _productionDivisionCase = ProductionDivisionOneofCase.None;
public bool IsNullProductionDivision
{
get { return _productionDivisionCase == ProductionDivisionOneofCase.IsNullproductionDivision ? (bool)_productionDivision : false; }
set
{
_productionDivision = value;
_productionDivisionCase = ProductionDivisionOneofCase.IsNullproductionDivision;
}
}
public string ProductionDivisionValue
{
get { return _productionDivisionCase == ProductionDivisionOneofCase.ProductionDivisionValue ? (string)_productionDivision : ""; }
set
{
_productionDivision = value;
_productionDivisionCase = ProductionDivisionOneofCase.ProductionDivisionValue;
}
}
}
public class StrinToBoolCustomResolver
: IValueConverter<string, bool>
{
public bool Convert(string sourceMember, ResolutionContext context)
{
return sourceMember == null;
}
}
public class MyAutoMapperProfile
: Profile
{
public MyAutoMapperProfile()
{
// add post and pre prefixes to add corresponding properties in the inner property map
RecognizeDestinationPostfixes("Value");
RecognizeDestinationPrefixes("IsNull");
// add mapping for "value" property
this.ForAllPropertyMaps(map => map.SourceMember.Name + "Value" == map.DestinationName,
(map, expression) =>
{
expression.Condition((source, dest, sMem, destMem) =>
{
return sMem != null;
});
expression.MapFrom(map.SourceMember.Name);
});
// add mapping for "IsNull" property
this.ForAllPropertyMaps(map => "IsNull" + map.SourceMember.Name == map.DestinationName,
(map, expression) =>
{
expression.Condition((source, dest, sMem, destMem) =>
{
return (bool)sMem;
});
//expression.MapFrom(map.SourceMember.Name);
expression.ConvertUsing<string, bool>(new StrinToBoolCustomResolver(), map.SourceMember.Name);
});
CreateMap<A, B>();
//.ForMember(g => g.IsNullProductionDivision, m =>
//{
// m.PreCondition(s => s.ProductionDivision == null);
// m.MapFrom(b => true);
//});
//.ForMember(g => g.ProductionDivisionValue, m =>
//{
// m.PreCondition(s => s.ProductionDivision != null);
// m.MapFrom(b => b.ProductionDivision);
//});
}
}
public static void Main(string[] args)
{
var configuration = new MapperConfiguration(cfg => {
cfg.AddProfile(new MyAutoMapperProfile());
});
var mapper = new Mapper(configuration);
mapper.ConfigurationProvider.AssertConfigurationIsValid();
var a = new A()
{
ProductionDivision = null
};
// b._productionDivisionCase will be equal IsNullproductionDivision
var b = mapper.Map<B>(a);
var c = new A()
{
ProductionDivision = "dsd"
};
// d._productionDivisionCase will be equal ProductionDivisionValue
var d = mapper.Map<B>(c);
}
}
Clarification:
add (post/pre)fixes to add corresponding properties to inner property map. It need to do here because our properties should be catched by AutoMapper. Otherwise properties will be abandoned and mapper configuration will be failed.
After that, we configurate how these properties need to be mapping. We call ForAllPropertyMaps method, filtering all properties and setting a rule to mapping properties suitable with our filter. When the mapper object is creating, the execution plan will be built taking look the specified filters.
For "Value" property we add a condition to check whether the source property is not null. If it is null, then mapping will be missed.
For "IsNull" property First of all, we add a converter to convert string type to bool type. The converter just compares the source string property with null value. If the source property equals null, then the converter returns true. So, the condition receives a true value, returns true, and mapping will be done. Otherwise the converter returns false, so the condition returns false and mapping will be missed.
Thus, the mapping of source property will occur to different destination properties depending on whether the source property is null value. Besides that, corresponding set methods of corresponding destination properties will be not called if it not must to be called.

Add dynamic type to List<object> using reflection

I am creating a Unity custom window, This window displays data from from an object that will eventually be converted to JSON.
I am able to read the data and modify it as long as it isn't an array which is where I am having issues.
The data looks like this:
public static class GameData {
private static SaveData data;
public static SaveData save { get { return data.save; } }
[System.Serializable]
public class SaveData {
public int energyCurrent = 100;
public float speed = 2.5f;
public List<int> itm = new List<int>() { 1, 2, 3, 4, 5, 6 };
}
}
I then have an object which stores each item like this (where they key is the field name and value is the field value; ex: key=speed, value=2.5f):
class KeyValue {
public string key;
public object value;
public KeyValue(string key, object value) {
this.key = key;
this.value = value;
}
}
It is then stored within a list:
List<KeyValue> keyValues = new List<KeyValue>();
The part I am having issues with is the save which looks like this:
void SaveDataLocal() {
keyValues.ForEach(item => {
// GameData.save is a reference to SaveData
var field = GameData.save.GetType().GetField(item.key);
field.SetValue(GameData.save, GetValue(item));
});
GameData.Save();
}
object GetValue(KeyValue keyVal) {
var value = keyVal.value;
if (value.GetType().IsArray || isList(value)) {
List<object> list = new List<object>();
((List<KeyValue>)value).ForEach(item => {
list.add(GetValue(item));
});
return list;
} else {
return value;
}
}
I have tried two ways to update the value, however, I am getting an error saying:
ArgumentException: Object type System.Collections.Generic.List`1[System.Object] cannot be converted to target type: System.Collections.Generic.List`1[System.Int32]
I am trying to use Reflection because list type could be int, float, Vector3, etc. so I need it to by dynamic, and I cannot use a dynamic type because we are not using 4.x.
The error is taking place here:
field.SetValue(GameData.save, GetValue(item));
GetValue() returning a List<object> when the field is a List<int>. What can I do to pass the List<object>?
Edit
I have tried casting it like so:
public static T Cast<T>(object o) {
return (T)o;
}
keyValues.ForEach(item => {
var field = GameData.save.GetType().GetField(item.key);
var val = GetValue(item);
var castMethod = this.GetType().GetMethod("Cast").MakeGenericMethod(field.FieldType);
var r = castMethod.Invoke(null, new object[] { val });
field.SetValue(GameData.save, r);
});
But I am getting this error:
InvalidCastException: Cannot cast from source type to destination type.
GameSmart.SaveData.Cast[List`1]
I am not sure if this is the cleanest way, but I basically had to do a cast by looping over all the items in the list/array and individually casting them. I can't seem to cast an entire list at once.
public static List<T> CastList<T>(List<object> o) {
var list = new List<T>();
foreach (var i in o) { list.Add((T)i); }
return list;
}
void SaveDataLocal() {
keyValues.ForEach(item => {
var field = GameData.save.GetType().GetField(item.key);
var value = GetValue(item);
if (value.GetType().IsArray || isList(value)) {
Type type;
if (isList(value)) type = field.FieldType.GetGenericArguments().Single();
else type = field.FieldType.GetElementType();
var castMethod = this.GetType().GetMethod("CastList").MakeGenericMethod(type);
value = castMethod.Invoke(null, new object[] { value });
}
field.SetValue(GameData.save, value);
});
GameData.Save();
}

Build dynamic predicate based on generic type

How do I make this expression dynamic based on the generic type passed in the parameter?
In the simplified form:
public static class CompareService
{
public static List<T> Run<T>(List<T> database_list, string directory_path)
{
var csv_list = CompareService.MergeRecordsFromFiles<T>(directory);
return CompareService.RunComparison<T>(database_list, csv_list);
}
public static T CompareData<T>(List<T> database_list, List<T> csv_list)
{
var diff = new List<T>();
foreach (var db_item in database_list)
{
// ...
// if T is of type Deathstar compare reference_number property
// if T is of type Stormtrooper compare id property
// if T is of type Sith compare id and anger_level property
var csv_item = csv_list.FirstOrDefault(x => x.reference_number == db_item.reference_number);
// Comparison code
ComparisonResult result = compareLogic.Compare(db_item, csv_item);
// ...
}
return diff;
}
}
It is called from another generic service:
public static void Whatever<T>(List<T> list)
{
// ...
var directory_path = "C:\";
var delta = CompareService.CompareData<T>(list, directory_path);
// ...
}
The most naive implementation would be to check if your itemToFind can be cast to DeathStar, StormTrooper or Sith and if so call the instances property.
var deathStar = itemToFind as DeathStar;
if(deathStar != null)
return database_list.Where(x => ((DeathStar)x).reference_number == deathStar.reference_number).FirstOrDefault();
else
{
var sith = itemToFind as Sith;
if(sith != null)
return database_list.Where(x => ((Sith)x).anger_level == sith.anger_level).FirstOrDefault();
else
return database_list.Where(x => ((StormTrooper)x).id== ((StormTrooper)item).id).FirstOrDefault();
}
This is quite cumbersome, including many casts. In particular it completely bypasses the actual benefits of generics using any arbitrary type (that fullfills the constraints if existing). In your case you´d have a generic method that will only wortk for three decent types.
A better approach is to let all your classes implement a common interface that defines a property, for instance:
interface IObject {
int Level { get; }
}
Now all classes define that level-property:
clas DeathStar : IObject
{
public int Level { get { return this.reference_number; } }
}
clas Sith : IObject
{
public int Level { get { return this.anger_level; } }
}
clas StormTrooper: IObject
{
public int Level { get { return this.id; } }
}
Than you can use a constraint on your type T to implement that interface:
public static T CompareData<T>(List<T> list, T itemToFind) where T: IObject
Why not like this:
public static T CompareData<T>(List<T> list, Func<T, bool> predicate)
{
return database_list.FirstOrDefault(predicate);
}
And then use it like this:
var itemToFind = new ItemToFind();
var myObjectList = new List<MyObject>();
var item = CompareData<MyObject>(myObjectList, x=> x.MyObjectProperty == itemToFind.Id);
You could add a property selector:
public static class CompareService
{
public static T CompareData<T>(this List<T> list, T itemToFind, Func<T, int> propSelector)
{
int propToFind = propSelector(itemToFind); // cache
return database_list.FirstOrDefault(x => propSelector(x) == propToFind);
}
}
And call it like that:
listOfDeathstars.CompareData(deathStarToFind, ds => ds.reference_number);
listOfStormtroopers.CompareData(trooperToFind, t => t.id);
listOfSiths.CompareData(sithStarToFind, sith => new { sith.id, sith.anger_level});
Note: I added the this keyword in the signature to make it an extension (not sure if you intended that but forgot the keyword). And Where(predicate).FirstOrDefault() can be reduced to FirstOrDefault(predicate).

Custom Mapping with AutoMapper

I have two very simple objects:
public class CategoryDto
{
public string Id { get; set; }
public string MyValueProperty { get; set; }
}
public class Category
{
public string Id { get; set; }
[MapTo("MyValueProperty")]
public string Key { get; set; }
}
When mapping a Category to a CategoryDto with AutoMapper, I would like the following behavior:
The properties should be mapped as usual, except for those that have the MapTo attribute. In this case, I have to read the value of the Attribute to find the target property. The value of the source property is used to find the value to inject in the destination property (with the help of a dictionary). An example is always better that 1000 words...
Example:
Dictionary<string, string> keys =
new Dictionary<string, string> { { "MyKey", "MyValue" } };
Category category = new Category();
category.Id = "3";
category.Key = "MyKey";
CategoryDto result = Map<Category, CategoryDto>(category);
result.Id // Expected : "3"
result.MyValueProperty // Expected : "MyValue"
The Key property is mapped to the MyValueProperty (via the MapTo Attribute), and the assigned value is "MyValue", because the source property value is "MyKey" which is mapped (via dictionary) to "MyValue".
Is this possible using AutoMapper ? I need of course a solution that works on every object, not just on Category/CategoryDto.
I finally (after so many hours !!!!) found a solution.
I share this with the community; hopefully it will help someone else...
Edit: Note that it's now much simpler (AutoMapper 5.0+), you can do like I answered in this post: How to make AutoMapper truncate strings according to MaxLength attribute?
public static class Extensions
{
public static IMappingExpression<TSource, TDestination> MapTo<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
{
Type sourceType = typeof(TSource);
Type destinationType = typeof(TDestination);
TypeMap existingMaps = Mapper.GetAllTypeMaps().First(b => b.SourceType == sourceType && b.DestinationType == destinationType);
string[] missingMappings = existingMaps.GetUnmappedPropertyNames();
if (missingMappings.Any())
{
PropertyInfo[] sourceProperties = sourceType.GetProperties();
foreach (string property in missingMappings)
{
foreach (PropertyInfo propertyInfo in sourceProperties)
{
MapToAttribute attr = propertyInfo.GetCustomAttribute<MapToAttribute>();
if (attr != null && attr.Name == property)
{
expression.ForMember(property, opt => opt.ResolveUsing(new MyValueResolve(propertyInfo)));
}
}
}
}
return expression;
}
}
public class MyValueResolve : IValueResolver
{
private readonly PropertyInfo pInfo = null;
public MyValueResolve(PropertyInfo pInfo)
{
this.pInfo = pInfo;
}
public ResolutionResult Resolve(ResolutionResult source)
{
string key = pInfo.GetValue(source.Value) as string;
string value = dictonary[key];
return source.New(value);
}
}
This should be fairly straightforward using an implementation of IValueResolver and the ResolveUsing() method. You basically just need to have a constructor for the resolver that takes in the property info (or if you wanna be fancy that takes in a lambda expression and resolves the property info similar to How to get the PropertyInfo of a specific property?. Though I haven't tested it myself I imagine the following would work:
public class PropertyBasedResolver : IValueResolver
{
public PropertyInfo Property { get; set; }
public PropertyBasedResolver(PropertyInfo property)
{
this.Property = property;
}
public ResolutionResult Resolve(ResolutionResult source)
{
var result = GetValueFromKey(property, source.Value); // gets from some static cache or dictionary elsewhere in your code by reading the prop info and then using that to look up the value based on the key as appropriate
return source.New(result)
}
}
Then to set up that mapping you need to do:
AutoMapper.Mapper.CreateMap<Category, CategoryDto>()
.ForMember(
dest => dest.Value,
opt => opt.ResolveUsing(
src =>
new PropertyBasedResolver(typeof(Category.Key) as PropertyInfo).Resolve(src)));
Of course that is a pretty gross lamda and I would suggest that you clean it up by having your property resolver determine the property on the source object it should look at based on the attribute/property info so you can just pass a clean new PropertyBasedResolver(property) into the ResolveUsing() but hopefully this explains enough to put you on the right track.
Lets assume I have the following classes
public class foo
{
public string Value;
}
public class bar
{
public string Value1;
public string Value2;
}
You can pass a lambda to ResolveUsing:
.ForMember(f => f.Value, o => o.ResolveUsing(b =>
{
if (b.Value1.StartsWith("A"));)
{
return b.Value1;
}
return b.Value2;
}
));

Convert LamdaFunctions - Func<IQueryable<TD>, IOrderedQueryable<TD>> to Func<IQueryable<TE>, IOrderedQueryable<TE>>

Is there a way to convertFunc<IQueryable<TD>, IOrderedQueryable<TD>> to Func<IQueryable<TE>, IOrderedQueryable<TE>>.
Let's say I have classes Country and CountryModel.
class CountryModel
{
public string Name {get;set;}
}
class Country{
public string Name{get;set;}
}
class Repo{
public IEnumerable<TD> Get(
Func<IQueryable<TD>, IOrderedQueryable<TD>> orderBy = null)
{
IQueryable<TEntityModel> query = CurrentDbSet;
return orderBy(query).ToList(); // Convert orderBy to TE.
}
}
Using the above method I would pass CountryModel instance. But the query has to happen the entity type Country.
There might be some syntax errors. Apologies for that.
public class Repo
{
public IEnumerable<TD> Get<TD, TE>(Func<IQueryable<TD>, IOrderedQueryable<TD>> orderBy = null)
{
IQueryable<TD> query = new List<TE>().Select(t => Convert<TD, TE>(t)).AsQueryable();
return orderBy(query).AsEnumerable(); // Convert orderBy to TE.
}
public TD Convert<TD, TE>(TE input) where TD : class
{
return input as TD;
}
}
If your type TE can be casted to the type TD this should work.
You can define explicit or implicit operator in your CountryModel to convert from Country
You may find AutoMapper to be very useful.
public static class MappingExtensions
{
static MappingExtensions()
{
Mapper.CreateMap<CustomAlerts, Domain.Models.CustomAlerts>();
Mapper.CreateMap<Domain.Models.CustomAlerts, CustomAlerts>();
//add mappings for each set of objects here.
//Remember to map both ways(from x to y and from y to x)
}
//map single objects
public static TDestination Map<TSource, TDestination>(TSource item)
{
return Mapper.Map<TSource, TDestination>(item);
}
//map collections
public static IEnumerable<TDestination> Map<TSource, TDestination>(IEnumerable<TSource> item)
{
return Mapper.Map<IEnumerable<TSource>, IEnumerable<TDestination>>(item);
}
}
Then in my code, I can do the following:
var TodayDate = DateTime.Now.AddDays(1);
var Alerts = DB.CustomAlerts
.Where(x => x.EndDate >= TodayDate)
.OrderByDescending(x => x.DateCreated)
.Skip(((id - 1) * 50))
.Take(50);
return Mapping.MappingExtensions.Map<CustomAlert,Models.CustomAlert>(Alerts).ToList();
Using AutoMapper and wrapping your Func with Expression should help you:
// Initialize AutoMapper
Mapper.Initialize(cfg =>
{
cfg.CreateMap<TE, TD>();
cfg.CreateMap<TD, TE>();
});
public class Repo
{
// Wrap Func with Expression
public IEnumerable<TD> Get(Expression<Func<IQueryable<TD>, IOrderedQueryable<TD>>> orderBy = null)
{
var orderByExpr = Mapper.Map<Expression<Func<IQueryable<TE>, IOrderedQueryable<TE>>>>(orderBy);
IQueryable<TE> query = CurrentDbSet;
// Compile expression and execute as function
var items = orderByExpr.Compile()(query).ToList();
// Map back to list of TD
return Mapper.Map<IEnumerable<TD>>(items);
}
}

Categories