目录
介绍
代码更改
执行
什么是“In”扩展方法?
一个简单的测试程序
结论
下载程序 - 1.9 KB
介绍我修改前两篇关于Code Project的文章的目的是因为我想进行一些有用的更改,而使用AutoMapper之类的东西是矫枉过正的。虽然我很欣赏IL一代,但我真的不想在MapperConfiguration对象中注册我的CreateMap映射。我只想在需要时映射两个对象,用几个铃声,没有口哨声。我生活中的一个主题似乎是KISS原则,这就是为什么我最终经常自己动手!
代码更改我对代码做了三处更改:
首先,映射方法CreateMapped从this对象中确定源类型,所以不要写:
Student source = new Student() { Id = 1, Name = "Smith" };
StudentLog newMapped = source.CreateMapped();
可以写:
Student source = new Student() { Id = 1, Name = "Smith" };
StudentLog newMapped = source.CreateMapped();
注意删除了泛型参数Student。
其次,我添加了一个属性MapperPropertyAttribute,用于在目标属性具有不同名称时指定源属性。
例如,我有一个类User:
public class User
{
public int Id { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public string Salt { get; set; }
public string AccessToken { get; set; }
public string RefreshToken { get; set; }
public bool IsSysAdmin { get; set; }
public DateTime? LastLogin { get; set; }
public int? ExpiresIn { get; set; }
public long? ExpiresOn { get; set; }
public bool Deleted { get; set; }
}
但我希望登录响应返回具有不同名称的属性子集。MapperProperty用于指定目标类中的属性名称转换:
public class LoginResponse
{
[MapperProperty(Name = "AccessToken")]
public string access_token { get; set; }
[MapperProperty(Name = "RefreshToken")]
public string refresh_token { get; set; }
[MapperProperty(Name = "ExpiresIn")]
public int expires_in { get; set; }
[MapperProperty(Name = "ExpiresOn")]
public long expires_on { get; set; }
public string token_type { get; set; } = "Bearer";
}
一个示例用例片段是:
var response = user.CreateMapped();
第三,我在某些地方重命名了变量名。
执行属性很简单:
public class MapperPropertyAttribute : Attribute
{
public string Name { get; set; }
public MapperPropertyAttribute() { }
}
扩展方法已被修改以提供两种共享通用private实现的public方法。
public static class MapExtensionMethods
{
public static TTarget MapTo(this TSource source, TTarget target)
{
var ret = MapTo(source.GetType(), source, target);
return ret;
}
public static TTarget CreateMapped(this object source) where TTarget : new()
{
return MapTo(source.GetType(), source, new TTarget());
}
private static TTarget MapTo(Type tSource, object source, TTarget target)
{
const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance |
BindingFlags.NonPublic;
var srcFields = (from PropertyInfo aProp in tSource.GetProperties(flags)
where aProp.CanRead //check if prop is readable
select new
{
Name = aProp.Name,
Alias = (string)null,
Type = Nullable.GetUnderlyingType(aProp.PropertyType) ?? aProp.PropertyType
}).ToList();
var trgFields = (from PropertyInfo aProp in target.GetType().GetProperties(flags)
where aProp.CanWrite //check if prop is writeable
select new
{
Name = aProp.Name,
Alias = aProp.GetCustomAttribute()?.Name,
Type = Nullable.GetUnderlyingType(aProp.PropertyType) ?? aProp.PropertyType
}).ToList();
var commonFields = trgFields.In(srcFields, /* T1 */ t => t.Alias ??
t.Name, /* T2 */ t => t.Name).ToList();
foreach (var field in commonFields)
{
var value = tSource.GetProperty(field.Alias ?? field.Name).GetValue(source, null);
PropertyInfo propertyInfos = target.GetType().GetProperty(field.Name);
propertyInfos.SetValue(target, value, null);
}
return target;
}
}
“秘诀”是在select语句返回的匿名对象中添加Alias属性和null解析运算符??,以确定源属性是使用别名名称还是属性名称。另一件有趣的事情是,由于这些是匿名属性,分配Alias为null需要将null:Alias = (string)null,转换为string。不是你经常看到的东西。
什么是“In”扩展方法?不幸的是,Linq IntersectBy仅在.NET 6中可用。 为此做了如下的修改。
// See Mr.PoorInglish's rework of my article here:
// https://www.codeproject.com/Articles/5293576/A-Performant-Items-in-List-A-that-are-not-in-List?msg=5782421#xx5782421xx
public static IEnumerable In(
this IEnumerable items1,
IEnumerable items2,
Func keySelector1, Func keySelector2)
{
var dict1 = items1.ToDictionary(keySelector1);
var k1s = dict1.Keys.Intersect(items2.Select(itm2 => keySelector2(itm2)));
var isIn = k1s.Select(k1 => dict1[k1]);
return isIn;
}
此外,.NET 6的IntersectedBy实现确实不是我想要的签名,我也不想实现iEqualityComparer,所以我们将使用上面的扩展方法。
一个简单的测试程序本文的下载有一个示例程序,您可以运行该示例程序来演示此版本的映射器:
public static void Main()
{
// We declare the epoch to be 1/1/1970.
var ts = (long)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;
var expiresSeconds = 24 * 60 * 60;
var user = new User()
{
Id = 1,
UserName = "fubar",
Password = "fizbin",
Salt = "pepper",
AccessToken = Guid.NewGuid().ToString(),
RefreshToken = Guid.NewGuid().ToString(),
ExpiresIn = expiresSeconds,
ExpiresOn = ts + expiresSeconds,
LastLogin = DateTime.Now,
};
var response = user.CreateMapped();
Console.WriteLine($"access_token: {response.access_token}");
Console.WriteLine($"refresh_token: {response.refresh_token}");
Console.WriteLine($"expires_in: {response.expires_in}");
Console.WriteLine($"expires_on: {response.expires_on}");
Console.WriteLine($"token_type: {response.token_type}");
}
输出:
access_token: 86384067-9193-449a-a6ff-8023be5fe203
refresh_token: 12e04d46-882e-4a25-a777-d1440f4783cd
expires_in: 86400
expires_on: 1644175047
token_type: Bearer
结论
在这里没什么好总结的——这一切都是近8年前写的一个简短而有用的提示和技巧的第三版!
https://www.codeproject.com/Tips/5324352/A-Third-Incarnation-of-DiponRoys-Simple-Model-Enti