将非贫血域模型与 Wpf MVVM 结合使用

发布于 2024-10-27 03:55:28 字数 287 浏览 6 评论 0原文

我正在使用 MVVM 作为 UI 实现基于 WPF 的应用程序。

我有一个 ViewModel,它包装了每个可编辑的可编辑模型。 VM 包含用于处理错误通知、“脏”管理等的所有逻辑。

此设计很好地支持简单域模型对象的 CRUD 方案,这些对象贫血,即不包含任何逻辑。

现在,我面临一个更棘手的问题,因为我有一个包含逻辑的域模型,并且该逻辑可以更改域模型的内部状态。

有人已经遇到过这种情况吗?如果是这样,您有一些正确处理这个问题的建议吗?

瑞安娜

I am implementing a WPF based application using MVVMfor the UI.

I have a ViewModel that wraps each editable Model that can be edited. The VM contains all the logic for handling error notifications, "is dirty" management and so forth ..

This design supports well CRUD schenarios for simple domain Model objects that are anemic, that is, do not contain any logic.

Now, I am facing a more tricky problem cause I have a domain Model that contains logic and that logic can change the internal state of the domain Model.

Do someone have already faced this scenario ? If so, do you have some advices to handle this correctly ?

Riana

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

转角预定愛 2024-11-03 03:55:28

以下是我通常处理它的方式:

  1. ViewModel 层由属于该层的类型组成,这意味着我不会直接在 ViewModel 内使用我的业务对象。我将业务对象映射到 ViewModel 对象,这些对象的形状可能完全相同,也可能不完全相同,但减去了行为。可以说这违反了“不要重复自己”,但这样做可以让您遵守单一责任原则。在我看来,SRP 通常应该胜过 DRY。 ViewModel 的存在是为了服务于视图,而模型的存在是为了服务于业务规则/行为。

  2. 我创建了一个外观/服务层,它接受并返回 ViewModel 作为参数,但将 ViewModel 映射到相应的业务对象版本。这样,非贫血对象就不会在 ViewModel 上强加非视图逻辑

依赖关系如下所示:
ViewModel <-->;外观/服务层 -->业务对象

我认为,如果您想释放 MVVM 的全部潜力,记住这一点很重要:ViewModel 是视图的模型/抽象,而不是呈现给视图的模型

Here is how I usually deal with it:

  1. The ViewModel layer is made of types that belong to this layer, meaning I don't ever directly use my business objects inside of a ViewModel. I map my business objects to ViewModel objects that may or may not be the exact same shape minus the behaviors. It can be argued that this violates Don't Repeat Yourself, but doing so allows you to adhere to the Single Responsibility Principle. In my opinion, SRP should usually trump DRY. The ViewModel exists to serve the view, and the model exists to serve business rules / behavior.

  2. I create a facade/service layer that takes and returns ViewModels as arguments, but maps the ViewModels to-and-from their corresponding business object versions. This, way the non-anemic objects won't impose non view logic on the ViewModel

The dependencies would look like this:
ViewModel <--> Facade/ServiceLayer --> Business Objects

I think it is important to keep this in mind if you want to unleash the full potential of MVVM: The ViewModel is the model/abstraction of the view, not the model presented to the view.

少女净妖师 2024-11-03 03:55:28

尝试使用命令模式。您的屏幕不应设计为编辑实体,而是对实体执行操作(命令)。如果您在设计屏幕时遵循该原则,您的 ViewModel 将具有应映射到命令对象的属性。然后,命令将被发送到域模型的(远程)外观。

用于显示数据的 ViewModel 可以直接映射到数据库(完全绕过域模型),这样您就不需要在域模型类中放置讨厌的 getter。

Try using Command pattern. Your screen should be design not to edit an entity but to perform an action (command) on an entity. If you follow that principle when designing your screens, your ViewModel will have properties that should be mapped to a command object. Then, the command will be send to an (remote) facade of the domain model.

ViewModels for displaying the data could be mapped directly to the database (bypassing the domain model altogether) so that you don't need to put nasty getters in the domain model classes.

星軌x 2024-11-03 03:55:28

如果域模型是非贫血的,您将需要使用事件将模型中的内部更改传达回视图模型。这样,您就不必担心跟踪哪些操作可能会使您的虚拟机与模型不同步。

下面是一个简单的示例:

首先,是一个示例模型:

public class NonAnemicModel
{
    private string _name;

    public string Name
    {
        get { return _name; }
        set
        {
            if (_name == value)
                return;

            _name = value;
            OnNameChanged(EventArgs.Empty);
        }
    }

    public event EventHandler NameChanged;
    protected virtual void OnNameChanged(EventArgs e)
    {
        if (NameChanged != null)
            NameChanged(this, e);
    }

    public void PerformNameCalculation(int chars)
    {
        //example of a complex logic that inadvertently changes the name
        this.Name = new String('Z', chars); //makes a name of Z's
    }
}

这是一个示例 ViewModel:

public class MyViewModel : INotifyPropertyChanged
{
    private NonAnemicModel _model;

    public NonAnemicModel Model 
    {
        get { return _model; }
        set
        {
            _model = value;
            _model.NameChanged += (sender, args) => NotifyPropertyChanged("UserName");
        }
    }

    public string UserName 
    {
        get { return this.Model.Name; }
        set { this.Model.Name = value; }
    }

    //this command would call out to the PerformNameCalculation method on the Model.
    public ICommand PerformNameCalculation { get; private set; }
}

请注意,当模型上的 Name 发生更改时,将引发 PropertyChanged 事件。这样,无论是否使用 UserName setter,或者使用 PerformNameCalculation 命令,ViewModel 都会保持同步。这样做的最大缺点是您必须向模型添加更多事件,但我发现从长远来看,将这些事件放在适当的位置通常非常有帮助。 请注意事件的内存泄漏!

If the domain model is non-anemic, you will need to use events to communicate internal changes in the Model back to the ViewModel. That way you don't have to worry about keeping track of what operations could potentially make your VM out-of-sync with the model.

Here's a simple example:

First, a sample model:

public class NonAnemicModel
{
    private string _name;

    public string Name
    {
        get { return _name; }
        set
        {
            if (_name == value)
                return;

            _name = value;
            OnNameChanged(EventArgs.Empty);
        }
    }

    public event EventHandler NameChanged;
    protected virtual void OnNameChanged(EventArgs e)
    {
        if (NameChanged != null)
            NameChanged(this, e);
    }

    public void PerformNameCalculation(int chars)
    {
        //example of a complex logic that inadvertently changes the name
        this.Name = new String('Z', chars); //makes a name of Z's
    }
}

And here's a sample ViewModel:

public class MyViewModel : INotifyPropertyChanged
{
    private NonAnemicModel _model;

    public NonAnemicModel Model 
    {
        get { return _model; }
        set
        {
            _model = value;
            _model.NameChanged += (sender, args) => NotifyPropertyChanged("UserName");
        }
    }

    public string UserName 
    {
        get { return this.Model.Name; }
        set { this.Model.Name = value; }
    }

    //this command would call out to the PerformNameCalculation method on the Model.
    public ICommand PerformNameCalculation { get; private set; }
}

Notice that the PropertyChanged event is raised when the Name on the model changes. That way, regardless of whether the UserName setter was used, or the PerformNameCalculation command was used, the ViewModel stays in sync. The big downside to this is that you have to add many more events to your Model, but I've found that having these events in place is usually very helpful in the long run. Just be careful about memory leaks with events!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文