我是从实体框架模型创建模型还是编辑实体框架模型并在MVVM中使用它们?

时间:2019-02-24 20:51:23

标签: wpf entity-framework mvvm

我一直在寻找一个直接的答案,但似乎找不到。

我试图学习WPF中的MVVM模式,一直想知道我的模型是实体框架创建的东西,还是应该创建基本上是实体框架模型的副本的模型。

例如,我最基本的tblMyEmployees的实体框架模型如下

    public partial class tblMyEmployee
{
    public int pkEmployee { get; set; }
    public string strFirstName { get; set; }
    public string strLastName { get; set; }
}

所以我应该创建一个基本上从实体框架生成的模型中复制并粘贴属性的模型,并使用BaseViewModel来实现INotifyPropertyChanged,以及其他我认为有用的其他属性,例如

class EmployeeModel: BaseViewModel
{
    public int pkEmployee { get; set; }
    private string _strFirstName;
    public string strFirstName
    {
        get { return _strFirstName; }
        set
        {
            _strFirstName = value;
            SetProperty(ref _strFirstName, value);
        }
    }
    private string _strLastName;
    public string strLastName
    {
        get { return _strLastName; }
        set
        {
            _strLastName = value;
            SetProperty(ref _strLastName, value);
        }
    }
    private string _strFullName;
    public string strFullName
    {
        get { return strFirstName + " " + strLastName; }
    }
}

还是应该添加我的其他属性,然后将INotifyPropertyChanged实现到生成的模型中?

这似乎是一个非常愚蠢的问题,但这是我很长时间以来一直想得到的明确答案。

生成的模型顶部的注释使我想知道是否应该基于该模型创建自己的模型

//此代码是从模板生成的。

//对此文件进行手动更改可能会导致应用程序出现意外行为。

//如果重新生成代码,则对该文件的手动更改将被覆盖。

2 个答案:

答案 0 :(得分:1)

如马克·费尔德曼(Mark Feldman)在此答案https://stackoverflow.com/a/54856785/249665中所提到的,有几种方法可以做到这一点。

在大多数情况下,视图模型和域实体(模型)不同;它们只会在一个非常简单的CRUD应用程序中完全相同。

例如,视图模型可以包含模型中属性的子集,也可以包含多个模型中的属性。

除此之外,视图模型还具有模型中不存在的属性。例如,也许只允许某些用户查看或编辑特定属性;例如,然后,视图模型将具有指示这些字段为隐藏或只读的属性。

此外,viewmodel可能具有诸如组合框列表之类的内容,并且这些值可能来自其他模型(值对象)。

这是我目前的方法(不幸的是,使用NHibernate,我对EF的经验很少,但是我想这个水平没有什么区别。

  1. 域实体不应实现INPC
  2. 在视图模型中避免太多逻辑
  3. 用于将视图模型加载到单独的类的要素输出代码。
  4. 要素更新代码,用于执行对单独类的更新。
  5. 使用FluentValidation验证3和4的数据。

对于上述3和4,请查看https://jimmybogard.com/vertical-slice-architecture/

您可能会遇到类似以下的类: *员工模型 *创建新的员工ViewModel *编辑薪水ViewModel * GetNewEmployee查询(此查询将获取创建新员工所需的所有数据,例如部门列表,职位等) * SaveNewEmployee命令(保留新员工) * GetEmployee Salary Query(这可能检查用户是否被允许查看但不更改薪水,也许它获取最低和最高薪水,薪水类型等) * UpdateSalary命令 *验证新员工 *确认薪金

可能您会发现所有这些类都有一些共同的逻辑。有几种方法可以解决此问题。通常,可以将逻辑移至模型中的方法。在其他情况下,您需要添加单独的服务类来实现这种通用逻辑。

答案 1 :(得分:0)

这是一个无数次被问到的问题,尽管要找到适合您的答案可能很棘手,因为有多种解决方法。选项包括:

  1. 为每个模型类创建一个相应的视图模型。
  2. 在编译时使用PropertyChange.Fody为您添加INPC。
  3. 在运行时使用类似Castle DynamicProxy的方式注入INPC(尽管有一些重要的问题需要考虑,但是您的ORM需要提供允许您代理它创建的模型对象,尤其是任何集合的功能)
  4. 手动执行INPC。很有可能模型的更改总是来自视图,在这种情况下,实际上不需要INPC。在这种情况下,唯一需要以其他方式传播的更改是集合的添加和删除,在这种情况下,您将它们保留为常规列表(例如),并在更改列表时手动调用INPC更改事件。 (这是一个难看的解决方案,可能存在性能问题,但这是有可能的。)

我用自己的哪一个取决于项目。我曾经使用#3,但是它与ORM紧密相连,因此不再是我的首选。对于Xamarin工作,我倾向于使用#2,但是对于大多数WPF东西,我倾向于使用视图模型来管理集合,但是在可能的情况下直接绑定到模型属性。首先,具有将视图的多个部分绑定到同一个视图模型的功能似乎很吸引人,直到您意识到它创建了一个更加混乱的界面并干扰了用户可能期望的任何类型的撤消/取消功能。 / p>

更新:如果确实采用了显式声明所有视图模型属性的方法,那么您会发现自己一遍又一遍地编写相同的代码。通过将此功能放入代码段中,我节省了很多时间:

<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets  xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
    <CodeSnippet Format="1.0.0">
        <Header>
            <Title>propc</Title>
            <Shortcut>propc</Shortcut>
            <Description>Code snippet for an automatically implemented change notification property</Description>
            <Author>Mark Feldman</Author>
            <SnippetTypes>
                <SnippetType>Expansion</SnippetType>
            </SnippetTypes>
        </Header>
        <Snippet>
            <Declarations>
                <Literal>
                    <ID>type</ID>
                    <ToolTip>Property type</ToolTip>
                    <Default>int</Default>
                </Literal>
                <Literal>
                    <ID>property</ID>
                    <ToolTip>Property name</ToolTip>
                    <Default>MyProperty</Default>
                </Literal>
            </Declarations>
            <Code Language="csharp"><![CDATA[private $type$ _$property$;
    public $type$ $property$
    {
        get { return this._$property$;}
        set
        {
            if (this._$property$ != value)
            {
                this._$property$ = value;
                RaisePropertyChanged(() => this.$property$);
            }
        }
    }
    $end$]]>
            </Code>
        </Snippet>
    </CodeSnippet>
</CodeSnippets>

通过“工具”->“代码片段管理器”导入该代码,然后在类中的任意位置键入“ propc”,后跟2个制表符,这将为带有后端字段的INPC属性生成大多数样板代码,并让您替换类型和名称:

private string _MyText;
public string MyText
{
    get { return this._MyText; }
    set
    {
        if (this._MyText != value)
        {
            this._MyText = value;
            RaisePropertyChanged(() => this.MyText);
        }
    }
}