在表达式<Func <TEntity,TResult >>中返回TEntity

时间:2019-06-20 03:50:28

标签: c# generics expression-trees generic-programming func

我有一个在表达式树中传递可选/默认参数的方法,如下所示:

Task<TResult> GetSingleAsync<TResult>(Expression<Func<TEntity, TResult>> selector = null);

我应该做的是,在忽略上述selector参数的情况下检查上述方法时,将TResult分配为TEntity到我的方法中的选择器,基本上像{{ 1}},所以我正在尝试以下实现方式

selector = (TEntity) => TEntity

当我调用类似public class Repository<TEntity> : IRepository<TEntity> where TEntity : class { private readonly DbSet<TEntity> _collection; public async Task<TResult> GetSingleAsync<TResult>(Expression<Func<TEntity, TResult>> selector = null) { IQueryable<TEntity> query = this._collection; if (selector == null) selector = (entity) => default(TResult); return await query.Select(selector).SingleOrDefaultAsync(); } } 之类的函数时,忽略默认参数GetSingleAsync<User>(),但是selector将选择器显示为 null ,有什么方法可以将值分配给selector = x => default(TResult)时返回TEntity作为TResult吗?我已经尝试了以下所有方法,但也失败了

selector

1 个答案:

答案 0 :(得分:1)

我认为您可能会根据自己的需求在此处选择几种方法。

  • 您可以尝试将传入的TEntity强制转换为TResult。这将需要对TResult进行类型约束,并在该约束上有点混乱。
  • 您可以检查TEntity是否为TResult类型,如果是,则返回强制类型。否则,返回默认的TResult。这个不需要类型约束,我认为这可能是没有约束的最佳选择。
  • 您可以一起放弃默认参数值并重载该函数。就个人而言,我认为这是您最好的选择。

以下是我上面列出的选项的示例:

选项1:具有类型约束的投射。

public async Task<TResult> GetSingleAsync<TResult, TNewEntity>(Expression<Func<TNewEntity, TResult>> selector = null)
where TNewEntity: TEntity, TResult
{
   IQueryable<TNewEntity> query = this._collection;

   if (selector == null) selector = entity => (TResult) entity;

   return await query.Select(selector).SingleOrDefaultAsync();
}

在这里,您看到了我在说的丑陋的类型约束。您必须引入一种新类型,以强制您的类约束为TResult类型。

选项2:键入检查并进行投射。

public async Task<TResult> GetSingleAsync<TResult>(Expression<Func<TEntity, TResult>> selector = null)
{
    IQueryable<TNewEntity> query = this._collection;

    // I would use pattern matching here if I could, but unfortunately it looks like
    // expression trees cannot have pattern matching so we have to box then cast.
    if (selector == null) selector = entity =>  entity is TResult ? (TResult)(object) entity : default(TResult);


    return await query.Select(selector).SingleOrDefaultAsync();
}

以此,它将尝试将实体转换为正确的类型,但是如果不能,它将返回TResult的默认值,如果它是引用类型,则默认值为null。仍然会遇到以前遇到的相同问题,但是,如果强制转换成功,则可能是您想要的。

选项3:。方法重载。

// New method with no selector. Notice the return type is now TEntity
public async Task<TEntity> GetSingleAsync(){
    return GetSingleAsync(x => x); // This now works because TResult is TEntity.
}

// Original method, but now it requires the selector
public async Task<TResult> GetSingleAsync<TResult>(Expression<Func<TEntity, TResult>> selector)
{
    IQueryable<TNewEntity> query = this._collection;
    return await query.Select(selector).SingleOrDefaultAsync();
}

就个人而言,这似乎是您想要的选项以及我将要使用的选项。它实质上具有与默认参数相同的功能,但现在要求,如果未提供选择器,则查询将返回TEntity。但是,我不知道问题的所有约束条件,也不知道是否需要默认参数。

注意:这类似于LINQ对可选选择器的作用。以下是一些ToDictionary扩展名的来源:

public static Dictionary<TKey, TSource> ToDictionary<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer) {
    return ToDictionary<TSource, TKey, TSource>(source, keySelector, IdentityFunction<TSource>.Instance, comparer);
}

public static Dictionary<TKey, TElement> ToDictionary<TSource, TKey, TElement>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector) {
    return ToDictionary<TSource, TKey, TElement>(source, keySelector, elementSelector, null);
}

请注意,在第一种方法中,它们如何传入elementSelector和每个TElement的身份函数中,实际上只是由TSource代替了。

结论:

这里没有列出其他选项,但是这些是我能想到的最好的选项。我测试了一下是否可以编译,但是还没有运行。