尝试使用表达式为可为空的 Guid 属性赋值

问题描述

大家好,我正在使用表达式树通过使用以下代码创建表达式来将值从源映射到目标

public static class PropertyMapper<TSource,TDest>
{
    private static Expression<Func<TSource,Dictionary<string,MasterSection>,TDest>> _mappingExpression;
    private static Func<TSource,TDest> _mapper;
    static PropertyMapper()
    {
        _mappingExpression = ProjectionMap();
        _mapper = _mappingExpression.Compile();
    }

    public static Func<TSource,TDest> Mapper => _mapper;

    public static Expression<Func<TSource,TDest>> ProjectionMap()
    {
        var sourceProperties = typeof(TSource).GetProperties().Where(p => p.CanRead);
        var destProperties = typeof(TDest).GetProperties().Where(p => p.CanWrite);
        var propertyMap =
            from d in destProperties
            join s in sourceProperties on new { d.Name,d.PropertyType } equals new { s.Name,s.PropertyType }
            where d.Name != "SourceOfDataId"
            select new { Source = s,Dest = d };
        var itemParam = Expression.Parameter(typeof(TSource),"item");
        var dictParam = Expression.Parameter(typeof(Dictionary<string,MasterSection>),"dict");
        var memberBindings = propertyMap.Select(p => (MemberBinding)Expression.Bind(p.Dest,Expression.Property(itemParam,p.source))).ToList();

        var sourceOfDataProp = destProperties.FirstOrDefault(s => s.Name == "SourceOfDataId");
        if (sourceOfDataProp != null)
        {
            // here i am setting one of the inner object(SourceOfData) ID to outer object nullable guid property (SourceOfDataId)
            memberBindings.Add(Expression.Bind(sourceOfDataProp,Expression.Property(Expression.Property(itemParam,"SourceOfData"),"Id")));
        }
       
        var newExpression = Expression.New(typeof(TDest));
        var memberInitExpression = Expression.MemberInit(newExpression,memberBindings);
        var projection = Expression.Lambda<Func<TSource,TDest>>(memberInitExpression,itemParam,dictParam);
        return projection;
    }
} 

然后像下面这样调用上面的方法

 AirflowsLab = sourceMechanicalData
                          .AirflowsLab
                          .Select(a => PropertyMapper<LibraryLabAirflow,LibraryLabAirflow>
                                  .Mapper(a,masterSectionMappedLibrary)).ToList()

我在源实体和目标实体上有这些属性 SourceOfDataId、IsApproved,我试图将 IsApproved 设置为 true 并将 sourceOfDataId 设置为相应的值。

此项目映射错误的值已设置为 SourceOfDataId 而不是内部对象属性 Id 后

实体 LibraryLabAirflow 看起来像这样

public class LibraryLabAirflow
{
    [ForeignKey("SourceOfData")]
    public Guid? SourceOfDataId { get; set; }
    public virtual CodeStandardGuideline SourceOfData { get; set; }
    public bool? IsApproved { get; set; }
}

我也尝试过下面的表达式绑定,但都没有工作

尝试 1:

 var sourceOfDataPropertyBind = Expression.Bind(sourceOfDataProp,typeof(Guid?),"Id"));

尝试 2:

var sourceOfDataPropertyBind = Expression.Bind(sourceOfDataProp,"Id"));

并试图摆脱上面的这些表达

  airflowLab => new LibraryLabAirflow
  {              
        IsApproved = true,SourceOfData = airflowLab.sourceOfData,SourceOfDataId = airflowLab.sourceOfData.Id,}

任何人都可以让我知道我在上面的代码中做错了什么,提前致谢。

解决方法

不太清楚为什么需要将 airflowLab.SourceOfData.Id 分配给显式 FK 属性 SourceOfDataId,因为在服务器端 LINQ to Entities 的上下文中,它应该与直接分配 airflowLab.SourceOfDataId 相同.

但是出于某种原因,您需要它。由于 airflowLab.SourceOfData.Id 表达式的静态类型是 non nullable Guid(C# 没有隐式空值传播的概念),C# 编译器为

SourceOfDataId = airflowLab.SourceOfData.Id

SourceOfDataId = (Guid?)airflowLab.SourceOfData.Id

注意转换为可为空的 Guid。在映射到 Expression.ConvertExpression 术语中,您需要的是类似

Expression.Convert(
    Expression.Property(Expression.Property(itemParam,"SourceOfData"),"Id"),typeof(Guid?))