using Raven.Client.Documents; using Raven.Client.Documents.Session; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Threading.Tasks; using zero.Core.Entities; using zero.Core.Options; namespace zero.Core.Mapper { public class DefaultMapper : IMapper { MapCache Maps = new MapCache(); IDocumentStore Raven = null; IZeroOptions Options = null; public DefaultMapper(IDocumentStore raven, IZeroOptions options) { Raven = raven; Options = options; foreach (IMapperConfig config in options.Mapper.GetAllItems()) { config.Configure(this); } } /// public void Add() where T : IMapperConfig { T config = Activator.CreateInstance(); config.Configure(this); } /// public void Add(Assembly assembly) { Type configType = typeof(IMapperConfig); IEnumerable types = assembly.GetTypes().Where(t => t.IsAssignableFrom(configType)); foreach (Type type in types) { IMapperConfig config = (IMapperConfig)Activator.CreateInstance(type); config.Configure(this); } } /// public async Task Map(TSource source) { if (source == null) { return default; } return await Map(source, Activator.CreateInstance()); } /// public async Task Map(TSource source, TTarget target) { if (source == null) { return target; } if (target == null) { return await Map(source); } await Maps.Call(source, target); return target; } /// public async Task> Map(IEnumerable source) { IList target = new List(); foreach (TSource item in source) { target.Add(await Map(item, Activator.CreateInstance())); } return target; } /// public async Task> Map(ListResult source) { IList target = new List(); foreach (TSource item in source.Items) { target.Add(await Map(item, Activator.CreateInstance())); } return new ListResult(target, source.TotalItems, source.Page, source.PageSize) { Statistics = source.Statistics }; } /// public async Task> Map(EntityResult source) { return new EntityResult() { IsSuccess = source.IsSuccess, Errors = source.Errors, Model = await Map(source.Model, Activator.CreateInstance()) }; } /// public void CreateMap(Action map) { Maps.Add((source, target) => { map((TSource)source, (TTarget)target); return Task.CompletedTask; }); } /// public void CreateMap(Func map) { Maps.Add(async (source, target) => { await map((TSource)source, (TTarget)target, null); }); } /// /// Internal mappings cache /// class MapCache : Dictionary>> { int Index = 0; Dictionary TypeMappings = new Dictionary(); /// /// Adds a new action for the mapping /// public void Add(Func map) { int sourceKey = GetKeyForType(typeof(TSource)); int targetKey = GetKeyForType(typeof(TTarget)); if (!ContainsKey(sourceKey)) { Add(sourceKey, new Dictionary>()); } if (!this[sourceKey].ContainsKey(targetKey)) { this[sourceKey].Add(targetKey, map); } } /// /// Get action from defined types /// public async Task Call(TSource source, TTarget target) { int sourceKey = GetKeyForType(typeof(TSource)); int targetKey = GetKeyForType(typeof(TTarget)); if (!ContainsKey(sourceKey) || !this[sourceKey].ContainsKey(targetKey)) { return; } Func result = this[sourceKey][targetKey]; await result(source, target); } /// /// Get stored key for this type /// int GetKeyForType(Type type) { string name = type.FullName; if (TypeMappings.TryGetValue(name, out int key)) { return key; } int index = Index++; TypeMappings.Add(name, index); return index; } } } public interface IMapper { /// void Add() where T : IMapperConfig; /// void Add(Assembly assembly); /// /// Map an object to the target type /// Task Map(TSource source); /// /// Map an object to the target type given an already existing target instance /// Task Map(TSource source, TTarget target); /// /// Map a list of objects to the target type /// Task> Map(IEnumerable source); /// /// Map a list result containing objects to the target type /// Task> Map(ListResult source); /// /// Map an entity result to the target type /// Task> Map(EntityResult source); /// /// Create a mapping from source to target object /// void CreateMap(Action map); /// void CreateMap(Func map); } }