Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/svg/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
关于如何重新设计Blazor服务器端应用程序以提高响应速度的建议_Blazor_Blazor Server Side - Fatal编程技术网

关于如何重新设计Blazor服务器端应用程序以提高响应速度的建议

关于如何重新设计Blazor服务器端应用程序以提高响应速度的建议,blazor,blazor-server-side,Blazor,Blazor Server Side,我正在努力解决服务器端应用程序的响应问题。我确信我的设计本身就有一些不好的地方,我无法理解。我为这篇文章提供了一个示例页面,它是我应用程序的简化版本 有一个由Chris Sainty control为rooms list提供的typeahead blazored typeahead控件和一个加入房间的按钮。用户基本上会从typeahead控件中选择一个房间,然后单击join按钮。在同一页中还有一个部分,我在其中显示用户在表控件中加入的所有房间 Typeahead的房间列表是使用数据库查询填充的

我正在努力解决服务器端应用程序的响应问题。我确信我的设计本身就有一些不好的地方,我无法理解。我为这篇文章提供了一个示例页面,它是我应用程序的简化版本

有一个由Chris Sainty control为rooms list提供的typeahead blazored typeahead控件和一个加入房间的按钮。用户基本上会从typeahead控件中选择一个房间,然后单击join按钮。在同一页中还有一个部分,我在其中显示用户在表控件中加入的所有房间

Typeahead的房间列表是使用数据库查询填充的

public async Task JoinRoom()
{
 //this adds the row to database table using async operation.
}

List<Rooms> GetAvailableRooms()
{
   //this is a synchronous operation
   var t = context.Rooms.ToList();
   return t;
}

List<Rooms> GetMyRooms()
{
   //this is a synchronous operation that returns list of user rooms
}


razor:

.
.
<RoomSelectorControl Operation="GetAvailableRooms" />
.
.
@if (SelectedRoom != null)
{
   <div class="col-sm-1">
     <div class="form-group">
       <button type="button" class="btn btn-info" @onclick="JoinRoom">&nbsp;Join&nbsp;</button>
     </div>
   </div>
}

var myRoomsList = GetMyRooms();
foreach (Room myRoom in myRoomsList)
{
    //displays the list in a table control
}

还有,我有两个剃须刀页面,一个房间页面,上面两个座位页面

我的问题是,每当我加载rooms页面或在页面之间切换并最终进入rooms页面时,我都会注意到没有响应的UI,直到同时显示rooms typeahead控件和My rooms部分。最重要的是,房间列表和我的房间部分每次都是从零开始构建的。我认为这也是问题的一部分

所以我的问题是:

如何重新设计代码,以便在应用程序的生命周期中或至少在浏览器会话中,仅初始化typeahead控件的数据一次。 我可以做什么来只构建一次“我的房间”分区,然后仅在他们单击“加入”按钮时刷新它?
围绕你的两部分问题有很多内容,但我要试一试。这形成了一个有点长,公平的警告

首先要指出的是,您应该记住Blazor服务器端的工作方式与传统的浏览器javascript应用程序不同。用户所做的一切都通过信号器连接传回服务器,然后服务器需要在UI线程上处理它,计算DOM差异,并将差异发送回网络。如果您正在执行任何同步操作,这些操作都在同一个线程上运行,因此如果在服务器端有阻塞DB或API调用,这将迫使UI锁定,并等待线程在后端完成长时间运行的过程。为了解决这个问题,您可以使用异步编程技术从UI线程中释放出任何需要花费大量时间的内容。我马上用这个

第二个要考虑的是如何处理应用程序中的并发性。是否有多个用户,并且一个用户添加、修改或删除的数据是否会影响列表中应填充的内容?你可能已经考虑过了,但这是值得考虑的

关于UI锁定的第一点,我将对您的属性名称进行一些假设,但您应该明白这一点

首先,您的列表支持字段

公共列表所有房间{get;set;}=新列表; 这将在应用程序运行之前初始化列表,并避免需要对此项进行空检查

然后,让我们将您的方法更改为任务,以便等待它,当我们执行它以加载数据时,它将在一分钟内应用

