SignalR OnDisconnected事件不将数据持久保存到DB

时间:2014-08-26 13:54:02

标签: nhibernate fluent-nhibernate asp.net-mvc-5 signalr castle-windsor

我有一个SignalR中心,我正在注入服务类,通过Castle Windsor将数据保存到本地SQL Server实例。

集线器看起来像:

[Authorize]
public class MyHub : Hub 
{
    private readonly IHubService _hubService;
    private readonly IHubUserService _hubUserService;
    private readonly IUserService _userService;

    public MyHub(IHubService hubService, IHubUserService hubUserService, IUserService userService)
    {
        _hubService = hubService;
        _hubUserService = hubUserService;
        _userService = userService;
    }

    public async Task JoinHub(Guid hubId) 
    {
        var hub = _hubService.GetHubById(hubId);

        if (hub == null)
            throw new NotFoundException(String.Format("Hub ({0}) was not found.", hubId.ToString()));

        var userName = Context.User.Identity.Name;
        var user = _userService.GetUserByUserName(userName);

        if (user == null)
            throw new NotFoundException(String.Format("User ({0}) was not found.", userName));

        var hubUser = new HubUser
        {
            User = user,
            Hub = hub,
            ConnectionId = Context.ConnectionId
        };

        // Persist a new HubUser to the DB
        hubUser = _hubUserService.InsertHubUser(hubUser);

        await Groups.Add(Context.ConnectionId, hub.Id.ToString());
        Clients.Group(hub.Id.ToString()).addChatMessage(userName + " has joined.");
    }

    public async Task LeaveHub()
    {
        var userName = Context.User.Identity.Name;

        var hubUser = _hubUserService.GetHubUserByUserName(userName);

        // Removes HubUser from the DB
        _hubUserService.RemoveHubUser(hubUser);

        await Groups.Remove(Context.ConnectionId, hubUser.Hub.Id.ToString());
        Clients.Group(hubUser.Hub.Id.ToString()).addChatMessage(userName + " has left.");
    }

    public override Task OnDisconnected(bool stopCalled)
    {
        var userName = Context.User.Identity.Name;

        var hubUser = _hubUserService.GetHubUserByUserName(userName);

        // Removes HubUser from the DB
        _hubUserService.RemoveHubUser(hubUser); // This line executes but does not persist anything to DB

        Groups.Remove(Context.ConnectionId, hubUser.Hub.Id.ToString());
        Clients.Group(hubUser.Hub.Id.ToString()).addChatMessage(userName + " has left.");
        return base.OnDisconnected(stopCalled);
    }
}

从客户端调用JoinHub和LeaveHub方法时,一切正常。但是,当OnDisconnected方法触发时,不会从数据库中删除任何内容。我可以看到代码确实执行了,但是记录仍保留在数据库中,不会被删除。

我想知道是否由于castle windsor的依赖生存期或者某事,我的nhibernate会话可能没有将事务提交到数据库,但是,LeaveHub按预期执行是奇怪的,但OnDisconnected方法中没有相同的代码。 / p>

根据this博客帖子,我的依赖项已使用以下配置注册。

Kernel.Register(
    //Nhibernate session factory
    Component.For<ISessionFactory>().UsingFactoryMethod(CreateNhSessionFactory).LifeStyle.Singleton,

    //Nhibernate session
    Component.For<ISession>().UsingFactoryMethod(kernel => kernel.Resolve<ISessionFactory>().OpenSession()).LifeStyle.HybridPerWebRequestTransient()
);

我还注册了一个拦截器来实现一个工作单元模式:

// Unitofwork interceptor
Component.For<NhUnitOfWorkInterceptor>().LifeStyle.HybridPerWebRequestTransient()

如果任何人都可以提供有关LeaveHub方法正常工作原因以及为什么它无法在OnDisconnected方法中保留任何内容的任何输入,那将非常感激。

1 个答案:

答案 0 :(得分:2)

只是一个FYI Nhibernate会话使用异步并不是很好,因为它们根本不是线程安全的。尝试同步运行并看看你得到了什么。

Nhibernate是否设置为在事务提交时刷新?我不能发表评论,因为我是一个新手,但我不久前遇到了这个问题。我没有使用FluentNhibernate,但我确信有一个配置选项可以在事务提交上设置flush。这假设您在事务中包装所有打开的会话调用。我使用类似的东西进行会话。另外去Nhibernate Profiler这是天赐之物。

 public class SessionManager : ISessionManager
{
    private readonly ISessionFactory _sessionFactory;
    private ISession _currentSession;
    private ITransaction _currentTransaction;

    public SessionManager(ISessionFactory sessionFactory)
    {
        _sessionFactory = sessionFactory;
    }

    public ISession OpenSession()
    {
        if (CurrentSessionContext.HasBind(_sessionFactory))
        {
            _currentSession = _sessionFactory.GetCurrentSession();
        }
        else
        {
            _currentSession = _sessionFactory.OpenSession();
            CurrentSessionContext.Bind(_currentSession);
        }
        CurrentSessionContext.Bind(_currentSession);
        _currentTransaction = _currentSession.BeginTransaction();
        return _currentSession;
    }

    public void Dispose()
    {
        try
        {
            if (_currentTransaction != null && _currentTransaction.IsActive)
                _currentTransaction.Commit();
        }
        catch (Exception)
        {
            if (_currentTransaction != null) _currentTransaction.Rollback();
            throw;
        }
        finally
        {
            if (_currentSession != null)
            {
                if (_currentTransaction != null) _currentTransaction.Dispose();
                _currentSession.Close();
            }
        }
    }
}

这是我的配置,我在几个应用程序上使用它。一方面不存在我不使用FluentNhibernate的原因,内置代码的映射非常灵活。请告诉我,我可以向您发送一些示例映射。

  public class SessionFactoryBuilder
{
    public static ISessionFactory BuildSessionFactory(string connectionString)
    {
        var cfg = new Configuration();
        cfg.DataBaseIntegration(db =>
        {
            db.Dialect<MsSql2012Dialect>();
            db.Driver<Sql2008ClientDriver>();
            db.ConnectionString = connectionString;
            db.BatchSize = 1500;
            db.LogSqlInConsole = false;
            db.PrepareCommands = true;
            db.ConnectionReleaseMode = ConnectionReleaseMode.AfterTransaction;
            db.IsolationLevel = IsolationLevel.ReadCommitted;
        })
            .SetProperty(Environment.CurrentSessionContextClass, "web")
            .SetProperty(Environment.UseSecondLevelCache, "true")
            .SetProperty(Environment.ShowSql, "true")
            .SetProperty(Environment.PrepareSql, "true")
            .Cache(c =>
            {
                c.UseQueryCache = true;
                c.Provider<RtMemoryCacheProvider>();
                c.DefaultExpiration = 1440;
            }).SessionFactory().GenerateStatistics();

        HbmMapping mapping = GetMappings();
        cfg.AddDeserializedMapping(mapping, "AppName");
        SchemaMetadataUpdater.QuoteTableAndColumns(cfg);
        return cfg.BuildSessionFactory();
    }

    private static HbmMapping GetMappings()
    {
        var mapper = new ModelMapper();

        mapper.AddMappings(typeof (UserMap).Assembly.GetTypes());
        HbmMapping mapping = mapper.CompileMappingForAllExplicitlyAddedEntities();
        return mapping;
    }
}

这是一个用于管理与Castle的SignalR依赖关系的简洁方法。你可能想试试这个只是为了咯咯笑。

  public class SignalRDependencyResolver : Microsoft.AspNet.SignalR.DefaultDependencyResolver
{
    private readonly IWindsorContainer _container;

    public SignalRDependencyResolver(IWindsorContainer container)
    {
        if (container == null)
        {
            throw new ArgumentNullException("container");
        }
        _container = container;
    }

    public override object GetService(Type serviceType)
    {
        return TryGet(serviceType) ?? base.GetService(serviceType);
    }

    public override IEnumerable<object> GetServices(Type serviceType)
    {
        return TryGetAll(serviceType).Concat(base.GetServices(serviceType));
    }

    [DebuggerStepThrough]
    private object TryGet(Type serviceType)
    {
        try
        {
            return _container.Resolve(serviceType);
        }
        catch (Exception)
        {
            return null;
        }
    }

    private IEnumerable<object> TryGetAll(Type serviceType)
    {
        try
        {
            Array array = _container.ResolveAll(serviceType);
            return array.Cast<object>().ToList();
        }
        catch (Exception)
        {
            return null;
        }
    }
}

在设置控制器工厂之前将其置于全局asax中

 // SignalR
        _container.Register(Classes.FromThisAssembly().BasedOn(typeof(IHub)).LifestyleTransient());
        SignalRDependencyResolver signalRDependencyResolver = new SignalRDependencyResolver(_container);
        Microsoft.AspNet.SignalR.GlobalHost.DependencyResolver = signalRDependencyResolver;