无法将已解析的对象强制转换为通用抽象autofac C#

时间:2017-09-25 21:34:11

标签: c# asp.net asp.net-core autofac

嘿我尝试将我的对象转换为该对象应该实现的接口。但在执行代码期间,我遇到以下错误:

  

无法将GetTreeHandler类型的对象强制转换为IQueryHandler,IQuery< Tree&gt ;.

为什么呢?一切似乎都没问题。

没有使用动态的任何想法?

QueryDispatcher

def demean_and_log(x):
    x_log = np.log(x)
    x_log_mean = x_log.mean()
    return x_log - x_log_mean

log_demean_col = X.groupby(['A'])['B'].transform(demean_and_log)

GetTreeHandler

    public async Task<TResult> ExecuteAsync<TResult>(IQuery<TResult> query)
    {
        if (query == null)
        {
            throw new ArgumentNullException("Query can not be null.");
        }

        var handlerType = typeof (IQueryHandler<,>).MakeGenericType(query.GetType(), typeof (TResult));
        var handler = _context.Resolve(handlerType);

        IQueryHandler<IQuery<TResult>, TResult> castedHandler = (IQueryHandler<IQuery<TResult>, TResult>) handler;

        return (TResult) await castedHandler.ExecuteAsync(query);    
    }   

修改

我目前的解决方案(部分有效):

   public class GetTreeHandler : IQueryHandler<GetTree, Tree>
    {
        private readonly ProductContext _context;
        public string Name { get; set; }
        public GetTreeHandler(ProductContext context)
        {
            _context = context;
        }
        public async Task<Tree> ExecuteAsync(GetTree query)
            =>  await Task.FromResult(
                    _context.Trees.FirstOrDefault(x => x.Id == query.Id)
                );
    }

为何部分? 例如。处理程序:

不起作用:

    public async Task<TResult> ExecuteAsync<TResult>(IQuery query) where TResult : class
    {
        if (query == null)
        {
            throw new ArgumentNullException("Query can not be null.");
        }

        var handlerType = typeof (IQueryHandler<,>).MakeGenericType(query.GetType(), typeof (TResult));
        var handler = _context.Resolve(handlerType);

        return await (Task<TResult>)                        
            handler.GetType()                               
                   .GetMethod("ExecuteAsync")               
                   .Invoke(handler, new object[]{query});   
    }  

使用:

public class GetIndexesHandler : IQueryHandler<GetIndexes, IEnumerable<AssocIndexDto>>

正如您所看到的,这两个类实现了相同的接口,但具有不同的第二个参数类型。

问题是Resolve方法无法找到已注册的服务,但如果我尝试调试此代码,我可以看到所需服务已正确注册,但无法找到。

你知道如何解决它吗?

1 个答案:

答案 0 :(得分:3)

您正在尝试将IQueryHandler<GetTree, Tree>投射到IQueryHandler<IQuery<Tree>, Tree>,但只有在IQueryHandler<TQuery, TResult>指定为:{/ p>}时才会有效

interface IQueryHandler<out TQuery, TResult>

请注意out参数。

out关键字将TQuery标记为输出参数。如果将泛型类型参数标记为输出参数,则允许我们将接口强制转换为更通用的参数。例如,IEnumerable<string>可以转换为IEnumerable<object>,因为IEnumerable<string>将返回字符串,并且它们可以表示为对象。

然而,对于in个参数,则相反。在查看Action<in T> for intance时,我们可以将Action<BaseType>转换为Action<SubType>,因为该操作始终也可以处理SubType。另一方面,将Action<BaseType>转换为Action<object>是不可能的,因为这样我们也可以将string传递给动作(因为string是一个object),但那显然会在运行时失败。

只有在IQueryHandler<GetTree, Tree>标有IQueryHandler<IQuery<Tree>, Tree>关键字的情况下才能将TQuery转换为out,但显然TQuery是输入参数。因此,CLR禁止转换,因为它可能在运行时导致错误。

但是,在你的情况下,这个问题不存在,因为你知道传入的类型总是适合。但请记住,CLR无法检查这一点,因此阻止了转换。

我的博客上的一个解决方案是described,使用dynamic关键字,如下所示:

public async Task<TResult> ExecuteAsync<TResult>(IQuery<TResult> query)
{
    var handlerType = 
        typeof(IQueryHandler<,>).MakeGenericType(query.GetType(), typeof(TResult));
    dynamic handler = _context.Resolve(handlerType);
    return (TResult)await castedHandler.ExecuteAsync((dynamic)query);    
}

另一个选项是指定并注册实现缺少TQuery泛型类型的接口的通用包装类型。这样您就可以完全阻止使用dynamic

public interface IWrapper<TResult>
{
    Task<TResult> ExecuteAsync(IQuery<TResult> query);
}

public async Task<TResult> ExecuteAsync<TResult>(IQuery<TResult> query)
{
    var wrapperType =
        typeof(Wrapper<,>).MakeGenericType(query.GetType(), typeof(TResult));
    var wrapper = (IWrapper<TResult>)_context.Resolve(wrapperType);
    return wrapper.ExecuteAsync(query);
}

// Don't forget to register this type by itself in Autofac.
public class Wrapper<TQuery, TResult> : IWrapper<TResult>
{
    private readonly IQueryHandler<TQuery, TResult> handler;
    public Wrapper(IQueryHandler<TQuery, TResult> handler) { this.handler = handler; }
    Task<TResult> IWrapper<TResult>.ExecuteAsync(IQuery<TResult> query) =>
        this.handler.ExecuteAsync((TQuery)query);
}
相关问题