任务GetMyRooms { //这是一个同步操作,返回用户房间列表 } 现在开始加载数据。您提到要将数据加载限制为每个浏览器会话一次或每个应用程序生命周期一次。我将继续使用浏览器会话示例,因为如果应用程序在服务器上运行很长时间(数小时、数天、数周),并为多个用户提供服务,那么很容易就会出现过时的数据,并且我假设MyRooms对每个用户都是唯一的

无论哪种方式,您都希望利用Blazor生命周期方法,对于这一种,我们将使用OnAfterRenderAsync方法

AfterRenderAsyncBool firstRender上的受保护重写异步任务 { 增强剂 { //等待从DB或其他持久性设备加载房间, //然后将结果指定给AllRooms属性 AllRooms=等待GetAvailableRooms; //仅当列表在GetAvailableRooms之后未填充时调用此选项 //完成,但可能不会。 州变了 } } 上面的一个要点是,在第一次呈现之后,当加载页面时,初始化逻辑只运行一次。这将使页面唤醒并响应,然后加载数据。您还可以使用OnInitializedAsync方法,并使正文中的第一行为wait Task.Delay1,这将从UI线程中断初始化并允许UI完成呈现,但是初始化的方法在页面加载时会被调用两次,因此您将有两个调用运行到数据存储。出于这个原因,我倾向于采用上面的方法,即加载一个将响应的静态初始状态,然后加载数据

为MyRooms创建列表将以类似的方式工作。更改对任务的调用,让它更新OnAfterRenderAsync方法中的backing属性,然后根据需要在初始化结束时更改调用状态

那么现在您要问的是,多次加载数据会怎么样?那么,您可以考虑服务注入。为此,我们将使用一个作用域服务,该服务的默认寿命为信号器电路的寿命,适合您的需要

因此,在应用程序中添加类似以下类的内容:

公共教室服务 { 公共房间服务//需要注入的任何内容 { //将注入的项目附加到支持字段 } //无论您使用什么实体访问,都通过构造函数DI初始化 私人实体(u实体),; //用于确定是否需要从实体存储加载项的标志 private bool allRoomsInitialLoad=true; private bool myRoomsInitialLoad=true; //所有房间的备份列表 私人房间清单; 公共列表GetAllRooms { 如果!allRoomsInitialLoad 返回房间; _房间=\u entity.whatevermethodgettherooms; allRoomsInitialLoad=假; 返回房间; } //我房间的备份列表 私人房间清单; 公共列表GetMyRoomsint用户ID { 如果!我的房间 返回我的房间; _rooms=\u entity.WhateverMethodGetsMyRoomsByIduserId; myRoomsInitialLoad=假; 返回我的房间; } 公共列表JoinRoomint roomId,int userId { _entity.WhateverMethodAddsYouToRoomByIdsroomId,userId; var room _entity.GetRoomByIdroomId; _我的房间; 返回我的房间; } } 此服务的要点是,公共方法只会在第一次加载时为文件室调用持久层,否则它们将返回持久层列表的内存表示形式。您可能需要针对过时数据的并发性问题进行检查,但这取决于您自己

让我们将其添加到Startup.cs服务集合:

公共无效配置服务服务服务收集服务 { //其他服务 services.AddScoped; } 然后在razor文件的顶部:

@注入房间服务房间服务 现在,您的页面方法可以使用数据服务:

任务GetMyRooms { return RoomService.GetAllRooms; } 如果您想为此切断一些调用链,也可以将服务方法转换为任务并直接等待它们,但这同样取决于您

这种方法需要记住几件事

对于UI,我已经取得了很好的效果,只要尽我最大的努力让所有我能运行的东西都在UI线程之外异步运行,这使得一切都非常响应。如果您也这样做,您可能需要在战略位置使用CancellationTokens构建应用程序,这样,如果您有一个任务启动,然后用户在任务完成前导航离开,您就可以在拆卸时取消该任务。为此,研究部件处理等问题。 在本例中,我省略了JS互操作和将数据存储在客户端浏览器会话存储中的想法。这当然是您可以做的事情,但是连接任何设置、检索和同步逻辑并不是一项小任务,因为您必须开始考虑浏览器DOM树中存在的内容,以及当它存在时,哪些内容会自动同步,哪些内容不会自动同步,等等,因此复杂性急剧增加。 如果您的组件继承了OwningComponentBase,则作用域服务生存期将缩短为活动组件的生存期。这在特定的场景中是很好的,但如果整体使用,将使我上面概述的内容变得相当无效。如果您使用OwningComponentBase,那么在OwningComponentBase上进行设置非常重要,因此请查看 您可能希望根据需要使用不同的生命周期方法,例如,您可以有条件地呈现加载页面或组件。 最后,如果您的文件室列表更改缓慢,并且/或者对于陈旧的数据问题有些持久,那么您可以构建一个长期运行的服务器端缓存,该缓存每隔一段时间从持久性中查询该列表并刷新,然后在RoomsService类中,在第一次加载持久性层之前,可以先查看缓存。这可能是对您所拥有的东西的过度使用,但它也可以减少第一次加载的等待时间,从而产生不同的效果。 这对你来说有点难,希望能有所帮助