Skip to content

Instantly share code, notes, and snippets.

@Alexei000
Created September 6, 2017 10:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Alexei000/3f6af8b3c20627b836cee5d5b29ac9da to your computer and use it in GitHub Desktop.
Save Alexei000/3f6af8b3c20627b836cee5d5b29ac9da to your computer and use it in GitHub Desktop.
Object list fetch - mapping service
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using AutoMapper;
using Common.Web;
using MdwAutomaticTesting.Models;
namespace MdwAutomaticTesting.Services
{
public class MappingService : IMappingService
{
#region Properties
public IMapper Mapper { get; }
public IScopedDataAccess DataAccess { get; }
#endregion
#region Constructor
public MappingService(IScopedDataAccess dataAccess)
{
DataAccess = dataAccess;
var config = new MapperConfiguration(cfg =>
{
// mappings come here
};
Mapper = config.CreateMapper();
}
#endregion
#region Private methods
private void GetTypesMapsInfo<TSrc>(IList<PropertyInfo> standardListItemAttrProps,
out Dictionary<string, Type> standardListPropToModelType, out Dictionary<string, PropertyInfo> sourcePropMap, out Dictionary<string, IRepository> reposMap)
{
var srcProps = typeof(TSrc).GetProperties();
standardListPropToModelType = standardListItemAttrProps.ToDictionary(pi => pi.Name, pi => typeof(IList).IsAssignableFrom(pi.PropertyType) ?
pi.PropertyType.GenericTypeArguments[0].GetGenericArguments()[0]:
pi.PropertyType.GenericTypeArguments[0]);
sourcePropMap = standardListItemAttrProps.ToDictionary(pi => pi.Name, pi => srcProps.FirstOrDefault(srcPi => srcPi.Name == pi.Name));
var standardListPropToModelTypeBuffer = standardListPropToModelType;
reposMap = standardListItemAttrProps.ToDictionary(pi => pi.Name, pi => DataAccess.GetRepository(standardListPropToModelTypeBuffer[pi.Name]));
}
private void PopulateStandardListItem(int key, StandardListItem destItem, Type modelType, PropertyInfo stdItemProp, IRepository repo)
{
// undefined value cannot be found within a repository, setting unspecified
if (key.IsUndefined())
{
// ReSharper disable once PossibleNullReferenceException
destItem.Key = Common.Web.Constants.Controls.NullId;
destItem.Value = Common.Web.Constants.Controls.NullStr;
return;
}
// ReSharper disable once PossibleNullReferenceException
destItem.Key = key;
var standardListItem = repo.GetGeneric(key) as IStandardListItem;
if (standardListItem == null)
throw new ArgumentException($"Property {stdItemProp.Name} of type {modelType} must implement IStandardListItem");
destItem.Value = standardListItem.Value;
destItem.IsEnabled = standardListItem.IsEnabled;
}
private StandardListItem GetEmptyStandardListItem(Type modelType)
{
var genericClass = typeof(StandardListItem<>);
var constructedClass = genericClass.MakeGenericType(modelType);
var newStandardListItem = Activator.CreateInstance(constructedClass) as StandardListItem;
return newStandardListItem;
}
private void TransferSingleStandardListItemValue<TSrc, TDest>(PropertyInfo srcPropInfo, PropertyInfo stdItemProp, Type modelType, TSrc srcEnt, TDest destEnt, IRepository repo)
{
// if destination is not initialized, initializing it
var destItem = stdItemProp.GetValue(destEnt) as StandardListItem;
if (destItem == null)
{
var newStandardListItem = GetEmptyStandardListItem(modelType);
stdItemProp.SetValue(destEnt, newStandardListItem);
destItem = newStandardListItem;
}
// if source property is not an integer, but a StandardListItem, direct copy the information
var srcObjValue = srcPropInfo.GetValue(srcEnt, index: null);
if (srcObjValue is int)
{
int key = Convert.ToInt32(srcObjValue);
PopulateStandardListItem(key, destItem, modelType, stdItemProp, repo);
}
else if (srcObjValue is StandardListItem)
{
var srcStdItem = srcObjValue as StandardListItem;
destItem.Key = srcStdItem.Key;
destItem.Value = srcStdItem.Value;
destItem.IsEnabled = srcStdItem.IsEnabled;
}
// other types will simply not be mapped
}
private void TransferListOfStandardListItemValue<TSrc, TDest>(PropertyInfo srcPropInfo, PropertyInfo stdItemProp, Type modelType, TSrc srcEnt, TDest destEnt, IRepository repo)
{
var destList = stdItemProp.GetValue(destEnt) as IList;
if (destList == null)
{
var genericClass = typeof(StandardListItem<>);
var constructedClass = genericClass.MakeGenericType(modelType);
var listGenericType = typeof(List<>);
var listClass = listGenericType.MakeGenericType(constructedClass);
var newListOfStandardListItems = Activator.CreateInstance(listClass) as IList;
stdItemProp.SetValue(destEnt, newListOfStandardListItems);
destList = newListOfStandardListItems;
}
var srcList = srcPropInfo.GetValue(srcEnt, null) as IList;
if (srcList == null)
return;
foreach (int srcItem in srcList)
{
var newStandardListItem = GetEmptyStandardListItem(modelType);
// ReSharper disable once PossibleNullReferenceException
destList.Add(newStandardListItem);
PopulateStandardListItem(srcItem, newStandardListItem, modelType, stdItemProp, repo);
}
}
private void MapEx<TSrc, TDest>(TSrc srcEnt, TDest destEnt,
IEnumerable<PropertyInfo> standardListItemAttrProps, IReadOnlyDictionary<string, Type> standardListPropToModelType, IReadOnlyDictionary<string, PropertyInfo> sourcePropMap, IReadOnlyDictionary<string, IRepository> reposMap)
{
if (destEnt == null)
return;
foreach (var stdItemProp in standardListItemAttrProps)
{
var srcPropInfo = sourcePropMap[stdItemProp.Name];
if (srcPropInfo == null)
continue;
var modelType = standardListPropToModelType[stdItemProp.Name];
var propIsList = typeof(IList).IsAssignableFrom(stdItemProp.PropertyType);
var repo = reposMap[stdItemProp.Name];
if (!propIsList)
TransferSingleStandardListItemValue(srcPropInfo, stdItemProp, modelType, srcEnt, destEnt, repo);
else
TransferListOfStandardListItemValue(srcPropInfo, stdItemProp, modelType, srcEnt, destEnt, repo);
}
}
#endregion
public IList<TDest> MapEx<TSrc, TDest>(IList<TSrc> srcList, IMapper customMapper = null) where TDest : new()
{
var actualMapper = customMapper ?? Mapper;
var result = actualMapper.Map<List<TDest>>(srcList);
// mapping standard item lists (marked with StandardListItemMappingInfoAttribute)
var standardListItemAttrProps = typeof(TDest).GetProperties().Where(pi => pi.GetCustomAttributes(typeof(StandardListItemMappingInfoAttribute), inherit: true).Length > 0).ToList();
if (standardListItemAttrProps.Count <= 0)
return result;
Dictionary<string, Type> standardListPropToModelType;
Dictionary<string, PropertyInfo> sourcePropMap;
Dictionary<string, IRepository> reposMap;
GetTypesMapsInfo<TSrc>(standardListItemAttrProps, out standardListPropToModelType, out sourcePropMap, out reposMap);
for (int index = 0; index < srcList.Count; index ++)
{
var srcEnt = srcList[index];
var destEnt = result[index];
MapEx(srcEnt, destEnt, standardListItemAttrProps, standardListPropToModelType, sourcePropMap, reposMap);
}
return result;
}
public void MapEx<TSrc, TDest>(TSrc src, TDest result, IMapper customMapper = null)
{
var actualMapper = customMapper ?? Mapper;
actualMapper.Map(src, result);
// sanity check to avoid transferring lists to this overload
if (typeof(IList).IsAssignableFrom(typeof(TSrc)))
throw new ArgumentException($"{nameof(src)} should not be a list");
// mapping standard item lists (marked with StandardListItemMappingInfoAttribute)
var standardListItemAttrProps = typeof(TDest).GetProperties().Where(pi => pi.GetCustomAttributes(typeof(StandardListItemMappingInfoAttribute), inherit: true).Length > 0).ToList();
if (standardListItemAttrProps.Count <= 0)
return;
Dictionary<string, Type> standardListPropToModelType;
Dictionary<string, PropertyInfo> sourcePropMap;
Dictionary<string, IRepository> reposMap;
GetTypesMapsInfo<TSrc>(standardListItemAttrProps, out standardListPropToModelType, out sourcePropMap, out reposMap);
MapEx(src, result, standardListItemAttrProps, standardListPropToModelType, sourcePropMap, reposMap);
}
public TDest MapEx<TSrc, TDest>(TSrc src, IMapper customMapper = null) where TDest : new()
{
var actualMapper = customMapper ?? Mapper;
var result = actualMapper.Map<TDest>(src);
MapEx(src, result, actualMapper);
return result;
}
public TListTotalDest MapListWithTotalCountModels<TSrc, TDest, TListTotalSrc, TListTotalDest>(TListTotalSrc src, IMapper customMapper = null) where TDest : new()
where TListTotalSrc : ListWithTotalCount<TSrc>
where TListTotalDest : ListWithTotalCount<TDest>
{
var mappedList = MapEx<TSrc, TDest>(src.Items, customMapper);
var result = Activator.CreateInstance<TListTotalDest>();
result.TotalCount = src.TotalCount;
result.Items = mappedList;
return result;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment