Blazor + SQLTableDependency + SignalR:从OnChange事件通知特定组

如何解决Blazor + SQLTableDependency + SignalR:从OnChange事件通知特定组

我有一个Blazor应用程序,该应用程序使用SQLTableDependency检测数据库更改,然后通过SignalR通知所有客户端有关更改的信息。这可行,但是我需要一种方法来检测更改,并且仅通知特定的SignalR组。由于SQLTableDependency并不关心谁在数据库中插入,更改或删除了一条记录,因此我不确定如何也知道该发送更新的组。有关我的应用以及我要完成的工作的更多详细信息,请参见下文。

我们为每个客户建立一个新的组织。一个组织拥有自己的资产列表,并且可以有多个用户。

Organization.cs

    public class Organization
    {
    public int OrganizationId { get; set; }

    public string OrganizationName { get; set; }

    public List<Asset> Assets { get; set; }

    public List<ApplicationUser> Users { get; set; }

    public bool IsDisabled { get; set; }

   }

Asset.cs

public class Asset
{
    public int AssetId { get; set; }

    public string SerialNumber { get; set; }

    public int OrganizationId { get; set; }

    public virtual Organization Organization { get; set; }

    public DateTime DateAdded { get; set; }
}

ApplicationUser.cs

 public class ApplicationUser 
{
    public string FirstName { get; set; }

    public string LastName { get; set; }

    public int OrganizationId { get; set; }

    public virtual Organization Organization { get; set; }

    public List<Connection> Connections { get; set; }

    public string Timezone { get; set; }

}

Connection.cs-我将每个SignalR连接存储在数据库中。

    public class Connection
    {
    public string ConnectionId { get; set; }

    public string UserName { get; set; }

    public bool Connected { get; set; }

    public string Group { get; set; }

    public DateTime ConnectionTime { get; set; }

    }

AssetService.cs

    public class AssetService : IAssetService
{
    private readonly IServiceScopeFactory _serviceScopeFactory;

    public AssetService(IServiceScopeFactory serviceScopeFactory)
    {
        _serviceScopeFactory = serviceScopeFactory;
    }
  
         public async Task<Asset> AddAssetAsync(Asset asset,string currentUserName)
    {
        try
        {
            using (var scope = _serviceScopeFactory.CreateScope())
            {
                var db = scope.ServiceProvider.GetService<DataContext>();

                if (asset.Device != null)
                {
                    db.Entry(asset.Device).State = EntityState.Modified;
                }
                asset.DateAdded = DateTime.UtcNow;
                await db.Assets.AddAsync(asset);
                await db.SaveChangesAsync();
                return asset;
            }
        }
        catch (System.Exception ex)
        {
           throw ex;
        }
    }
}

AssetHub.cs-SignalR集线器

 public class ChatHub : Hub
{
    private readonly UserManager<ApplicationUser> _userManager;
    private readonly IServiceScopeFactory _serviceScopeFactory;

    public ChatHub(UserManager<ApplicationUser> userManager,IServiceScopeFactory serviceScopeFactory)
    {
        _userManager = userManager;
        _serviceScopeFactory = serviceScopeFactory;
    }

    public async Task SendAssetToGroup(string userName,string location,Asset asset)
    {

        if (!string.IsNullOrWhiteSpace(userName))
        {
            var user = await _userManager.Users.Include(x => x.Connections).SingleAsync(x => x.UserName == userName);

            if (user != null)
            {
                var group = $"{user.AccountId}-{location}";

                await Clients.Group(group).SendAsync("AssetUpdate",user.Email,asset);
            }
        }
    }

    public override async Task OnConnectedAsync()
    {
        var httpContext = Context.GetHttpContext();
        var location = httpContext.Request.Query["location"];

        using (var scope = _serviceScopeFactory.CreateScope())
        {
            var db = scope.ServiceProvider.GetService<ApplicationDbContext>();
            if (!string.IsNullOrWhiteSpace(userName))
            {

                var user = await db.Users.Include(x => x.Connections).SingleAsync(x => x.UserName == httpContext.User.Identity.Name);

                if (user != null)
                {
                    var group = $"{user.OrganizationId}-{location}";
                    var connection = new Connection { Connected = true,ConnectionId = Context.ConnectionId,Group = group,UserName = user.UserName };

                    await Groups.AddToGroupAsync(connection.ConnectionId,group);

                    user.Connections.Add(connection);

                    db.Users.Update(user);
                }
            }
           
            await db.SaveChangesAsync();
        }
        await base.OnConnectedAsync();
    }

    public override async Task OnDisconnectedAsync(Exception exception)
    {
        if (!string.IsNullOrWhiteSpace(Context.ConnectionId))
        {
            using (var scope = _serviceScopeFactory.CreateScope())
            {
                var db = scope.ServiceProvider.GetService<ApplicationDbContext>();

                var connection = await db.Connections.Where(x => x.ConnectionId == 
                Context.ConnectionId).FirstOrDefaultAsync();

                if (connection != null)
                {
                    await Groups.RemoveFromGroupAsync(connection.ConnectionId,connection.Group);
                    db.Connections.Remove(connection);
                    await db.SaveChangesAsync();
                }
            }
        }

        await base.OnDisconnectedAsync(exception);
    }
}

AssetTableChangeService.cs-这是我需要帮助的地方。当SQLTableDependency检测到Assets表的更改时,我需要能够在AssetHub中调用SendAssetToGroup方法。由于用户属于组织机构,因此我不想将更新发布给所有组织,我只想仅将更新发送给特定组织组之外的用户。

 public class AssetTableChangeService : IAssetTableChangeService
{
    private const string TableName = "Assets";
    private SqlTableDependency<Asset> _notifier;
    private IConfiguration _configuration;

    public event AssetChangeDelegate OnAssetChanged;

    public StockTableChangeService(IConfiguration configuration)
    {
        _configuration = configuration;

        // SqlTableDependency will trigger an event 
        // for any record change on monitored table  
        _notifier = new SqlTableDependency<Asset>(
             _configuration.GetConnectionString("DefaultConnection"),TableName);
        _notifier.OnChanged += AssetChanged;
        _notifier.Start();
    }

    private void AssetChanged(object sender,RecordChangedEventArgs<Asset> e)
    {

        OnAssetChanged.Invoke(this,new AssetChangeEventArgs(e.Entity,e.EntityOldValues));
    }

    public void Dispose()
    {
        _notifier.Stop();
        _notifier.Dispose();
    }

因此流程应如下所示。

  1. 用户登录-通过SignalR建立连接
  2. 连接信息存储在数据库中。
  3. 根据用户从哪个页面和OrganizationId将连接添加到SignalR组。
  4. 用户从用户界面创建新资产。
  5. 在资产服务中调用AddAsset方法。
  6. 资产被插入到数据库中。
  7. SQLTableDependency检测到更改,然后调用AssetChanged处理程序方法。
  8. AssetChanged处理程序方法调用OnAssetChanged事件。
  9. AssetHub需要预订OnAssetChanged事件。
  10. 触发OnAssetChanged事件时,AssetHub中的处理程序方法需要调用SendAssetToGroup方法。
  11. 当用户从“资产”页面导航到另一个页面时,SignalR连接将从数据库中删除,并且该连接也从组中删除。

直到第9步和第10步,我的所有工作都可以进行。由于SQLTableDependency不在乎谁进行了更改,因此是否有其他方法可以使之成为可能,所以我无法查找需要更新的连接组。也被推。有什么想法吗?

解决方法

当用户界面正在使用一个类时,例如:Student

UI组件加入了一个名为“ Student”或“ BlahNamespace.Student”的组。 如果其列表仅是名称,则如果其实体将名称和另一个ID作为字符串连接的组连接在一起 “ BlahNamespace.Student:201”如果您的数据库知道该实体的信息,那么您也可以附加组织的名称以获取更好的信息。

服务器可以根据操作通知组。

我将集线器注入API控制器以实现这一目标。

我个人不会使用信号发送器服务来传输数据,保持它的重量轻,而只是“通知”更改。然后,客户可以决定如何处理。这样一来,只有通过所有已配置的安全性,才能通过API以一种方式访问​​数据。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-