MVVM:我是否使用事件或方法调用来在ViewModel和Model之间进行通信

时间:2014-11-21 15:19:43

标签: c# wpf mvvm

这是一项设计决定,但由于我是这个项目的唯一开发人员,我没有其他人可以问。

我有一个MVVM项目,我有一个允许用户将人员添加到聚会预订的视图。 有一个商业规则(和那个讨厌的“现实”)说一个人不能被添加到同一个聚会的预订中。 View调用RelayCommand来添加Person。

由于有添加Person的业务规则,我知道我需要调用Model。我的问题是:怎么样?

我可以在Model中调用一个方法,传递Person,如果返回true,我可以添加Person。 - 要么 - 我可以订阅ViewModel的PropertyChanged,如果有人添加,请检查它们并在属性失败时清除它。

前进的更好方法是什么?

2 个答案:

答案 0 :(得分:6)

我不同意@Gigi。

ViewModel应仅包含与ViewModel / View相关的表示逻辑,例如处理验证或事件(如点击)或格式化视图的输入/输出。但是,对于同一方而言,不能将Person添加到Reservation中,可能会在所有地方强制执行,而不仅仅是在您的ViewModel中(即如果您有不同的ViewModel执行类似的操作,则必须重复您的逻辑,很糟糕)。

相反,您应该有用于处理预留的服务类,其中包含相关逻辑并将此服务注入您的ViewModel并从那里调用它的操作。

// Implement interface, if you go for dependency injection or unit tests
public class PartyRegistrationService 
{   
    private RegistrationRepository registrationRepository;
    private PartyRepository partyRepository;
    private PersonRepository personRepository;

    // inject the repositories here, if you use DI
    public PartyRegistrationService() 
    {
        this.registrationRepository = new RegistrationRepository();
        this.personRepository = new PersonRepository();
        this.partyRepository = new PartyRepository();
    }

    public bool RegisterPersonToParty(int personId, int partyId) 
    {
        Person person = personRepository.GetById(personId);
        Party party = partyRepository.GetById(partyId);

        // invalid person or party id
        if(person==null || party==null)
            return false;

        if(registrationRepository.GetByPerson(person)!=null) 
        {
            // person has a registration already
            return false;
        }

        // person has no registration yet
        var registration = new Registration(person, party);
        registrationRepository.Save(registration);

        return true;
    }
}

这种方式将您的逻辑耦合到服务类中,并从ViewModel中调用服务RegisterPersonToParty并将ID传递给它。理想情况下,您使用AsyncRelayCommand来避免在存储库/数据库操作期间进行UI锁定。

不要在ViewModels属性中使用这种异步/昂贵/昂贵的操作!

编辑:添加评论 根据您是否要使用富域或贫血域(后来被许多现代开发人员所劝阻),可以将某些类型的逻辑放入模型中,而其他逻辑则不应该放在模型中。

贫血域基本上是模型对象,根本不包含任何或非常少的(业务)逻辑,所有逻辑都发生在服务类中。

在Rich域中,您基本上将与特定模型绑定的业务逻辑放入模型中,并使服务层保持较薄。

您的要求中的业务逻辑不应该进入某个模型。原因是,你需要的不仅仅是" Person"和#34; Party"数据应用规则。您还需要Resevation模型,您需要根据一种持久性存储(SQL,本地数据库,Web服务,xml等)进行验证,因此您需要访问存储库来执行此验证。

并且您不应该在模型中访问持久层表单,因为这违反了单一责任原则(一个类应该只负责一件事)并使您的模型依赖于存储库。如果你使用依赖注入,那么实例化你的模型并将存储库注入其中将会有很多麻烦,而不会违反其他模式(即你的核心不应该依赖于依赖注入框架,因此很难交换你的IoC / DI容器稍后)。

<强> @AsyncRelayCommand : 即使您希望用户等到操作完成,也应该使用异步操作。如果您不使用,您的用户界面将锁定/冻结。这是非常糟糕的用户体验。

使用AsyncRelayCommand,您可以执行操作异步并禁用UI容器(灰显所有项目并阻止用户输入),同时不锁定整个应用程序。这也将阻止您的应用程序收到&#34;应用程序未响应&#34;信息。

Application not responding

答案 1 :(得分:0)

任何业务逻辑都应该出现在ViewModel中。因为根据MVVM模式,ViewModel应该与View和Model隔离开来,这样可以很容易地测试这个业务逻辑。

模型通常是轻量级数据类或DTO,甚至可以自动生成(例如,如果您首先使用Entity Framework数据库)。

总而言之,您的RelayCommand在ViewModel中调用一个方法,您创建Person并进行业务逻辑检查,如果一切正常,则保存。如果没有,则将错误发送回UI(例如,通过设置一些错误文本)。