最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c# - Proxied Service error (Unable to cast object when types are inheritable) - Stack Overflow

programmeradmin1浏览0评论

The error occurs only with ReverseProxy Server. With Normal Server everything is fine. ReverseProxy Server creates proxied Instances of services and injects them in the controller. In the proxied service I got the error:

Unable to cast object of type 'd__1[NotificationMarkResultDto]' to type 'System.Collections.Generic.IReadOnlyCollection`1[NotificationMarkResultDto]'.

When I debug the Reverse Proxy Server, go to the controller, then I can see the contents of the injected service in which an error occurs:

debug screenshot Injected NotificationService

These are methods where proxy services are created.

public INotificationService GetNotificationService()
{
    return _host.Get<INotificationService>(INotificationService.NetServiceId, NetServiceFactory.Instance);
}

private sealed class NetServiceFactory : INetServiceFactory
{
    public static readonly INetServiceFactory Instance = new NetServiceFactory();

    /// <inheritdoc />
    public string ResolveServiceAddress(string serviceKey)
    {
        throw new NotSupportedException();
    }

    /// <inheritdoc />
    public TServiceContract CreateProxy<TServiceContract>(INetServiceProxy netServiceProxy)
    {
        return NetServiceDispatchProxy.Create<TServiceContract>(netServiceProxy)!;
    }
}

internal class NetServiceDispatchProxy : DispatchProxy
{
    private INetServiceProxy? _netServiceProxy;

    public static TServiceContract? Create<TServiceContract>(INetServiceProxy netServiceProxy)
    {
        object? proxy = DispatchProxy.Create<TServiceContract, NetServiceDispatchProxy>() as NetServiceDispatchProxy;
        if (proxy == null)
            return default;

        ((NetServiceDispatchProxy)proxy).Initialize(netServiceProxy);

        return (TServiceContract)proxy;
    }

    /// <inheritdoc />
    protected override object? Invoke(MethodInfo? targetMethod, object?[]? args)
    {
        var cts = new CancellationTokenSource(30000);
        return _netServiceProxy?.Invoke(targetMethod, args, cts.Token);
    }

    private void Initialize(INetServiceProxy netServiceProxy)
    {
        _netServiceProxy = netServiceProxy;
    }
}

Why does an error occur if IReadOnlyCollection appears in the inheritance chain of `List'? Maybe something strange happend in Initialization or Serialization of the proxied Service?

I get a returned object IReadOnlyCollection from this method:

public async Task<IReadOnlyCollection<NotificationMarkResultDto>> MarkNotifications(AccountId accountKey, NotificationDto[] notificationsToUpdate)
{
    var result = new List<NotificationMarkResultDto>(notificationsToUpdate.Length);
    var notificationKeys = notificationsToUpdate.Select(x => x.NotificationKey).ToHashSet();
    var notifications = await _uow.Notifications.GetNotifications(notificationKeys, accountKey);

    foreach (var updateItem in notificationsToUpdate)
    {
        var foundNotification = notifications.FirstOrDefault(x => x.NotificationKey == updateItem.NotificationKey);

        if (foundNotification != null) 
        {
            foundNotification.Status = updateItem.Status ?? foundNotification.Status;
            foundNotification.Importance = updateItem.Important switch
            {
                true => NotificationImportance.High,
                false => NotificationImportance.Low,
                _ => foundNotification.Importance,
            };

            result.Add(new NotificationMarkResultDto(updateItem.NotificationKey, foundNotification.Status, foundNotification.Importance));
        }
        else 
        {
            result.Add(new NotificationMarkResultDto(updateItem.NotificationKey, "Notificaton not found"));
        }
    }

    await _uow.SaveAsync();

    return result;
}

This method is called in the controller, controller looks like this:

[HttpPost("marks")]
[Produces(MediaTypeNames.Application.Json)]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ValidationProblemDetails))]
[ProducesResponseType(StatusCodes.Status401Unauthorized, Type = typeof(ProblemDetails))]
[ProducesResponseType(StatusCodes.Status500InternalServerError, Type = typeof(ProblemDetails))]
public async Task<IReadOnlyCollection<NotificationMarkResultDto>> MarkNotifications([FromBody, Required] NotificationDto[] update)
{
    return await _notificationService.MarkNotifications(params);
}

The whole error contains these lines:

{
    "type": ";,
    "title": "Unable to cast object of type '<Enumerate>d__1[Acp.Domain.Queries.Dtos.Notifications.NotificationMarkResultDto]' to type 'System.Collections.Generic.IReadOnlyCollection`1[Notifications.NotificationMarkResultDto]'.",
    "status": 500,
    "detail": "System.InvalidCastException: Unable to cast object of type '<Enumerate>d__1[NotificationMarkResultDto]' to type 'System.Collections.Generic.IReadOnlyCollection`1[Notifications.NotificationMarkResultDto]'.\n 
