从对象到保持类型并在运行时确定的类型的 C# 类型转换

问题描述 投票:0回答:1

我想在我的项目中使用拦截器来拦截服务层的一些方法。 我正在使用 Castle Dynamic Proxy 和 Autofac Aspect Interceptor 选择器来做到这一点。我正在尝试实现缓存方面。这基本上是拦截服务方法,然后给出一个参数,该参数直接是被拦截的方法名称,然后也是时间为小时。

我正在使用redis进行缓存。 cashe类的get方法的返回类型是object。

 public object Get(string key, Type type)
        {
            var jsonData = _cache.GetString(key);

            if (jsonData is null)
                return default;

            return JsonSerializer.Deserialize(jsonData, type);
        }

但是,由于我将切面实现为属性,因此我只能确定将在运行时拦截的方法的返回类型。类型不匹配导致异常

Aspect 类

public class CacheAspect : MethodInterception
{
    private readonly int _duration;
    private readonly string _key;
    private readonly ICacheService _cacheService;

    public CacheAspect(string key, int duration)
    {
        _key = key;
        _duration = duration;
        _cacheService = ServiceTool.ServiceProvider.GetService<ICacheService>();
    }

    public override async void Intercept(IInvocation invocation)
    {

        var returnType = invocation.Method.ReturnType.GetGenericArguments()[0];
        // I was try to reach of the return type of the method which is called 
        // I thought that is a type so i can cast at the runtime but i couldnt.

        var cachedValue = _cacheService.Get(_key, returnType);

        if (cachedValue is not null)
        {


            invocation.ReturnValue = Task.FromResult(cachedValue);  // since cacheValue is object
            // it leads to exception            
            return;
        }

        invocation.Proceed();

        var returnValue = await (dynamic)invocation.ReturnValue;

        TimeSpan span = TimeSpan.FromHours(_duration);


        _cacheService.Add(_key, returnValue, span);
    }
}

异常消息是:“无法转换类型为“System.Threading.Tasks.Task

1[System.Object]' to type 'System.Threading.Tasks.Task
1[System.Collections.Generic.IEnumerable`1[Models.Concrete.ResponseModels.Movie.MovieResponse]]”的对象。”

但是,当我写

invocation.ReturnValue = Task.FromResult(cachedValue as IEnumerable<MovieResponse>);
而不是
invocation.ReturnValue = Task.FromResult(cachedValue);
时,一切都很好。

问题是返回值可能会有所不同;

IEnumerable<MovieResponse>
IEnumerable<Movie>
IEnumerable<etc>

由于我总是使用方法名称,因此强制转换不会成为问题,但正如我所说,我只能在运行时获取方法的返回类型。

我在下面的服务层使用的方法之一,(示例返回 IEnumerable)

[CacheAspect(nameof(GetUpcomingMoviesIn30Days), 24)]
public async Task<IEnumerable<MovieResponse>> GetUpcomingMoviesIn30Days(MovieParameters requestParameters)
{
    var thirtyDaysFromNow = DateTime.Now.AddDays(30);

    var movies = await _repositoryManager.Movie.GetAllMoviesAsync(
       m => m.IsReleased.Equals(false)
       && m.ReleaseDate <= thirtyDaysFromNow
       && m.ReleaseDate >= DateTime.Now,
       requestParameters,
       false);

    var mappedResult = _mapper.Map<IEnumerable<MovieResponse>>(movies);

}

我该如何处理这种类型不匹配的情况?

编辑:我已经尝试过以下方法

我尝试改变

invocation.ReturnValue = Task.FromResult(cachedValue);
invocation.ReturnValue = Task.FromResult((dynamic)cachedValue);

不过,这次例外的是

“无法将类型为“System.Threading.Tasks.Task

1[System.Collections.Generic.List
1[Models.Concrete.ResponseModels.Movie.MovieResponse]]”的对象转换为类型为“System.Threading.Tasks.Task
1[System.Collections.Generic.IEnumerable
1[Models. Concrete.ResponseModels.Movie.MovieResponse]]'。”

它提供了

List
但返回类型是
IEnumerable

c# .net type-conversion aop castle-dynamicproxy
1个回答
0
投票

一种选择是使用反射来获取正确的通用

Task<>
类型:

invocation.ReturnValue = typeof(Task)
    .GetMethod("FromResult")
    .MakeGenericMethod(returnType)
    .Invoke(null, new object[]{cachedValue});

您还可以考虑缓存整个任务而不是其结果。

var returnValue = invocation.ReturnValue;
TimeSpan span = TimeSpan.FromHours(_duration);
_cacheService.Add(_key, returnValue, span);

然后你可以直接返回该任务。

invocation.ReturnValue = cachedValue;

这样,如果在任务完成时发生其他调用,您只需执行该工作 (

invocation.Proceed()
) 一次:所有内容最终都会
await
相同的缓存
Task
。缺点是,如果任务失败,您最终会缓存失败的任务,并且后续调用会失败,而不是重试。当然,您仍然可以等待缓存的任务,并在失败时将其从缓存中删除,以允许将来的尝试重试。

_cacheService.Add(_key, returnValue, span);
try
{
    await (Task)returnValue;
}
catch (Exception e)
{
    // ideally log the exception here...
    _cacheService.Remove(_key);
}
© www.soinside.com 2019 - 2024. All rights reserved.