使用公开的API在MVVM应用程序中进行大量包装

时间:2014-11-11 08:56:29

标签: c# wpf design-patterns mvvm

我有一个使用WPF和MVVM开发的桌面应用程序,可以通过公开的API用作库。

我的问题是我发现自己做了很多包装。例如,我有一个客户实体。此实体必须由API包装,以提供诸如Save(),Delete()等功能。同时,GUI中使用的ViewModel需要包装实体以提供与API相同的功能。并且为了添加GUI特定属性,例如:IsSelected,IsExpanded等。

我解决这个问题的方法是将暴露的api包装在ViewModel中,看起来像这样:

Architecture of my application

但我的客户仍有3个班级:

  1. 映射数据库的实体
  2. 通过API公开并包装数据库实体的模型
  3. 以及包装API模型的视图模型。
  4. 添加属性需要我在3或4个位置进行更改,而且我的很多代码只是样板代码,如何设计我的应用程序以减少包装?

2 个答案:

答案 0 :(得分:0)

你可以扩展,而不是仅仅包裹一个外观。

因此,使用您的MVVM实现,模型将扩展(继承)映射到数据库的实体。然后在正确的继承级别插入任何添加的属性。

在这些模型/实体通过绑定显示在UI中之前将视图模型包装起来是很正常的,特别是当涉及列表并且您需要维护每个项目状态或选项时。如果您的模型/实体开始变得非常大,那么您应该考虑使用继承/扩展,而不是包装。当您决定开始拥有过度深度的绑定路径时,重复包装较大的实体会很快导致您违反Law of Demeter,因为您不再希望复制每个外观层中的每个公共属性。

一旦你开始达到这种复杂程度,我个人更喜欢改变模型的角色 - 而不是它是数据对象的表示,它成为负责数据存储库操作的业务层。因此它有一个Save()和GetData()方法,但它对数据实体进行操作,而不是数据实体本身。这降低了复杂性 - 每个模型都可能处理几个不同的数据实体。这是一个微妙的变化 - 模型变得更像客户服务或控制器,并且您有功能区的模型而不是数据类型

答案 1 :(得分:0)

我找到了一个相对简单的设计解决方案:扩展对象模式。

库提供模型和扩展点:

public interface IExtension<T>
{
    void Initialize(T target);
}

// model class
public class Project
{
    ...

    public IExtension<Project> Gui
    {
        get { return serviceProvider.Get<Project>(this); } 
    }
}

// model class
public class Application
{
    ...
    public ObservableCollection<Projects> Projects { get { ... } }

    public IExtension<Application> Gui
    {
        get { return serviceProvider.Get<Application>(this); } 
    }
}

桌面应用程序而不是包装或继承我的模型使用与库相同的api,但实现了IExtension接口,以便使用GUI特定的方法,命令和属性扩展模型。

桌面应用程序包含如下扩展名:

class ProjectExt : IExtension<Project>
{
    Project project;

    public DelegateCommand OpenProjectCommand { get { ... } }
    public DelegateCommand SaveProjectCommand { get { ... } }
    public DelegateCommand CloseProjectCommand { get { ... } }

    public bool IsSelected { get { ... } set { ... } }

    ...
}

并使用服务提供商注入扩展。缺点是xaml不再那么干净了,因为有时我必须引用GUI扩展中的属性和命令,有时我引用库api:

<TextBox Text="{Binding ProjectName}" /> <!--Library API-->
<Button Command="{Binding Gui.SaveProjectCommand}" /> <!--Gui extension-->