at Net.NetCommHost.NetServiceProxy.TaskWrapper`1.<>c.<Wrap>b__0_0(Task`1 x)
\n at System.Threading.Tasks.ContinuationResultTaskFromResultTask`2.InnerInvoke()\n 
at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)\n--- End of stack trace from previous location ---\n 
at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)\n 
at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread)\n--- End of stack trace from previous location ---\n 
at Controllers.NotificationController.MarkNotifications(NotificationDto[] update) in /source/src/Controllers/NotificationController.cs:line 101\n at lambda_method305(Closure, Object)\n 
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableObjectResultExecutor.Execute(ActionContext actionContext, IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)\n 
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)\n 
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)\n 
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)\n 
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)\n 
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)\n 
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextExceptionFilterAsync>g__Awaited|26_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)"
}

In the IDE, the compiler highlights explicit type conversion for service method in gray color as unnecessary code: "IDE0004 Cast is redudant"

return (IReadOnlyCollection<NotificationMarkResultDto>)result;

The error occurs only with ReverseProxy Server. With Normal Server everything is fine. ReverseProxy Server creates proxied Instances of services and injects them in the controller. In the proxied service I got the error:

Unable to cast object of type 'd__1[NotificationMarkResultDto]' to type 'System.Collections.Generic.IReadOnlyCollection`1[NotificationMarkResultDto]'.

When I debug the Reverse Proxy Server, go to the controller, then I can see the contents of the injected service in which an error occurs:

debug screenshot Injected NotificationService

These are methods where proxy services are created.

public INotificationService GetNotificationService()
{
    return _host.Get<INotificationService>(INotificationService.NetServiceId, NetServiceFactory.Instance);
}

private sealed class NetServiceFactory : INetServiceFactory
{
    public static readonly INetServiceFactory Instance = new NetServiceFactory();

    /// <inheritdoc />
    public string ResolveServiceAddress(string serviceKey)
    {
        throw new NotSupportedException();
    }

    /// <inheritdoc />
    public TServiceContract CreateProxy<TServiceContract>(INetServiceProxy netServiceProxy)
    {
        return NetServiceDispatchProxy.Create<TServiceContract>(netServiceProxy)!;
    }
}

internal class NetServiceDispatchProxy : DispatchProxy
{
    private INetServiceProxy? _netServiceProxy;

    public static TServiceContract? Create<TServiceContract>(INetServiceProxy netServiceProxy)
    {
        object? proxy = DispatchProxy.Create<TServiceContract, NetServiceDispatchProxy>() as NetServiceDispatchProxy;
        if (proxy == null)
            return default;

        ((NetServiceDispatchProxy)proxy).Initialize(netServiceProxy);

        return (TServiceContract)proxy;
    }

    /// <inheritdoc />
    protected override object? Invoke(MethodInfo? targetMethod, object?[]? args)
    {
        var cts = new CancellationTokenSource(30000);
        return _netServiceProxy?.Invoke(targetMethod, args, cts.Token);
    }

    private void Initialize(INetServiceProxy netServiceProxy)
    {
        _netServiceProxy = netServiceProxy;
    }
}

