Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/AutoGenMapperGenerator/AutoGenMapperGenerator.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.0;net6.0;net8.0;net10.0</TargetFrameworks>
<TargetFrameworks>net6.0;net8.0;net10.0</TargetFrameworks>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
Expand All @@ -25,4 +25,8 @@
<None Include=".\readme.md" Pack="true" PackagePath="\" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.2" />
</ItemGroup>

</Project>
60 changes: 52 additions & 8 deletions src/AutoGenMapperGenerator/GMapper.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
namespace AutoGenMapperGenerator;
using AutoGenMapperGenerator.ReflectMapper;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;

namespace AutoGenMapperGenerator;

/// <summary>
/// <para>G -> Generator</para>
Expand All @@ -13,21 +19,59 @@ public static class GMapper
/// <typeparam name="TTarget"></typeparam>
/// <param name="source"></param>
/// <returns></returns>
public static TTarget Map<TSource, TTarget>(TSource source)
public static TTarget Map<
#if NET8_0_OR_GREATER
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
#endif
TSource,
#if NET8_0_OR_GREATER
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
#endif
TTarget>(this TSource source)
{
if (source is null)
{
return default!;
}
if (source is IAutoMap m)
{
return m.MapTo<TTarget>();
}
var (IsComplex, IsDictionary, IsEnumerable) = ExpressionHelper.IsComplexType(typeof(TSource));
if (IsComplex && IsDictionary)
{
throw new InvalidOperationException("请使用TEntity ToEntity<TEntity>(this IDictionary<string, object?> dict)");
}
if (IsComplex && IsEnumerable)
{
throw new InvalidOperationException("不支持集合类型的映射,请使用LINQ的Select方法进行映射");
}
return ExpressionMapper<TSource, TTarget>.Map(source);
}
}

internal static class ExpressionMapper<TSource, TTarget>
{
public static TTarget Map(TSource source)
/// <summary>
///
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <param name="entity"></param>
/// <returns></returns>
public static IDictionary<string, object?> ToDictionary<TEntity>(this TEntity entity)
{
if (entity is null)
{
return new Dictionary<string, object?>();
}
return ExpressionMapper<TEntity>.MapToDictionary(entity);
}

/// <summary>
///
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <param name="dict"></param>
/// <returns></returns>
public static TEntity ToEntity<TEntity>(this IDictionary<string, object?> dict)
{
// TODO
throw new System.NotImplementedException();
return ExpressionMapper<TEntity>.MapFromDictionary(dict);
}
}
94 changes: 94 additions & 0 deletions src/AutoGenMapperGenerator/ReflectMapper/DictionaryExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
using System;
using System.Collections.Generic;
using System.Globalization;

namespace AutoGenMapperGenerator.ReflectMapper;

internal static class DictionaryExtensions
{
private static readonly Dictionary<Type, Delegate> conversionCache = [];

private static T TryParse<T>(string str, CultureInfo culture)
{
var targetType = Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T);
if (conversionCache.TryGetValue(targetType, out var del))
{
var parser = (Func<string, CultureInfo, T>)del;
return parser(str, culture);
}
Func<string, CultureInfo, T> parserFunc = targetType switch
{
Type t when t == typeof(int) => (s, c) => (T)(object)int.Parse(s, NumberStyles.Any, c),
Type t when t == typeof(long) => (s, c) => (T)(object)long.Parse(s, NumberStyles.Any, c),
Type t when t == typeof(float) => (s, c) => (T)(object)float.Parse(s, NumberStyles.Any, c),
Type t when t == typeof(double) => (s, c) => (T)(object)double.Parse(s, NumberStyles.Any, c),
Type t when t == typeof(decimal) => (s, c) => (T)(object)decimal.Parse(s, NumberStyles.Any, c),
Type t when t == typeof(DateTime) => (s, c) => (T)(object)DateTime.Parse(s, c),
Type t when t == typeof(bool) => (s, c) => (T)(object)bool.Parse(s),
_ => throw new NotSupportedException($"Type {targetType.FullName} is not supported for parsing.")
};
conversionCache[targetType] = parserFunc;
return parserFunc(str, culture);
}


public static bool TryGetValue<TValue>(this IDictionary<string, object?> dict, string key, out object value)
{
value = default!;
if (!dict.TryGetValue(key, out var dictValue))
{
return default!;
}
if (dictValue is null)
{
return false;
}
if (typeof(TValue) == typeof(object))
{
value = (TValue)dictValue;
return true;
}
if (typeof(TValue) == typeof(string))
{
value = (TValue)(object)dictValue.ToString()!;
return true;
}
if (dictValue is TValue tValue)
{
value = tValue;
return true;
}
// 处理可空类型
var underlyingType = Nullable.GetUnderlyingType(typeof(TValue));
var conversionTargetType = underlyingType ?? typeof(TValue);
if (conversionTargetType.IsEnum)
{
value = (TValue)Enum.Parse(conversionTargetType, dictValue.ToString() ?? string.Empty, true);
return true;
}
if (dictValue is string str)
{
// string转基础类型
try
{
value = TryParse<TValue>(str, CultureInfo.CurrentCulture);

Check warning on line 74 in src/AutoGenMapperGenerator/ReflectMapper/DictionaryExtensions.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference assignment.

Check warning on line 74 in src/AutoGenMapperGenerator/ReflectMapper/DictionaryExtensions.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference assignment.
return true;
}
catch
{
return false;
}
}
// 使用 Convert.ChangeType 兜底
try
{
var convertedValue = Convert.ChangeType(dictValue, conversionTargetType, CultureInfo.CurrentCulture);
value = (TValue)convertedValue!;
return true;
}
catch
{
return false;
}
}
}
Loading
Loading