Search code examples
c#entity-frameworklambdalinq-to-sqlexpression-trees

How to use a dynamic expression for a value in an Entity Framework select clause


I'm not sure if I'm asking this question correctly, or in an understandable way, but here it goes:

I'm trying to figure out a dynamic way to set a value within a select from a database context. I'm using something I call a CodeTableValuePOCO in over a hundred places throughout my code, and I don't want to specify the whole expression every time I need it (especially the syntax for finding the CodeName value). So, I tried with an expression like this, to generate it dynamically.

internal static Expression<Func<CodeTableValue, CodeTableValuePOCO>> GetCodeTableValueSelectorExpression(int languageId) {
        return x => new CodeTableValuePOCO
        {
            Code = x.Code,
            // Check if there are any translations for the code name
            CodeName = x.Translations != null
                        // If there are, check if there is a translation for user's language
                        ? x.Translations.LanguageTranslationTexts.Count(t => t.Language_ID == languageId) > 0
                            // If there is a translation, take the first for the given language
                            ? x.Translations.LanguageTranslationTexts.FirstOrDefault(t => t.Language_ID == languageId).Text
                            // Else, default to codeName
                            : x.CodeName
                        // Else, default to codeName
                        : x.CodeName,
            CodeTableId = x.CodeTable.CodeTableID,
            Id = x.CodeTableValueID,
            Key = x.CodeTable.Key,
            ParentId = x.ParentID
        };
    }

But, I can't figure out how to use the above expression in my code. I think it would work if I only was looking for a CodeTableValuePOCO , but the CodeTableValuePOCO is embedded in larger selects, like the example below. It's not working, obviously, since the program is expecting a CodeTableValuePOCO, but getting an Expression.

using (MyDbContext db = new MyDbContext()) {

    return db.Notifications
        .Where(x => x.id == 1)        
        .Select(x => new NotificationPOCO
        {
            Id = x.Id,
            Message = x.Message,
            //This part is, obviously, not working
            Type = code.GetCodeTableValueSelectorExpression(2)
         })
         .ToList();
        }
}

Solution

  • There is no natural C# compile time support for using expressions inside other expressions.

    You need some expression composition library - for instance, LINQKit. It explains the problem and addresses it with custom Invoke and AsExpandable extension methods. The solution in your case is something like this (after installing the package):

    using LinqKit;
    
    using (MyDbContext db = new MyDbContext()) {
        var codeTableValueSelector = GetCodeTableValueSelectorExpression(2); 
        return db.Notifications.AsExpandable()
            .Where(x => x.id = 1)        
            .Select(x => new NotificationPOCO
            {
                Id = x.Id,
                Message = x.Message,
                Type = codeTableValueSelector.Invoke(x) // or x.CodeTable? not sure from the sample code
            })
            .ToList();
    }