在MVVM中封装模型
本文关键字:模型 封装 MVVM | 更新日期: 2023-09-27 18:21:09
这个问题最初是在代码评审中开始的,所以为了避免重复,我不会把它全部粘贴在这里。因此,在我收到第一个问题的答案后,我还有另一个问题:
我应该如何包装我的business object
(用作Entity Framework Code First model
),以便我可以包括一些仅与特定模型相关的属性,以及我应该如何从ViewModel
中公开它?如何将更改保存回原始model
?
编辑:
仍在与此作斗争,因此我添加了一个可复制的小示例:
//class used as an EF code first model
public class Person
{
public int PersonId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual ObservableCollection<Order> Orders { get; set; }
}
Person
在一个名为PersonManagerWindow
的窗口中进行管理
//view model for the PersonManagerWindow
public class PersonManagerViewModel : ObservableObject
{
private string _personName;
public string PersonName
{
get { return _personName; }
set
{
_personName = value;
SelectedPerson = null;
RaisePropertyChanged();
}
}
private Person _selectedPerson;
public Person SelectedPerson
{
get { return _selectedPerson; }
set
{
_selectedPerson = value;
RaisePropertyChanged();
}
}
}
我的问题是:
SelectedPerson
应该公开"真实"的Person对象吗为它创建视图模型- 如果
ViewModel
是首选,我应该如何使用它Entity-Framework
访问
我认为您不能更改的原始型号
在我的一个项目中,我用T4模板增强了原始模型。类(由生成器)进行修改,但不修改源文件。
例如,这是原始代码:
public partial class ImprimanteSNData
{
[T4Order(2)]
private Boolean isConnected;
[T4Order(0)]
private List<String> printer;
[T4Order(1)]
private String serialNumber;
}
到T4时,该级别增强了:
生成的代码:
[DataContract]
public partial class ImprimanteSNData : IExtensibleDataObject
{
private ExtensionDataObject extensionDataObjectValue;
public ExtensionDataObject ExtensionData
{
get { return this.extensionDataObjectValue; }
set { this.extensionDataObjectValue = value; }
}
[DataMember(Order = 2)]
public System.Boolean IsConnected
{
get
{
return this.isConnected;
}
set
{
this.isConnected = value;
}
}
[DataMember(Order = 0)]
public System.Collections.Generic.List<System.String> Printer
{
get
{
return this.printer ?? new System.Collections.Generic.List<System.String>();
}
set
{
this.printer = value;
}
}
[DataMember(Order = 1)]
public System.String SerialNumber
{
get
{
return this.serialNumber == null ? String.Empty : this.serialNumber.Trim();
}
set
{
this.serialNumber = value;
}
}
}
所有这些第二部分都是从第一部分完全生成的。因此,编写新类和修改getter函数非常简单。
T4代码:
<#@ template language="C#" debug="true" hostSpecific="true" #>
<#@ output extension=".cs" #>
<#@ Assembly Name="System.Core.dll" #>
<#@ Assembly Name="System.Xml.dll" #>
<#@ Assembly Name="System.Xml.Linq.dll" #>
<#@ Assembly Name="System.Windows.Forms.dll" #>
<#@ Assembly name="EnvDTE" #>
<#@ import namespace="EnvDTE" #>
<#@ import namespace="System" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Diagnostics" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Xml.Linq" #>
<#@ import namespace="System.Collections" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Reflection" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Runtime.Serialization" #>
<#@ import namespace="XXXXXXXXXXX" #>
<#@ import namespace="System.Runtime.Serialization" #>
<#@ Assembly Name="$(ProjectDir)'bin'$(ConfigurationName)'FicheSignaletiqueViseoData.dll" #>
// T4Class : Génération des accesseurs de toutes les classes de FicheSignaletiqueViseoData
// pour les types non génériques et les classes, on rajoute une protection contre le null
// pour les String, on rajoute une protection contre le null et un Trim automatique
// Date de génération : <#= System.DateTime.Now.ToString() #>
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
<#
const String BusinessEntityNamespace= "XXXXXXXXXXX";
WriteLine("namespace {0}", BusinessEntityNamespace);
WriteLine("{");
PushIndent("'t");
String T4TemplatePath = Path.GetDirectoryName(Host.TemplateFile);
String dataContractSource = Path.Combine(T4TemplatePath, "DataContract");
String[] sources = Directory.GetFiles(dataContractSource, "*.cs");
IServiceProvider hostServiceProvider = (IServiceProvider)Host;
EnvDTE.DTE dte = (EnvDTE.DTE)hostServiceProvider.GetService(typeof(EnvDTE.DTE));
foreach(string file in sources)
{
EnvDTE.ProjectItem projectItem = dte.Solution.FindProjectItem(file);
FileCodeModel fileCodeModel = projectItem.FileCodeModel;
if (fileCodeModel != null)
{
foreach (CodeElement codeElement in fileCodeModel.CodeElements)
{
if (codeElement is CodeNamespace)
{
CodeNamespace nsp = codeElement as CodeNamespace;
foreach (CodeElement subElement in nsp.Children)
{
if (subElement is CodeClass)
{
CodeClass classe = subElement as CodeClass;
if (classe.Access == vsCMAccess.vsCMAccessPublic && classe.Name.StartsWith("T4") == false && classe.Name != "Important")
{
GenerateClassFromCode(classe);
}
}
}
}
}
}
}
PopIndent();
WriteLine("}");
#>
<#+
private void GenerateClassFromCode(CodeClass classToGenerate)
{
WriteLine("[DataContract]");
WriteLine("public partial class {0} : IExtensibleDataObject", classToGenerate.Name);
WriteLine("{");
PushIndent("'t");
WriteLine("private ExtensionDataObject extensionDataObjectValue;");
WriteLine(String.Empty);
WriteLine("public ExtensionDataObject ExtensionData");
WriteLine("{");
PushIndent("'t");
WriteLine("get { return this.extensionDataObjectValue; }");
WriteLine("set { this.extensionDataObjectValue = value; }");
PopIndent();
WriteLine("}");
WriteLine(String.Empty);
List<Tuple<string, Int16>> checkOrder = new List<Tuple<string, Int16>>();
List<CodeVariable> listVariable = new List<CodeVariable>();
List<CodeEnum> listEnum = new List<CodeEnum>();
foreach (CodeElement elem in classToGenerate.Members)
{
if (elem is CodeVariable)
{
listVariable.Add(elem as CodeVariable);
}
if (elem is CodeEnum)
{
listEnum.Add(elem as CodeEnum);
}
}
foreach(CodeVariable variable in listVariable)
{
if (variable.Access == vsCMAccess.vsCMAccessPrivate)
{
// attributs
foreach (CodeAttribute attribut in variable.Attributes)
{
Int16 order = getPropertyOrderFromCode(attribut);
if (order >= 0)
{
if (checkOrder.Where(c => c.Item2 == order).Count() > 0)
{
WriteLine("// Ci dessous, erreur de compilation voulue. Veuillez corriger et recompiler.");
WriteLine("ERROR : Dans la classe " + classToGenerate.Name + ", doublon sur le T4Order " + order + " (utilisé par " + checkOrder.Where(c => c.Item2 == order).First().Item1 + ")");
}
else
{
WriteLine("[DataMember(Order = {0})]", order);
checkOrder.Add(new Tuple<string, Int16>(variable.Name, order));
}
}
else
{
Write("[" + attribut.FullName);
if(attribut.Children != null && attribut.Children.Count > 0)
{
TextPoint start = attribut.Children.Cast<CodeElement>().First().GetStartPoint();
TextPoint finish = attribut.GetEndPoint();
String allArguments = start.CreateEditPoint().GetText(finish);
Write("(" + allArguments);
}
WriteLine("]");
}
}
// variable
string propertyTypeStr = variable.Type.AsFullName;
Type type = Type.GetType(propertyTypeStr);
WriteLine("public {0} {1}", propertyTypeStr, MakeUpper(variable.Name));
WriteLine("{");
PushIndent("'t");
// getter
WriteLine("get ");
WriteLine("{");
PushIndent("'t");
if (propertyTypeStr == "System.String")
{
WriteLine("return this.{0} == null ? String.Empty : this.{0}.Trim();", variable.Name, propertyTypeStr);
}
else if (listEnum.Select(e => e.FullName).Contains(propertyTypeStr))
{
WriteLine("return this.{0};", variable.Name);
}
else if (type == null || ((type.IsGenericType || type.IsClass)))
{
WriteLine("return this.{0} ?? new {1}();", variable.Name, propertyTypeStr);
}
else
{
WriteLine("return this.{0};", variable.Name);
}
PopIndent();
WriteLine("}");
WriteLine(String.Empty);
// setter
WriteLine("set ");
WriteLine("{");
PushIndent("'t");
WriteLine("this.{0} = value;", variable.Name);
PopIndent();
WriteLine("}");
PopIndent();
WriteLine("}");
WriteLine(String.Empty);
}
}
PopIndent();
WriteLine("}");
WriteLine(String.Empty);
}
private Int16 getPropertyOrderFromCode(CodeAttribute member)
{
if (member != null)
{
if (member.FullName == typeof(T4Order).ToString())
{
return Convert.ToInt16(member.Value);
}
}
return -1;
}
private String MakeUpper(String name)
{
return name.Substring(0, 1).ToUpper() + name.Substring(1, name.Length-1);
}
#>
我还使用了一个属性(棘手的是,我可以标记原始代码中的私有属性,以使生成的代码中的公共属性上的DataOrder)
public class T4Order : Attribute
{
private Int16 order;
public T4Order(Int16 o)
{
this.order = o;
}
public Int16 Order
{
get { return this.order; }
set { this.order = value; }
}
}
这只是一个例子,但它表明,在不修改原始代码的情况下,可以用T4做很多事情。