C# 在调用时调用async会产生CancellationToken错误

C# 在调用时调用async会产生CancellationToken错误,c#,async-await,cancellation-token,C#,Async Await,Cancellation Token,我正在为.NETFramework4.8上的EntityFramework6.x创建一个通用异步更新方法。下面是课程: public class GenericUpdate<TEntity, TId, TDto> where TEntity : class where TId : IConvertible where TDto : class { public async Task UpdateSingleAsync(string searchProp

我正在为.NETFramework4.8上的EntityFramework6.x创建一个通用异步更新方法。下面是课程:

public class GenericUpdate<TEntity, TId, TDto>
    where TEntity : class
    where TId : IConvertible
    where TDto : class
{
    public async Task UpdateSingleAsync(string searchPropertyName, TId searchPropertyValue, TDto dto)
    {
        try
        {
            var tableType = typeof(TEntity);

            // https://stackoverflow.com/questions/30029230/dynamic-lambda-expression-for-singleordefault
            var param = Expression.Parameter(tableType, "m");
            var searchProperty = Expression.PropertyOrField(param, searchPropertyName);
            var constSearchValue = Expression.Constant(searchPropertyValue);

            var body = Expression.Equal(searchProperty, constSearchValue);

            var lambda = Expression.Lambda(body, param);

            using (var context = new MyContext())
            {
                var dbTable = context.Set(tableType);

                var genericSingleOrDefaultAsyncMethod =
                    typeof(QueryableExtensions).GetMethods().First(m => m.Name == "SingleOrDefaultAsync" && m.GetParameters().Length == 2);
                var specificSingleOrDefaultAsync = genericSingleOrDefaultAsyncMethod.MakeGenericMethod(tableType);

                // https://stackoverflow.com/a/16153317/177416
                var result = (Task<TEntity>) specificSingleOrDefault.Invoke(null, new object[] { dbTable, lambda });
                await result;

                context.Entry(result).CurrentValues.SetValues(dto);
                await context.SaveChangesAsync();
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
            throw;
        }
    }
}
第一个建议不起作用,因为
dbTable
没有
SingleOrDefaultAsync

现在得到这个错误:

与“System.Data.Entity.QueryableExtensions.SingleOrDefaultAsync(System.Linq.IQueryable,System.Threading.CancellationToken)”匹配的最佳重载方法具有一些无效参数

更新3和解决方案:多亏了@StephenCleary,这个解决方案就像一个魔咒:

dynamic lambda = Expression.Lambda(body, param);

using (var context = new MyContext())
{
    dynamic dbTable = context.Set(tableType);
    var result = await QueryableExtensions.SingleOrDefaultAsync(dbTable, lambda);

    if(result != null)
    {
        context.Entry(result).CurrentValues.SetValues(dto);
        await context.SaveChangesAsync();
    }
}
通用异步更新方法

我不得不质疑,一个极其复杂的方法是否真的比六个左右干净而明显的方法更易于维护

也就是说,您收到的错误表明您检索的
SingleOrDefaultAsync
重载错误。实际上,代码
typeof(QueryableExtensions).GetMethods().First(m=>m.Name==“SingleOrDefaultAsync”&&m.GetParameters().Length==2)
只返回该名称中具有两个参数的第一个方法。这不足以确保您获得正确的方法

您可以添加更多检查以确保参数是您期望的类型,也可以完全删除反射,因为它似乎不是必需的:

var dbTable = context.Set(tableType);
var result = await dbTable.SingleOrDefaultAsync(lambda);
这是假设
dbTable
属于适当的类型。如果它是某种未指定的类型(例如,
对象
),您仍然可以通过使它成为
动态的
,来使用编译器的重载解析:

dynamic dbTable = context.Set(tableType);
var result = await QueryableExtensions.SingleOrDefaultAsync(dbTable, lambda);

请看:@Wyck,这就是我得到
(任务)
代码的地方,这正是我正在做的,不是吗?请查看我的更新,现在正在获取上面更新2中指示的错误@StephenCleary。请尝试使
lambda
动态
。谢谢@StephenCleary,成功了。但是为什么呢?
dynamic
在这里做什么?反射中的Lambda表达式很奇怪。具有类型
表达式
,但
lambda
属于类型
LambdaExpression
。所以
dynamic
正在做“神奇的编译器工作”。我甚至不知道。
dynamic dbTable = context.Set(tableType);
var result = await QueryableExtensions.SingleOrDefaultAsync(dbTable, lambda);