将映射的对象分配给LINQ到实体中的表达式结果

huangapple go评论61阅读模式
英文:

Assign Mapped Object to Expression Result in LINQ to Entities

问题

我有以下的子对象,我们使用一个表达式来将我们的 'entity' 映射到我们的 'domain' 模型。当专门调用我们的 ChildRecordService 方法 GetChild 或 GetChildren 时,我们使用这个:

public static Expression<Func<global::Database.Models.ChildRecord, ChildRecord>> MapChildRecordToCommon = entity => new ChildRecord
{
    DateTime = entity.DateTime,
    Type = entity.Type,
};

public static async Task<List<ChildRecord>> ToCommonListAsync(this IQueryable<global::Database.Models.ChildRecord> childRecords)
{
    var items = await
        childRecords.Select(MapChildRecordToCommon).ToListAsync().EscapeContext();

    return items;
}

public async Task<List<ChildRecord>> GetChildRecords()
{
    using (var uow = this.UnitOfWorkFactory.CreateReadOnly())
    {
        var childRecords = await uow.GetRepository<IChildRecordRepository>().GetChildRecords().ToCommonListAsync().EscapeContext();

        return childRecords;
    }
}

所以一切都运作正常。然而,我们还有另一个对象,它是该子对象的父对象,在某些情况下,我们也希望在物化和映射过程中获取该子对象。

换句话说,标准对象如下:

private static Expression<Func<global::Database.Models.Plot, Plot>> MapPlotToCommonBasic = (entity) => new Plot
{
    Id = entity.Id,
    Direction = entity.Direction,
    Utc = entity.Utc,
    Velocity = entity.Velocity,
};

然而,我还想映射 Plot.ChildRecord 属性,使用我已经创建的 MapChildRecordToCommon 表达式。我创建了第二个表达式来测试这个:

private static Expression<Func<global::Database.Models.Plot, Plot>> MapPlotToCommonAdvanced = (entity) => new Plot
{
    ChildRecord = MapChildRecordToCommon.Compile()(entity.ChildRecord)
};

这会失败:

System.NotSupportedException
The LINQ expression node type 'Invoke' is not supported in LINQ to Entities.

是否有一种方法可以重用我现有的 ChildRecord 表达式,将 ChildRecord 对象(即一对一/单个对象)物化到 Plot 对象上?我认为我的问题是由于只有一个对象,无法使用 .Select(Map) 方法。我对表达式不太了解,对此感到困扰。

供参考,实际上在 "Plot" 对象上还有多达 5 或 6 个其他子对象,我也希望为它们创建表达式。

英文:

I have the following child object that we use an expression to map our 'entity' to our 'domain' model. We use this when specifically calling our ChildRecordService method GetChild or GetChildren:

    public static Expression&lt;Func&lt;global::Database.Models.ChildRecord, ChildRecord&gt;&gt; MapChildRecordToCommon = entity =&gt; new ChildRecord
    {
        DateTime = entity.DateTime,
        Type = entity.Type,
    };

    public static async Task&lt;List&lt;ChildRecord&gt;&gt; ToCommonListAsync(this IQueryable&lt;global::Database.Models.ChildRecord&gt; childRecords)
    {
        var items = await
            childRecords.Select(MapChildRecordToCommon).ToListAsync().EscapeContext();

        return items;
    }

    public async Task&lt;List&lt;ChildRecord&gt;&gt; GetChildRecords()
    {
        using (var uow = this.UnitOfWorkFactory.CreateReadOnly())
        {
            var childRecords= await uow.GetRepository&lt;IChildRecordRepository&gt;().GetChildRecords().ToCommonListAsync().EscapeContext();

            return childRecords;
        }
    }

So that all works just fine. However we have another object that is a parent to that child, that in SOME cases, we also wish to get the child during the materialisation and mapping process.

In other words the standard object looks as such:

    private static Expression&lt;Func&lt;global::Database.Models.Plot, Plot&gt;&gt; MapPlotToCommonBasic = (entity) =&gt; new Plot
    {
        Id = entity.Id,
        Direction = entity.Direction,
        Utc = entity.Utc,
        Velocity = entity.Velocity,
    };

However what I also want to map is the Plot.ChildRecord property, using the expression MapChildRecordToCommon I have already created. I made a second expression just to test this:

    private static Expression&lt;Func&lt;global::Database.Models.Plot, Plot&gt;&gt; MapPlotToCommonAdvanced = (entity) =&gt; new Plot
    {
        ChildRecord = MapChildRecordToCommon.Compile() (entity.ChildRecord)
    };

This fails:

System.NotSupportedException
The LINQ expression node type &#39;Invoke&#39; is not supported in LINQ to Entities.

Is there a way to reuse my existing expression for ChildRecord, to materialise the object of ChildRecord (ie. one to one/singular not multiple) on the Plot object? I think my trouble is caused by there being just one object and being unable to use the .Select(Map) method. I am not too great at expressions and have hit a wall with this.

For reference, there are actually up to 5 or 6 other child objects on the "Plot" object that I also want to make expressions for.

答案1

得分: 0

I resolved this by using the third party library LinqKit.

The library allowed the use of 2 methods, .AsExpandable() (which allows for the expressions to properly compile and be invoked as I understand), and .Invoke() as an extension method to an expression, rather than calling Expression.Invoke(yourexpression). I included a null check just in case.

My code now looks as follows:

public static async Task<List<Plot>> ToCommonListAsync(this IQueryable<global::Database.Models.Plot> plots)
{
    var items = await
        plots.AsExpandable().Select(MapPlotToCommon).ToListAsync().EscapeContext();

    return items;
}

private static Expression<Func<global::Database.Models.Plot, Plot>> MapPlotToCommon = (entity) => new Plot
{
    Id = entity.Id,
    Direction = entity.Direction,
    Utc = entity.Utc,
    Velocity = entity.Velocity,
    ChildRecord = entity.ChildRecord != null ? MapChildRecordToCommon.Invoke(entity.ChildRecord) : default
};

public static Expression<Func<global::Database.Models.ChildRecord, ChildRecord>> MapChildRecordToCommon = entity => new ChildRecord
{
    DateTime = entity.DateTime,
    Type = entity.Type,
};
英文:

I resolved this by using the third party library LinqKit.

The library allowed the use of 2 methods, .AsExpandable() (which allows for the expressions to properly compile and be invoked as I understand), and .Invoke() as an extension method to an expression, rather than calling Expression.Invoke(yourexpression). I included a null check just in case.

My code now looks as follows:

public static async Task&lt;List&lt;Plot&gt;&gt; ToCommonListAsync(this IQueryable&lt;global::Database.Models.Plot&gt; plots)
{
    var items = await
        plots.AsExpandable().Select(MapPlotToCommon).ToListAsync().EscapeContext();

    return items;
}

private static Expression&lt;Func&lt;global::Database.Models.Plot, Plot&gt;&gt; MapPlotToCommon = (entity) =&gt; new Plot
{
    Id = entity.Id,
    Direction = entity.Direction,
    Utc = entity.Utc,
    Velocity = entity.Velocity,
    ChildRecord = entity.ChildRecord != null ? MapChildRecordToCommon.Invoke(entity.ChildRecord) : default
};

public static Expression&lt;Func&lt;global::Database.Models.ChildRecord, ChildRecord&gt;&gt; MapChildRecordToCommon = entity =&gt; new ChildRecord
{
    DateTime = entity.DateTime,
    Type = entity.Type,
};

huangapple
  • 本文由 发表于 2020年1月4日 00:15:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/59581816.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定