Search code examples
c#lambdafunc

How to get column value from C# Expression Func


My EF model looks like this:

public partial class SalesPipelineEntryClosedInformation : FoxHireEntityBase
{
    [Key]
    public int Id { get; set; }
    public string ClosedDetails { get; set; }
    public bool Won { get; set; }
    public int? TypeCompetitorId { get; set; }
    public int? TypeLostReasonId { get; set; }
    public int? TypeDeclineReasonId { get; set; }

    [ForeignKey(nameof(TypeCompetitorId))]
    [InverseProperty(nameof(TypeCompetitor.SalesPipelineEntryClosedInformations))]
    public virtual TypeCompetitor Competitor { get => Get(_competitor); set => _competitor = value; }
    
    [ForeignKey(nameof(TypeLostReasonId))]
    [InverseProperty(nameof(TypeLostReason.SalesPipelineEntryClosedInformations))]
    public virtual TypeLostReason LostReason { get => Get(_lostReason); set => _lostReason = value; }

    [ForeignKey(nameof(TypeDeclineReasonId))]
    [InverseProperty(nameof(TypeDeclineReason.SalesPipelineEntryClosedInformations))]
    public virtual TypeDeclineReason DeclineReason { get => Get(_declineReason); set => _declineReason = value; }
}

I have a method that updates stats based on which property of the entity changed. It looks like this:

private async Task CalculateStats(InsightType insightType)
{
    if (insightType.Value == InsightType.Competitor.Value)
    {
        await processStatsList(x => x.TypeCompetitorId.HasValue, x => x.Competitor.Name, new List<string> {"Competitor"}, InsightType.Competitor);
    }
    else if (insightType.Value == InsightType.IndustryReason)
    {
        await processStatsList(x => x.TypeDeclineReasonId.HasValue, x => x.DeclineReason.Name, new List<string> {"DeclineReason"}, InsightType.IndustryReason);
    }
    else if (insightType.Value == InsightType.LostReason)
    {
        await processStatsList(x => x.TypeLostReasonId.HasValue, x => x.LostReason.Name, new List<string> {"LostReason"}, InsightType.LostReason);
    }
}

And the processStatsList method is:

private async Task processStatsList(Expression<Func<SalesPipelineEntryClosedInformation, bool>> whereClause, 
                                    Expression<Func<SalesPipelineEntryClosedInformation, string>> relatedTableNameValue, 
                                    List<string> includes, InsightType insightType)
{
    await clearRecordTypes(insightType.Value);
    var closedInfo = await _genericRepository.GetList(whereClause, includes);

    var stats = closedInfo.Select(x => new
        {
            x.TypePipelineDealTypeId,
            relatedTableNameValue.Value
        }).GroupBy(x => new {x.Name})
        .Select(x => new SalesInsightStat
        {
            Count = x.Count(),
            Name = x.Key.Name,
            InsightTypeId = insightType.Value
        }).ToList();

    await _genericRepository.AddRangeAsync(stats);
} 

I'm not sure how to set up the second expression, relatedTableNameValue, to output the actual value of the Name column. All three tables have the same Name column, so I'm just trying to get the value of that column. Obviously, relatedTableNameValue.Value doesn't work, but I'm not sure how to get that value.


Solution

  • Since closedInfo is IEnumerable you can just compile the expression and invoke it:

    var stats = closedInfo
        .Select(x => new
        {
            x.TypePipelineDealTypeId,
            relatedTableNameValue.Compile()(x)
        })
        .GroupBy(x => new {x.Name})
    

    But since relatedTableNameValue is not used with IQueryable's I would recommend to just change the parameter to be a Func:

    private async Task processStatsList(Expression<Func<SalesPipelineEntryClosedInformation, bool>> whereClause,
        Func<SalesPipelineEntryClosedInformation, string> relatedTableNameValue,
         List<string> includes, 
         InsightType insightType)
    {
        // ...
        var stats = closedInfo
            .Select(x => new
            {
                x.TypePipelineDealTypeId,
                relatedTableNameValue(x)
            })
            .GroupBy(x => new {x.Name})
    
        // ...
    }