Why does an error occur if IReadOnlyCollection appears in the inheritance chain of `List'? Maybe something strange happend in Initialization or Serialization of the proxied Service?

I get a returned object IReadOnlyCollection from this method:

public async Task<IReadOnlyCollection<NotificationMarkResultDto>> MarkNotifications(AccountId accountKey, NotificationDto[] notificationsToUpdate)
{
    var result = new List<NotificationMarkResultDto>(notificationsToUpdate.Length);
    var notificationKeys = notificationsToUpdate.Select(x => x.NotificationKey).ToHashSet();
    var notifications = await _uow.Notifications.GetNotifications(notificationKeys, accountKey);

    foreach (var updateItem in notificationsToUpdate)
    {
        var foundNotification = notifications.FirstOrDefault(x => x.NotificationKey == updateItem.NotificationKey);

        if (foundNotification != null) 
        {
            foundNotification.Status = updateItem.Status ?? foundNotification.Status;
            foundNotification.Importance = updateItem.Important switch
            {
                true => NotificationImportance.High,
                false => NotificationImportance.Low,
                _ => foundNotification.Importance,
            };

            result.Add(new NotificationMarkResultDto(updateItem.NotificationKey, foundNotification.Status, foundNotification.Importance));
        }
        else 
        {
            result.Add(new NotificationMarkResultDto(updateItem.NotificationKey, "Notificaton not found"));
        }
    }

    await _uow.SaveAsync();

    return result;
}

This method is called in the controller, controller looks like this:

[HttpPost("marks")]
[Produces(MediaTypeNames.Application.Json)]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ValidationProblemDetails))]
[ProducesResponseType(StatusCodes.Status401Unauthorized, Type = typeof(ProblemDetails))]
[ProducesResponseType(StatusCodes.Status500InternalServerError, Type = typeof(ProblemDetails))]
public async Task<IReadOnlyCollection<NotificationMarkResultDto>> MarkNotifications([FromBody, Required] NotificationDto[] update)
{
    return await _notificationService.MarkNotifications(params);
}

The whole error contains these lines:

{
    "type": "https://httpstatuses/500",
    "title": "Unable to cast object of type '<Enumerate>d__1[Acp.Domain.Queries.Dtos.Notifications.NotificationMarkResultDto]' to type 'System.Collections.Generic.IReadOnlyCollection`1[Notifications.NotificationMarkResultDto]'.",
    "status": 500,
    "detail": "System.InvalidCastException: Unable to cast object of type '<Enumerate>d__1[NotificationMarkResultDto]' to type 'System.Collections.Generic.IReadOnlyCollection`1[Notifications.NotificationMarkResultDto]'.\n 
at Net.NetCommHost.NetServiceProxy.TaskWrapper`1.<>c.<Wrap>b__0_0(Task`1 x)
\n at System.Threading.Tasks.ContinuationResultTaskFromResultTask`2.InnerInvoke()\n 
at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)\n--- End of stack trace from previous location ---\n 
at System.Threading.ExecutionContext.RunFromThreadPoolDispatchLoop(Thread threadPoolThread, ExecutionContext executionContext, ContextCallback callback, Object state)\n 
at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task& currentTaskSlot, Thread threadPoolThread)\n--- End of stack trace from previous location ---\n 
at Controllers.NotificationController.MarkNotifications(NotificationDto[] update) in /source/src/Controllers/NotificationController.cs:line 101\n at lambda_method305(Closure, Object)\n 
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableObjectResultExecutor.Execute(ActionContext actionContext, IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)\n 
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)\n 
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)\n 
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)\n 
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)\n 
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)\n 
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextExceptionFilterAsync>g__Awaited|26_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)"
}

In the IDE, the compiler highlights explicit type conversion for service method in gray color as unnecessary code: "IDE0004 Cast is redudant"

return (IReadOnlyCollection<NotificationMarkResultDto>)result;
Share Improve this question edited Mar 21 at 13:18 Taya asked Mar 13 at 11:10 TayaTaya 13 bronze badges 4
  • 1 "the compiler highlights explicit type conversion in gray color as unnecessary code." What are you referring to and where is that in your code ? Like NotificationMarkResultDto is in the service and the controller isn't the same class and you expecting some type conversion somewhere occurring? – Ralf Commented Mar 13 at 14:16
  • Are you make sure the runtime for different environment is the same? – Brando Zhang Commented Mar 14 at 6:14
  • @BrandoZhang Update information: deployed the same version in docker, the error is still not reproducible in there. I checked the file history in git, the error has been appearing since the method began to return the IReadOnlyCollection type (previously the method was void) – Taya Commented Mar 14 at 8:25
  • Consider either adding an Answer yourself, or closing the question. At the moment nobody except you might be able to write an Answer. – Peter B Commented Mar 17 at 21:06
Add a comment  | 

1 Answer 1

Reset to default 0

Problem was solved, when I set return value of MarkNotifications from IReadOnlyCollection<NotificationMarkResultDto> to NotificationMarkResult[] and in NotificationMarkResultDto were no setters written before (because I wanted to do that for the immutable response), but now they have been added.

发布评论

评论列表(0)

  1. 暂无评论