SignalR 中“无法访问已处置的对象”崩溃

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

我有一个带有计时器的测试中心,可以将日期发送给所有客户端。

客户端连接后,它会崩溃并显示以下错误:无法访问已处置的对象。

这是错误:

System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'MyHub'.
   at Microsoft.AspNetCore.SignalR.Hub.CheckDisposed()
   at Microsoft.AspNetCore.SignalR.Hub.get_Clients()

这里是中心代码:

public class MyHub : Hub
{
    public MyHub()
    {
        Program.T = new Timer(TickTimer, null, 1000, 1000);
    }

    private void TickTimer(object State)
    {
        try
        {
            var Time = DateTime.UtcNow.ToString(CultureInfo.InvariantCulture);
            Console.WriteLine(Time);

            Clients.All.SendCoreAsync("update", new object[] { Time });
        }
        catch (Exception E)
        {
            Console.WriteLine(E);
            throw;
        }
    }
}

看起来Clients对象已被处理,但我不明白为什么。


编辑,这里有更多信息:

集线器可以来自不同的程序集,因此它们是在 asp 启动的配置部分动态注册的。

每个集线器都装饰有一个属性来识别它并提供路径:

[AttributeUsage(AttributeTargets.Class)]
public class SignalRHub : Attribute
{
    public readonly string Route;

    public SignalRHubPath(string Route)
    {
        this.Route = Route;
    }
}

然后通过这种方式找到并注册它们:

    private static void RegisterHubs(IApplicationBuilder Application)
    {
        // find all SignalR hubs
        var HubsList = ReflectionHelper.FindType<SignalRHubPath>();
        Logging.Info($"Found {HubsList.Count} hubs");

        // get a link to the mapper method of the hubroutebuilder.
        var MapperMethodInfo = typeof(HubRouteBuilder).GetMethod("MapHub", new[] { typeof(PathString) }, null);

        // register them
        foreach (var H in HubsList)
        {
            // get the route attribute
            var Route = string.Empty;
            var Attributes = Attribute.GetCustomAttributes(H);
            foreach (var Attribute in Attributes)
            {
                if (Attribute is SignalRHubPath A) { Route = A.Route; break; }
            }

            // register the hub
            if (string.IsNullOrEmpty(Route))
            {
                Logging.Warn($"[Hub] {H.Name} does not have a path, skipping");
            }
            else
            {
                Logging.Info($"[Hub] Registering {H.Name} with path {Route}");
                // Application.UseSignalR(_ => _.MapHub<Hub>("/" + Route));
                // use the mapper method call instead so we can pass the hub type
                var Path = new PathString("/" + Route);
                Application.UseSignalR(R => MapperMethodInfo.MakeGenericMethod(H).Invoke(R, new object [] { Path }));
            }
        }
    }
c# signalr
3个回答
6
投票

集线器生命周期是按请求计算的(请参阅注释https://learn.microsoft.com/en-us/aspnet/core/signalr/hubs?view=aspnetcore-3.1),因此您会收到已处理的异常,因为您正在访问已处置对象的属性(客户端)。

当您想要向集线器外部的客户端发送消息时(并且您在外部,因为对计时器做出反应,因此在 .netcore 集线器生命周期之后),您应该使用 IHubContext(您可以通过 DI 获取),有一个看看 https://learn.microsoft.com/en-us/aspnet/core/signalr/hubcontext?view=aspnetcore-3.1


1
投票

集线器是暂时的:

不要将状态存储在 hub 类的属性中。每个集线器方法调用都在新的集线器实例上执行。 调用依赖于集线器保持活动状态的异步方法时,请使用await。例如,如果在没有等待的情况下调用 Clients.All.SendAsync(...) 等方法,并且集线器方法在 SendAsync 完成之前完成,则该方法可能会失败。在此处输入链接说明


0
投票

他们更改了框架中的某些内容,导致了错误。

更改前有效的代码:

     private ApplicationUser GetCurrentUser()
 {
      var userName = Context.User.Identity.GetUserName();
      var user = _userManager.FindByName<ApplicationUser, string>(userName);

工作代码:

private async Task<ApplicationUser> GetCurrentUserAsync()
{
    // Use HttpContext.Current.GetOwinContext() to get the OWIN context in ASP.NET 4.8
    var userManager = HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>();

    // Get the current user's username from the authenticated identity
    var userName = Context.User.Identity.GetUserName();

    // Use FindByNameAsync to find the user by username asynchronously
    var user = await userManager.FindByNameAsync(userName);

    // Log the user's filter if available
    if (user?.Filter != null)
    {
        logger.Info("Filter of user: " + user.Name + " is " + user.Filter.IsFilterOn);
    }

    return user;
}
© www.soinside.com 2019 - 2024. All rights reserved.