【问题标题】:Singleton State vs. Singleton Event单例状态与单例事件
【发布时间】:2022-10-24 23:18:36
【问题描述】:

我有一个 Blazor 服务器应用程序。此应用程序连接到 SQL DB,目前相对复杂。由于主要关注可用性,我们在直接访问数据库时遇到了一些问题(组件未正确更新等)。

因此,我正在尝试创建一个StateService,它基本上充当某种“缓存”。数据存储在其中,组件可以访问它,无需任何加载时间。在我的研究过程中,我有一些问题,文档无法回答我。


问题

应该有可能所有组件始终具有数据的最新状态。这意味着需要自动通知客户端任何更改并自动刷新其状态。还应该有能力为大约 1.000 个并发用户提供服务,而无需升级到高端服务器(我知道,这很模糊)。


可能的解决方案

单身状态

我基本上有一个服务,它将数据作为属性保存在其中,并有一个OnChange-event。每当设置任何数据属性时,都会触发事件。然后组件使用此服务来显示数据。当我将数据添加到数据库时,数据将自动加载回状态。我将此服务添加为单例,因此在服务器运行时只有一个对象。

public class SharedStateService
{
    public event Action OnChange;
    private ICollection<MyData>? myData;

    public ICollection<MyData>? MyData
    {
        get => this.myData;
        set
        {
            this.myData= value;
            this.OnChange?.Invoke();
        }
    }
}
public class MyDataService
{
    private readonly SharedStateService sharedStateService;
    private readonly TestDbContext context;

    public MyDataService(TestDbContext context, SharedStateService sharedService)
    {
        this.context = context;
        this.sharedStateService = sharedService;
    }

    public async Task<bool> DeleteData(MyData data)
    {
        try
        {
            this.context.Set<MyData>().Remove(data);
            await this.context.SaveChangesAsync();
        }
        catch (Exception)
        {
            return false;
        }

        await this.ReloadData();
        return true;
    }

    public async Task ReloadData()
    {
        this.sharedStateService.MyData =
            await this.context.Set<MyData>().ToListAsync();
    }
}

在我看来,现在可以订阅OnChange 事件并自由使用MyData 属性。

<table class="table">
    <thead>
        <tr>
            <!-- ... -->
        </tr>
    </thead>
    <tbody>
    @foreach (var data in SharedStateService.MyData)
    {
        <tr>
            <!-- ... -->
        </tr>
    }
    </tbody>
</table>

@code {
    public void Dispose()
    {
        SharedStateService.OnChange -= Refresh;
    }

    protected override void OnInitialized()
    {
        SharedStateService.OnChange += Refresh;
    }

    private async void Refresh()
    {
        await InvokeAsync(this.StateHasChanged);
    }
}

我在这种情况下看到的问题是整个数据不断地存储在服务器上。会不会有什么问题?我是不是想太多了?这种方法可能存在哪些风险?

单例事件

它类似于单例状态,但我不会将数据存储在任何地方。而不是状态,我有一个服务,它只提供一个可以订阅的事件。该服务再次作为单例添加。

public class RefreshService
{
    public event Action OnChange;

    public void Refresh()
    {
        OnChange?.Invoke();
    }
}

然后将该服务注入数据提供者并在发生更改时调用。 我通过一种新方法扩展了MyDataService

public async Task<ICollection<MyData>> GetAll()
{
    return await this.context.Set<MyData>().ToListAsync();
}

之后,在我看来,我添加一个属性并调整Refresh 方法,以将数据加载到此本地属性中。

private async void Refresh()
{
    this.MyData= await MyDataService.GetAll();
    await InvokeAsync(this.StateHasChanged);
}

这种方法与第一种方法非常相似,但我不需要不断地存储数据。这种方法对服务器来说更容易处理吗?由于数据是冗余存储的,这会导致显示错误的数据吗?


我知道这是一篇长篇文章,但也许​​有人知道哪种方法通常比另一种更可取。

【问题讨论】:

    标签: asp.net dependency-injection blazor singleton blazor-server-side


    【解决方案1】:

    听数据更改这不是一个坏主意,我唯一认为我会关注它的是您删除和更改的方式。首先,我将改进使用EFCoreBulkExtensions 只是为了提高性能,如果您每次都更新/删除数据,那么执行该操作并不是一个坏主意(主要是因为您的数据库会随着时间的推移而增长)。

    我认为这是正确的解决方案,它是第二个,单例事件,这样可以让您防止可能导致第一个错误的可能错误。在这种情况下思考:您有 1000 个用户,可能是您的大多数用户同时与数据交互。如果您删除,然后刷新数据可能会导致数据不一致,但如果您获得事件更改状态,您可以将其用作标志,该数据需要在用户与其交互之前更新。

    最后,我认为你可以使用 BulkInsertOrUpdateOrDelete 方法,所以如果数据不存在(带有他们的 id),你插入它,如果有任何变化,它会得到更新,如果它不存在(现有的 id)你删除它,全部采用一种优化的批量扩展方法。如果您不能添加另一个库,您应该创建自己的添加/更新/删除方法!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-04-10
      • 1970-01-01
      • 1970-01-01
      • 2018-06-25
      • 2011-12-05
      • 2011-01-02
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多