【问题标题】:What is the best way to populate a data-bound property from an async method?从异步方法填充数据绑定属性的最佳方法是什么?
【发布时间】:2019-07-06 07:08:21
【问题描述】:

我有一个简单的要求,我正在努力实现。基本上,我有一个填充了企业列表的视图。企业列表的属性体现在我的视图模型类中,该类又绑定到视图。这是一个带有业务列表的简单 MVC 应用程序。

但是,我遇到的问题是我为另一个依赖于视图模型的类派生了业务列表,它基本上类似于我称为 BusinessService 的存储库。繁忙的服务由异步方法组成,这是我的困境,当从 viewModel 的 Ctor 或属性的 getter 进行调用时,我的应用程序挂起。该调用也是对业务服务中的 EF 数据库的异步调用,并且不确定对此的正确方法是什么。请看下面的代码:

视图模型:

    #region Ctor

    public BusinessListViewModel(IBusinessService businessService, IStringBuilder builder)
    {
        _businessService = businessService;
        _builder = builder;

        InitBusinesses().Wait(); //OPTION 1
    }

    #endregion

    #region Properties

    public IEnumerable<BusinessViewModel> _businesses;
    public IEnumerable<BusinessViewModel> Businesses
    {
        get
        {
            if (_businesses == null)
            {
                InitBusinesses().Wait(); //OPTION 2
            }
            return _businesses;
        }
        set => _businesses = value;
    }

    private async Task InitBusinesses()
    {
        var response = await _businessService.Get();
        Businesses = response.IsSuccessful 
                            ? response.Data.Select(p => new BusinessViewModel(_builder, p)) 
                            : new List<BusinessViewModel>();
    }

商业服务:

    #region Service Methods

    public async Task<Response<IEnumerable<Models.Business>>> Get()
    {
        var data = await Db.Businesses.ToListAsync();
        return new Response<IEnumerable<Models.Business>>
        {
            IsSuccessful = true,
            Message = "Successful",
            Data = Mapper.Map<List<Models.Business>>(data)
        };
    }

请您建议最好的模式和正确的方法,我已经知道这是错误的>谢谢

【问题讨论】:

  • 您询问的两个选项都不好。属性不应加载数据。方法应该加载数据。属性应该包含有关对象的信息,而不是从其他来源加载数据的一种方式。在设计用于封装信息(模型)的数据与设计用于检索该信息的逻辑(数据层)之间保持明显的分离
  • @mason 谢谢你的回复,如上所述,我已经明白这不是正确的方法。根据您的解释,即使使用工作单元和存储库模式,您将如何填充属性以绑定到用户界面?
  • 不要让视图模型这样做。让外面的代码来做。它通常看起来像var myViewModel = new MyViewModel(); myViewModel.Businesses = await _repository.GetBusinessesAsync();
  • @mason 正是我出错的地方,我试图让我的控制器尽可能薄,但我认为这种方式最有意义。

标签: c# asp.net-mvc entity-framework asynchronous async-await


【解决方案1】:

我写了一个article on the subject

当 UI 框架要求您的代码显示某些内容时,它必须立即(同步)显示。 ViewModel 构造函数和数据绑定属性应该是同步和即时的。做网络 I/O 根本不是一种选择。即使你让它工作了(这是可能的),只会阻塞你的 UI 线程,从而降低你的用户体验。

更合适的解决方案是同步初始化进入加载状态(“正在加载...”消息、微调器等)并同时启动异步操作。然后,当操作完成时,更新 UI 与实际数据。

【讨论】:

  • @JohnWu:抱歉,我错过了这是 ASP.NET。 MVC 不使用数据绑定;执行此操作的 MVC 方法是让控制器异步加载所有数据,然后在 ViewModel 上设置属性。在 MVC 中,ViewModel 不应该加载自己的数据;这是控制器的责任。
  • @StephenCleary 感谢您的帮助。我现在明白了,在网上的所有例子中,人们以不同的方式做这件事,有时 ViewModel 失去了它的意义,它做得更多。我选择做的是将 BusinessService 注入 Controller 以完成繁重的工作并填充 ViewModel。就像下面 Daniel 提到的那样,我不需要注入视图模型,而是在其中实例化并填充。拍摄
【解决方案2】:

您应该考虑使用返回 Task 的工厂方法

private BusinessListViewModel(IBusinessService businessService, IStringBuilder builder)
{
    _businessService = businessService;
    _builder = builder;
}

public static async Task<BusinessListViewModel> Create(IBusinessService businessService, IStringBuilder builder)
{
    var instance = new BusinessListViewModel(businessService, builder)
    await InitBusiness();
    return instance;
}

【讨论】:

  • 要么使用依赖注入,要么通过接口契约。根据您的示例,使用两者都没有任何意义
猜你喜欢
  • 2015-12-18
  • 1970-01-01
  • 1970-01-01
  • 2021-05-20
  • 2012-08-30
  • 1970-01-01
  • 1970-01-01
  • 2010-10-05
  • 1970-01-01
相关资源
最近更新 更多