我正在使用Entity Framework 4.3,因此我使用DbContext Generator来创建上下文和实体类。
使用默认的EF 4代码生成器模板,实体类实现INotifyPropertyChanged,并在属性设置器中添加Changing
和Changed
部分方法。
当我使用EF 4.x DbContext生成器时,如下图所示,实体类更轻,并且不包括任何跟踪属性更改的方法。
以下是一个例子:
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated from a template.
//
// Manual changes to this file may cause unexpected behavior in your application.
// Manual changes to this file will be overwritten if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
namespace SomeNamespace
{
public partial class SomeTable
{
public SomeTable()
{
this.Children = new HashSet<Child>();
}
public long parent_id { get; set; }
public long id { get; set; }
public string filename { get; set; }
public byte[] file_blob { get; set; }
public virtual Parent Parent { get; set; }
public virtual ICollection<Child> Children { get; set; }
}
}
我必须错过一个重要的难题,但我的搜索没有结果。所以我的问题是:如何使用EF 4.3生成包含属性更改通知的类型?
修改
我完全同意@derape的答案;但我很好奇为什么当EF 4默认代码生成模板已经有钩子时,我需要更改.tt
文件。我的意思是什么时候绑定到WPF DependencyProperty
'?如果没有INotifyPropertyChanged,命令对一堆对象中的一组属性所做的更改将不会反映在UI中。我错过了什么?
答案 0 :(得分:23)
我最近偶然发现了这个问题,我编辑了我的Entity.tt来实现以下更改,快速补丁但效果很好..
将以下内容添加到CodeStringGenerator类
public string EntityClassOpening(EntityType entity)
{
return string.Format(
CultureInfo.InvariantCulture,
"{0} {1}partial class {2}{3} : {4}",
Accessibility.ForType(entity),
_code.SpaceAfter(_code.AbstractOption(entity)),
_code.Escape(entity),
_code.StringBefore(" : ", _typeMapper.GetTypeName(entity.BaseType)),
"INotifyPropertyChanged");
}
public string Property(EdmProperty edmProperty)
{
return string.Format(
CultureInfo.InvariantCulture,
"{0} {1} {2} {{ {3}{6} {4}{5} }}",
Accessibility.ForProperty(edmProperty),
_typeMapper.GetTypeName(edmProperty.TypeUsage),
_code.Escape(edmProperty),
_code.SpaceAfter(Accessibility.ForGetter(edmProperty)),
_code.SpaceAfter(Accessibility.ForSetter(edmProperty)),
"set { _"+_code.Escape(edmProperty).ToLower()+" = value; OnPropertyChanged(\""+_code.Escape(edmProperty)+"\");}",
"get { return _"+_code.Escape(edmProperty).ToLower()+"; }");
}
public string Private(EdmProperty edmProperty) {
return string.Format(
CultureInfo.InvariantCulture,
"{0} {1} _{2};",
"private",
_typeMapper.GetTypeName(edmProperty.TypeUsage),
_code.Escape(edmProperty).ToLower());
}
将以下内容添加到生成器
using System.ComponentModel;
<#=codeStringGenerator.EntityClassOpening(entity)#>
{
<#
var propertiesWithDefaultValues = typeMapper.GetPropertiesWithDefaultValues(entity);
var collectionNavigationProperties = typeMapper.GetCollectionNavigationProperties(entity);
var complexProperties = typeMapper.GetComplexProperties(entity);
#>
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
进一步下降
foreach (var edmProperty in simpleProperties)
{
#>
<#=codeStringGenerator.Private(edmProperty)#>
<#=codeStringGenerator.Property(edmProperty)#>
<#
}
foreach(var complexProperty in complexProperties)
{
#>
<#=codeStringGenerator.Private(complexProperty)#>
<#=codeStringGenerator.Property(complexProperty)#>
<#
}
答案 1 :(得分:14)
我在Anders答案中创建了一个变体,但有以下不同之处:
所以我的步骤是:
为要扩展的模型类创建基类:
public abstract class BaseModel : INotifyPropertyChanged
{
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null)
{
if (object.Equals(storage, value)) return false;
storage = value;
this.OnPropertyChanged(propertyName);
return true;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
感谢Juan Pable Gomez的建议改进!我喜欢你的改进,即使其他评论者没有:)
将Entity.tt文件中的EntityClassOpening方法更新为以下内容:
public string EntityClassOpening(EntityType entity)
{
return string.Format(
CultureInfo.InvariantCulture,
"{0} {1}partial class {2}{3}",
Accessibility.ForType(entity),
_code.SpaceAfter(_code.AbstractOption(entity)),
_code.Escape(entity),
_code.StringBefore(" : ", string.IsNullOrEmpty(_typeMapper.GetTypeName(entity.BaseType)) ? "BaseModel" : _typeMapper.GetTypeName(entity.BaseType)));
}
找到一行:
<#=Accessibility.ForType(complex)#> partial class <#=code.Escape(complex)#>
并将其更新为:
<#=Accessibility.ForType(complex)#> partial class <#=code.Escape(complex)#> : BaseModel
感谢Manolo!
将Entity.tt文件中的Property方法更新为以下内容:
public string Property(EdmProperty edmProperty)
{
return string.Format(
CultureInfo.InvariantCulture,
"private {1} {3};\r\n"+
"\t{0} {1} {2} \r\n" +
"\t{{ \r\n" +
"\t\t{4}get {{ return {3}; }} \r\n" +
"\t\t{5}set {{ SetProperty(ref {3}, value); }} \r\n" +
"\t}}\r\n",
Accessibility.ForProperty(edmProperty),
_typeMapper.GetTypeName(edmProperty.TypeUsage),
_code.Escape(edmProperty),
"_" + Char.ToLowerInvariant(_code.Escape(edmProperty)[0]) + _code.Escape(edmProperty).Substring(1),
_code.SpaceAfter(Accessibility.ForGetter(edmProperty)),
_code.SpaceAfter(Accessibility.ForSetter(edmProperty)));
}
你已经完成了!现在您的模型类将如下所示:
public partial class User : BaseModel
{
private int _id;
public int Id
{
get { return _id; }
set { SetProperty(ref _id,value);}
}
private string _name;
public string Name
{
get { return _name; }
set { SetProperty(ref _name , value); }
}
如果您能看到其他改进,请随时(尝试)编辑此解决方案。
答案 2 :(得分:5)
我试图编辑 Brian Hinchey解决方案但是EDIT被拒绝了。然后我在这里发布我的addapts。
此解决方案为每个属性Setter生成较少的代码,利用 CallerMemberName属性。
注意:我正在使用EF 6.1,它运行良好。
BaseClass现在看起来像这样。
public abstract class BaseModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null)
{
if (object.Equals(storage, value)) return false;
storage = value;
this.OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var eventHandler = this.PropertyChanged;
if (eventHandler != null)
{
eventHandler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
更新Entity.tt中的EntityClassOpening方法完全和Brian一样:
public string EntityClassOpening(EntityType entity)
{
return string.Format(
CultureInfo.InvariantCulture,
"{0} {1}partial class {2}{3}",
Accessibility.ForType(entity),
_code.SpaceAfter(_code.AbstractOption(entity)),
_code.Escape(entity),
_code.StringBefore(" : ", string.IsNullOrEmpty(_typeMapper.GetTypeName(entity.BaseType)) ? "BaseModel" : _typeMapper.GetTypeName(entity.BaseType)));
}
正如布莱恩所说,记得改变:
<#=Accessibility.ForType(complex)#> partial class <#=code.Escape(complex)#>
FOR
<#=Accessibility.ForType(complex)#> partial class <#=code.Escape(complex)#> : BaseModel
我的最后一项更改是属性方法
public string Property(EdmProperty edmProperty)
{
return string.Format(
CultureInfo.InvariantCulture,
"private {1} {3};\r\n"+
"\t{0} {1} {2} \r\n" +
"\t{{ \r\n" +
"\t\t{4}get {{ return {3}; }} \r\n" +
"\t\t{5}set {{ SetProperty(ref {3}, value); }} \r\n" +
"\t}}\r\n",
Accessibility.ForProperty(edmProperty),
_typeMapper.GetTypeName(edmProperty.TypeUsage),
_code.Escape(edmProperty),
"_" + Char.ToLowerInvariant(_code.Escape(edmProperty)[0]) + _code.Escape(edmProperty).Substring(1),
_code.SpaceAfter(Accessibility.ForGetter(edmProperty)),
_code.SpaceAfter(Accessibility.ForSetter(edmProperty)));
}
最后这个课程看起来像是:
public partial class User : BaseModel
{
private int _id;
public int Id
{
get { return _id; }
set { SetProperty(ref _id , value);}
}
private string _name;
public string Name
{
get { return _name; }
set { SetProperty(ref _name , value);}
}
}
这使得生成的clases更轻。
最近我正在使用 PropertyChanged.Fody 库,但原因不明(至少对我而言)它有时无法正常工作。这就是我降落在这里的原因。这个解决方案(布莱恩的解决方案)每次都有效。
答案 3 :(得分:1)
以上Anders的解决方案有效,但我在此过程中发现了一些问题:
在步骤1中,它说“将以下内容添加到CodeStringGenerator类”,只能添加“public string Private(...”函数,因为其他两个已经存在。所以,你需要找到它们并替换它们这两个函数不会添加它们,否则你会得到错误。要找到你需要放置它们的确切位置,搜索“public class CodeStringGenerator”,并查找下面的函数。
在步骤2“将以下内容添加到生成器”中,您只需要添加“using System.ComponentModel”行,以及来自(包括)“public event PropertyChangedEventHandler ...”的行。同样,其他行已经存在,你会发现它们位于.tt文件的顶部附近。
在第3步“又向下”,这两个“foreach”循环也已经存在,因此必须更换它们而不是添加它们。最终每个foreach循环只添加一行,“&lt;#= codeStringGenerator.Private(edmProperty)#&gt;”和“&lt;#= codeStringGenerator.Private(complexProperty)#&gt;”分别在每个循环中。
另外,不要更换错误的循环,在需要替换的循环之上还有两个额外的循环,它们循环通过相同的对象...确保替换正确的循环: - )
我想我会提到这个,因为作为MVVM / EF新手(曾经使用过NHibernate)我不得不对它进行调整才能正常工作。
答案 4 :(得分:0)
这取决于你想要做什么。如果您只想实现自定义属性/方法,则可以使用部分类的功能。 如果你想在实体设计器中更改属性的setter / getter,则必须调整dbContext生成器模板文件。这是一个T4模板。
答案 5 :(得分:0)
我创建了以下内容用于EF 6.1.2,但测试相当有限,因此使用风险自负。
<#@ template language="C#" debug="false" hostspecific="true"#>
<#@ include file="EF6.Utility.CS.ttinclude"#><#@
output extension=".cs"#><#
const string inputFile = @"Model.edmx";
var textTransform = DynamicTextTransformation.Create(this);
var code = new CodeGenerationTools(this);
var ef = new MetadataTools(this);
var typeMapper = new TypeMapper(code, ef, textTransform.Errors);
var fileManager = EntityFrameworkTemplateFileManager.Create(this);
var itemCollection = new EdmMetadataLoader(textTransform.Host, textTransform.Errors).CreateEdmItemCollection(inputFile);
var codeStringGenerator = new CodeStringGenerator(code, typeMapper, ef);
if (!typeMapper.VerifyCaseInsensitiveTypeUniqueness(typeMapper.GetAllGlobalItems(itemCollection), inputFile))
{
return string.Empty;
}
WriteHeader(codeStringGenerator, fileManager);
foreach (var entity in typeMapper.GetItemsToGenerate<EntityType>(itemCollection))
{
fileManager.StartNewFile(entity.Name + ".cs");
BeginNamespace(code);
#>
<#=codeStringGenerator.UsingDirectives(inHeader: false)#>
<#=codeStringGenerator.EntityClassOpening(entity)#>
{
<#
var propertiesWithDefaultValues = typeMapper.GetPropertiesWithDefaultValues(entity);
var collectionNavigationProperties = typeMapper.GetCollectionNavigationProperties(entity);
var complexProperties = typeMapper.GetComplexProperties(entity);
if (propertiesWithDefaultValues.Any() || collectionNavigationProperties.Any() || complexProperties.Any())
{
#>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public <#=code.Escape(entity)#>()
{
<#
foreach (var edmProperty in propertiesWithDefaultValues)
{
#>
this.<#=code.Escape(edmProperty)#> = <#=typeMapper.CreateLiteral(edmProperty.DefaultValue)#>;
<#
}
foreach (var navigationProperty in collectionNavigationProperties)
{
#>
this.<#=code.Escape(navigationProperty)#> = new HashSet<<#=typeMapper.GetTypeName(navigationProperty.ToEndMember.GetEntityType())#>>();
<#
}
foreach (var complexProperty in complexProperties)
{
#>
this.<#=code.Escape(complexProperty)#> = new <#=typeMapper.GetTypeName(complexProperty.TypeUsage)#>();
<#
}
#>
}
<#
}
var simpleProperties = typeMapper.GetSimpleProperties(entity);
if (simpleProperties.Any())
{
foreach (var edmProperty in simpleProperties)
{
#>
<#=codeStringGenerator.Property(edmProperty)#>
<#
}
}
if (complexProperties.Any())
{
#>
<#
foreach(var complexProperty in complexProperties)
{
#>
<#=codeStringGenerator.Property(complexProperty)#>
<#
}
}
var navigationProperties = typeMapper.GetNavigationProperties(entity);
if (navigationProperties.Any())
{
#>
<#
foreach (var navigationProperty in navigationProperties)
{
if (navigationProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many)
{
#>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
<#
}
#>
<#=codeStringGenerator.NavigationProperty(navigationProperty)#>
<#
}
}
#>
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
WhenPropertyChanged(e);
if (PropertyChanged != null)
{
PropertyChanged(this, e);
}
}
partial void WhenPropertyChanged(PropertyChangedEventArgs e);
#endregion
}
<#
EndNamespace(code);
}
foreach (var complex in typeMapper.GetItemsToGenerate<ComplexType>(itemCollection))
{
fileManager.StartNewFile(complex.Name + ".cs");
BeginNamespace(code);
#>
<#=codeStringGenerator.UsingDirectives(inHeader: false, includeCollections: false)#>
<#=Accessibility.ForType(complex)#> partial class <#=code.Escape(complex)#>
{
<#
var complexProperties = typeMapper.GetComplexProperties(complex);
var propertiesWithDefaultValues = typeMapper.GetPropertiesWithDefaultValues(complex);
if (propertiesWithDefaultValues.Any() || complexProperties.Any())
{
#>
public <#=code.Escape(complex)#>()
{
<#
foreach (var edmProperty in propertiesWithDefaultValues)
{
#>
this.<#=code.Escape(edmProperty)#> = <#=typeMapper.CreateLiteral(edmProperty.DefaultValue)#>;
<#
}
foreach (var complexProperty in complexProperties)
{
#>
this.<#=code.Escape(complexProperty)#> = new <#=typeMapper.GetTypeName(complexProperty.TypeUsage)#>();
<#
}
#>
}
<#
}
var simpleProperties = typeMapper.GetSimpleProperties(complex);
if (simpleProperties.Any())
{
foreach(var edmProperty in simpleProperties)
{
#>
<#=codeStringGenerator.Property(edmProperty)#>
<#
}
}
if (complexProperties.Any())
{
#>
<#
foreach(var edmProperty in complexProperties)
{
#>
<#=codeStringGenerator.Property(edmProperty)#>
<#
}
}
#>
}
<#
EndNamespace(code);
}
foreach (var enumType in typeMapper.GetEnumItemsToGenerate(itemCollection))
{
fileManager.StartNewFile(enumType.Name + ".cs");
BeginNamespace(code);
#>
<#=codeStringGenerator.UsingDirectives(inHeader: false, includeCollections: false)#>
<#
if (typeMapper.EnumIsFlags(enumType))
{
#>
[Flags]
<#
}
#>
<#=codeStringGenerator.EnumOpening(enumType)#>
{
<#
var foundOne = false;
foreach (MetadataItem member in typeMapper.GetEnumMembers(enumType))
{
foundOne = true;
#>
<#=code.Escape(typeMapper.GetEnumMemberName(member))#> = <#=typeMapper.GetEnumMemberValue(member)#>,
<#
}
if (foundOne)
{
this.GenerationEnvironment.Remove(this.GenerationEnvironment.Length - 3, 1);
}
#>
}
<#
EndNamespace(code);
}
fileManager.Process();
#>
<#+
public void WriteHeader(CodeStringGenerator codeStringGenerator, EntityFrameworkTemplateFileManager fileManager)
{
fileManager.StartHeader();
#>
//------------------------------------------------------------------------------
// <auto-generated>
// <#=CodeGenerationTools.GetResourceString("Template_GeneratedCodeCommentLine1")#>
//
// <#=CodeGenerationTools.GetResourceString("Template_GeneratedCodeCommentLine2")#>
// <#=CodeGenerationTools.GetResourceString("Template_GeneratedCodeCommentLine3")#>
// </auto-generated>
//------------------------------------------------------------------------------
<#=codeStringGenerator.UsingDirectives(inHeader: true)#>
<#+
fileManager.EndBlock();
}
public void BeginNamespace(CodeGenerationTools code)
{
var codeNamespace = code.VsNamespaceSuggestion();
if (!String.IsNullOrEmpty(codeNamespace))
{
#>
namespace <#=code.EscapeNamespace(codeNamespace)#>
{
<#+
PushIndent(" ");
}
}
public void EndNamespace(CodeGenerationTools code)
{
if (!String.IsNullOrEmpty(code.VsNamespaceSuggestion()))
{
PopIndent();
#>
}
<#+
}
}
public const string TemplateId = "CSharp_DbContext_Types_EF6";
public class CodeStringGenerator
{
private readonly CodeGenerationTools _code;
private readonly TypeMapper _typeMapper;
private readonly MetadataTools _ef;
public CodeStringGenerator(CodeGenerationTools code, TypeMapper typeMapper, MetadataTools ef)
{
ArgumentNotNull(code, "code");
ArgumentNotNull(typeMapper, "typeMapper");
ArgumentNotNull(ef, "ef");
_code = code;
_typeMapper = typeMapper;
_ef = ef;
}
public string Property(EdmProperty edmProperty)
{
StringBuilder propertyCode = new StringBuilder();
propertyCode.AppendFormat("private {0} _{1};",_typeMapper.GetTypeName(edmProperty.TypeUsage), _code.Escape(edmProperty));
propertyCode.AppendFormat(
CultureInfo.InvariantCulture,
"{0} {1} {2} {{ {3}get{{ return _{2};}} {4}set{{if(_{2} != value){{_{2} = value; OnPropertyChanged(\"{2}\");}}}}}}",
Accessibility.ForProperty(edmProperty),
_typeMapper.GetTypeName(edmProperty.TypeUsage),
_code.Escape(edmProperty),
_code.SpaceAfter(Accessibility.ForGetter(edmProperty)),
_code.SpaceAfter(Accessibility.ForSetter(edmProperty)));
return propertyCode.ToString();
}
public string NavigationProperty(NavigationProperty navProp)
{
var endType = _typeMapper.GetTypeName(navProp.ToEndMember.GetEntityType());
return string.Format(
CultureInfo.InvariantCulture,
"{0} {1} {2} {{ {3}get; {4}set; }}",
AccessibilityAndVirtual(Accessibility.ForNavigationProperty(navProp)),
navProp.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many ? ("ICollection<" + endType + ">") : endType,
_code.Escape(navProp),
_code.SpaceAfter(Accessibility.ForGetter(navProp)),
_code.SpaceAfter(Accessibility.ForSetter(navProp)));
}
public string AccessibilityAndVirtual(string accessibility)
{
return accessibility + (accessibility != "private" ? " virtual" : "");
}
public string EntityClassOpening(EntityType entity)
{
return string.Format(
CultureInfo.InvariantCulture,
"{0} {1}partial class {2} : INotifyPropertyChanged{3}",
Accessibility.ForType(entity),
_code.SpaceAfter(_code.AbstractOption(entity)),
_code.Escape(entity),
_code.StringBefore(", ", _typeMapper.GetTypeName(entity.BaseType)));
}
public string EnumOpening(SimpleType enumType)
{
return string.Format(
CultureInfo.InvariantCulture,
"{0} enum {1} : {2}",
Accessibility.ForType(enumType),
_code.Escape(enumType),
_code.Escape(_typeMapper.UnderlyingClrType(enumType)));
}
public void WriteFunctionParameters(EdmFunction edmFunction, Action<string, string, string, string> writeParameter)
{
var parameters = FunctionImportParameter.Create(edmFunction.Parameters, _code, _ef);
foreach (var parameter in parameters.Where(p => p.NeedsLocalVariable))
{
var isNotNull = parameter.IsNullableOfT ? parameter.FunctionParameterName + ".HasValue" : parameter.FunctionParameterName + " != null";
var notNullInit = "new ObjectParameter(\"" + parameter.EsqlParameterName + "\", " + parameter.FunctionParameterName + ")";
var nullInit = "new ObjectParameter(\"" + parameter.EsqlParameterName + "\", typeof(" + TypeMapper.FixNamespaces(parameter.RawClrTypeName) + "))";
writeParameter(parameter.LocalVariableName, isNotNull, notNullInit, nullInit);
}
}
public string ComposableFunctionMethod(EdmFunction edmFunction, string modelNamespace)
{
var parameters = _typeMapper.GetParameters(edmFunction);
return string.Format(
CultureInfo.InvariantCulture,
"{0} IQueryable<{1}> {2}({3})",
AccessibilityAndVirtual(Accessibility.ForMethod(edmFunction)),
_typeMapper.GetTypeName(_typeMapper.GetReturnType(edmFunction), modelNamespace),
_code.Escape(edmFunction),
string.Join(", ", parameters.Select(p => TypeMapper.FixNamespaces(p.FunctionParameterType) + " " + p.FunctionParameterName).ToArray()));
}
public string ComposableCreateQuery(EdmFunction edmFunction, string modelNamespace)
{
var parameters = _typeMapper.GetParameters(edmFunction);
return string.Format(
CultureInfo.InvariantCulture,
"return ((IObjectContextAdapter)this).ObjectContext.CreateQuery<{0}>(\"[{1}].[{2}]({3})\"{4});",
_typeMapper.GetTypeName(_typeMapper.GetReturnType(edmFunction), modelNamespace),
edmFunction.NamespaceName,
edmFunction.Name,
string.Join(", ", parameters.Select(p => "@" + p.EsqlParameterName).ToArray()),
_code.StringBefore(", ", string.Join(", ", parameters.Select(p => p.ExecuteParameterName).ToArray())));
}
public string FunctionMethod(EdmFunction edmFunction, string modelNamespace, bool includeMergeOption)
{
var parameters = _typeMapper.GetParameters(edmFunction);
var returnType = _typeMapper.GetReturnType(edmFunction);
var paramList = String.Join(", ", parameters.Select(p => TypeMapper.FixNamespaces(p.FunctionParameterType) + " " + p.FunctionParameterName).ToArray());
if (includeMergeOption)
{
paramList = _code.StringAfter(paramList, ", ") + "MergeOption mergeOption";
}
return string.Format(
CultureInfo.InvariantCulture,
"{0} {1} {2}({3})",
AccessibilityAndVirtual(Accessibility.ForMethod(edmFunction)),
returnType == null ? "int" : "ObjectResult<" + _typeMapper.GetTypeName(returnType, modelNamespace) + ">",
_code.Escape(edmFunction),
paramList);
}
public string ExecuteFunction(EdmFunction edmFunction, string modelNamespace, bool includeMergeOption)
{
var parameters = _typeMapper.GetParameters(edmFunction);
var returnType = _typeMapper.GetReturnType(edmFunction);
var callParams = _code.StringBefore(", ", String.Join(", ", parameters.Select(p => p.ExecuteParameterName).ToArray()));
if (includeMergeOption)
{
callParams = ", mergeOption" + callParams;
}
return string.Format(
CultureInfo.InvariantCulture,
"return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction{0}(\"{1}\"{2});",
returnType == null ? "" : "<" + _typeMapper.GetTypeName(returnType, modelNamespace) + ">",
edmFunction.Name,
callParams);
}
public string DbSet(EntitySet entitySet)
{
return string.Format(
CultureInfo.InvariantCulture,
"{0} virtual DbSet<{1}> {2} {{ get; set; }}",
Accessibility.ForReadOnlyProperty(entitySet),
_typeMapper.GetTypeName(entitySet.ElementType),
_code.Escape(entitySet));
}
public string UsingDirectives(bool inHeader, bool includeCollections = true)
{
return inHeader == string.IsNullOrEmpty(_code.VsNamespaceSuggestion())
? string.Format(
CultureInfo.InvariantCulture,
"{0}using System;" + Environment.NewLine +
"using System.ComponentModel;{1}" +
"{2}",
inHeader ? Environment.NewLine : "",
includeCollections ? (Environment.NewLine + "using System.Collections.Generic;") : "",
inHeader ? "" : Environment.NewLine)
: "";
}
}
public class TypeMapper
{
private const string ExternalTypeNameAttributeName = @"http://schemas.microsoft.com/ado/2006/04/codegeneration:ExternalTypeName";
private readonly System.Collections.IList _errors;
private readonly CodeGenerationTools _code;
private readonly MetadataTools _ef;
public TypeMapper(CodeGenerationTools code, MetadataTools ef, System.Collections.IList errors)
{
ArgumentNotNull(code, "code");
ArgumentNotNull(ef, "ef");
ArgumentNotNull(errors, "errors");
_code = code;
_ef = ef;
_errors = errors;
}
public static string FixNamespaces(string typeName)
{
return typeName.Replace("System.Data.Spatial.", "System.Data.Entity.Spatial.");
}
public string GetTypeName(TypeUsage typeUsage)
{
return typeUsage == null ? null : GetTypeName(typeUsage.EdmType, _ef.IsNullable(typeUsage), modelNamespace: null);
}
public string GetTypeName(EdmType edmType)
{
return GetTypeName(edmType, isNullable: null, modelNamespace: null);
}
public string GetTypeName(TypeUsage typeUsage, string modelNamespace)
{
return typeUsage == null ? null : GetTypeName(typeUsage.EdmType, _ef.IsNullable(typeUsage), modelNamespace);
}
public string GetTypeName(EdmType edmType, string modelNamespace)
{
return GetTypeName(edmType, isNullable: null, modelNamespace: modelNamespace);
}
public string GetTypeName(EdmType edmType, bool? isNullable, string modelNamespace)
{
if (edmType == null)
{
return null;
}
var collectionType = edmType as CollectionType;
if (collectionType != null)
{
return String.Format(CultureInfo.InvariantCulture, "ICollection<{0}>", GetTypeName(collectionType.TypeUsage, modelNamespace));
}
var typeName = _code.Escape(edmType.MetadataProperties
.Where(p => p.Name == ExternalTypeNameAttributeName)
.Select(p => (string)p.Value)
.FirstOrDefault())
?? (modelNamespace != null && edmType.NamespaceName != modelNamespace ?
_code.CreateFullName(_code.EscapeNamespace(edmType.NamespaceName), _code.Escape(edmType)) :
_code.Escape(edmType));
if (edmType is StructuralType)
{
return typeName;
}
if (edmType is SimpleType)
{
var clrType = UnderlyingClrType(edmType);
if (!IsEnumType(edmType))
{
typeName = _code.Escape(clrType);
}
typeName = FixNamespaces(typeName);
return clrType.IsValueType && isNullable == true ?
String.Format(CultureInfo.InvariantCulture, "Nullable<{0}>", typeName) :
typeName;
}
throw new ArgumentException("edmType");
}
public Type UnderlyingClrType(EdmType edmType)
{
ArgumentNotNull(edmType, "edmType");
var primitiveType = edmType as PrimitiveType;
if (primitiveType != null)
{
return primitiveType.ClrEquivalentType;
}
if (IsEnumType(edmType))
{
return GetEnumUnderlyingType(edmType).ClrEquivalentType;
}
return typeof(object);
}
public object GetEnumMemberValue(MetadataItem enumMember)
{
ArgumentNotNull(enumMember, "enumMember");
var valueProperty = enumMember.GetType().GetProperty("Value");
return valueProperty == null ? null : valueProperty.GetValue(enumMember, null);
}
public string GetEnumMemberName(MetadataItem enumMember)
{
ArgumentNotNull(enumMember, "enumMember");
var nameProperty = enumMember.GetType().GetProperty("Name");
return nameProperty == null ? null : (string)nameProperty.GetValue(enumMember, null);
}
public System.Collections.IEnumerable GetEnumMembers(EdmType enumType)
{
ArgumentNotNull(enumType, "enumType");
var membersProperty = enumType.GetType().GetProperty("Members");
return membersProperty != null
? (System.Collections.IEnumerable)membersProperty.GetValue(enumType, null)
: Enumerable.Empty<MetadataItem>();
}
public bool EnumIsFlags(EdmType enumType)
{
ArgumentNotNull(enumType, "enumType");
var isFlagsProperty = enumType.GetType().GetProperty("IsFlags");
return isFlagsProperty != null && (bool)isFlagsProperty.GetValue(enumType, null);
}
public bool IsEnumType(GlobalItem edmType)
{
ArgumentNotNull(edmType, "edmType");
return edmType.GetType().Name == "EnumType";
}
public PrimitiveType GetEnumUnderlyingType(EdmType enumType)
{
ArgumentNotNull(enumType, "enumType");
return (PrimitiveType)enumType.GetType().GetProperty("UnderlyingType").GetValue(enumType, null);
}
public string CreateLiteral(object value)
{
if (value == null || value.GetType() != typeof(TimeSpan))
{
return _code.CreateLiteral(value);
}
return string.Format(CultureInfo.InvariantCulture, "new TimeSpan({0})", ((TimeSpan)value).Ticks);
}
public bool VerifyCaseInsensitiveTypeUniqueness(IEnumerable<string> types, string sourceFile)
{
ArgumentNotNull(types, "types");
ArgumentNotNull(sourceFile, "sourceFile");
var hash = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase);
if (types.Any(item => !hash.Add(item)))
{
_errors.Add(
new CompilerError(sourceFile, -1, -1, "6023",
String.Format(CultureInfo.CurrentCulture, CodeGenerationTools.GetResourceString("Template_CaseInsensitiveTypeConflict"))));
return false;
}
return true;
}
public IEnumerable<SimpleType> GetEnumItemsToGenerate(IEnumerable<GlobalItem> itemCollection)
{
return GetItemsToGenerate<SimpleType>(itemCollection)
.Where(e => IsEnumType(e));
}
public IEnumerable<T> GetItemsToGenerate<T>(IEnumerable<GlobalItem> itemCollection) where T: EdmType
{
return itemCollection
.OfType<T>()
.Where(i => !i.MetadataProperties.Any(p => p.Name == ExternalTypeNameAttributeName))
.OrderBy(i => i.Name);
}
public IEnumerable<string> GetAllGlobalItems(IEnumerable<GlobalItem> itemCollection)
{
return itemCollection
.Where(i => i is EntityType || i is ComplexType || i is EntityContainer || IsEnumType(i))
.Select(g => GetGlobalItemName(g));
}
public string GetGlobalItemName(GlobalItem item)
{
if (item is EdmType)
{
return ((EdmType)item).Name;
}
else
{
return ((EntityContainer)item).Name;
}
}
public IEnumerable<EdmProperty> GetSimpleProperties(EntityType type)
{
return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type);
}
public IEnumerable<EdmProperty> GetSimpleProperties(ComplexType type)
{
return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type);
}
public IEnumerable<EdmProperty> GetComplexProperties(EntityType type)
{
return type.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == type);
}
public IEnumerable<EdmProperty> GetComplexProperties(ComplexType type)
{
return type.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == type);
}
public IEnumerable<EdmProperty> GetPropertiesWithDefaultValues(EntityType type)
{
return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type && p.DefaultValue != null);
}
public IEnumerable<EdmProperty> GetPropertiesWithDefaultValues(ComplexType type)
{
return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type && p.DefaultValue != null);
}
public IEnumerable<NavigationProperty> GetNavigationProperties(EntityType type)
{
return type.NavigationProperties.Where(np => np.DeclaringType == type);
}
public IEnumerable<NavigationProperty> GetCollectionNavigationProperties(EntityType type)
{
return type.NavigationProperties.Where(np => np.DeclaringType == type && np.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many);
}
public FunctionParameter GetReturnParameter(EdmFunction edmFunction)
{
ArgumentNotNull(edmFunction, "edmFunction");
var returnParamsProperty = edmFunction.GetType().GetProperty("ReturnParameters");
return returnParamsProperty == null
? edmFunction.ReturnParameter
: ((IEnumerable<FunctionParameter>)returnParamsProperty.GetValue(edmFunction, null)).FirstOrDefault();
}
public bool IsComposable(EdmFunction edmFunction)
{
ArgumentNotNull(edmFunction, "edmFunction");
var isComposableProperty = edmFunction.GetType().GetProperty("IsComposableAttribute");
return isComposableProperty != null && (bool)isComposableProperty.GetValue(edmFunction, null);
}
public IEnumerable<FunctionImportParameter> GetParameters(EdmFunction edmFunction)
{
return FunctionImportParameter.Create(edmFunction.Parameters, _code, _ef);
}
public TypeUsage GetReturnType(EdmFunction edmFunction)
{
var returnParam = GetReturnParameter(edmFunction);
return returnParam == null ? null : _ef.GetElementType(returnParam.TypeUsage);
}
public bool GenerateMergeOptionFunction(EdmFunction edmFunction, bool includeMergeOption)
{
var returnType = GetReturnType(edmFunction);
return !includeMergeOption && returnType != null && returnType.EdmType.BuiltInTypeKind == BuiltInTypeKind.EntityType;
}
}
public static void ArgumentNotNull<T>(T arg, string name) where T : class
{
if (arg == null)
{
throw new ArgumentNullException(name);
}
}
#>
答案 6 :(得分:0)
我使用Visual Basic并倾向于享受重构遗留和非分层体系结构。
所以,我已经使用上面的答案但在Entity Framework 6.1.3和.net 4.6.1中解决了如何做到这一点。
我已经从其他人给出的答案中翻译过来,所以我只能对今晚我已经完成的重新发现和翻译工作表示赞赏。
这个项目非常小,它是winforms,我想要数据绑定而不是一大堆手动更新控件。我不想通过添加更多的分离来增加更多的复杂性,因为这里没有足够的好处。这足以证明这一点:)。
我希望它有助于其他VB程序员。
基类:
Imports System.ComponentModel
Imports System.Runtime.CompilerServices
Public MustInherit Class BaseModel
Implements INotifyPropertyChanged
Protected Function SetProperty(Of T)(ByRef storage As T, value As T, <CallerMemberName> Optional propertyName As String = Nothing) As Boolean
If Equals(storage, value) Then Return False
storage = value
OnPropertyChanged(propertyName)
Return True
End Function
Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
Protected Overridable Sub OnPropertyChanged(propertyName As String)
RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
End Sub
End Class
公共字符串属性在VB TT文件中不存在(确保它的ModelName.tt不是ModelName.Context.tt)。注意使用条件为可空的比较。如果你没有这个,你就不会被解雇!
Public Function AnyProperty(accessibility As String, type As String, name As String, getterAccessibility As String, setterAccessibility As String, defaultValue As String)
Dim CompareLine = if(type.contains("Null"), $"if Not Nullable.Equals({name}, value) ", $"if {name} <> value ")
Return String.Format( _
CultureInfo.InvariantCulture, _
"{6} Private _{0} As {1}{2}{6}" & _
" {3} Property {0} As {1}{6}" & _
" {4}Get{6}" & _
" Return _{0}{6}" & _
" End Get{6}" & _
" {5}Set(ByVal value As {1}){6}" & _
" {7} then SetProperty(_{0}, value){6}" & _
" End Set{6}" & _
" End Property", _
name, _
type, _
defaultValue, _
accessibility, _
getterAccessibility, _
setterAccessibility, _
Environment.NewLine,
CompareLine)
End Function
<强> EntityClassOpening 强>
Public Function EntityClassOpening(entity As EntityType) As String
Return String.Format( _
CultureInfo.InvariantCulture, _
"Partial {0} {1}Class {2}{3}", _
Accessibility.ForType(entity), _
_code.SpaceAfter(_code.MustInheritOption(entity)), _
_code.Escape(entity), _
_code.StringBefore(Environment.Newline & " Inherits ", If(String.IsNullOrEmpty(_typeMapper.GetTypeName(entity.BaseType)), "BaseModel", _typeMapper.GetTypeName(entity.BaseType))))
End Function
如果我发现任何有意义的内容,我会这样做。
请注意我的用例:轻量级VB项目,没有理由介绍MVVM / MVC和存储库层,但有充分的理由想要数据绑定而不是烦人的控件条件更新 - 其中许多都有一个EditValue属于类型Object(Devexpress winforms控件)。
答案 7 :(得分:-1)
如果在直接绑定到数据模型类时需要属性更改通知,则应继续使用ObjectContext。
轻量级DBContext类适用于MVVM或MVVMC等模式,其中视图模型实现属性更改通知,而UI仅绑定到视图模型的属性。您永远不会在这些模式中绑定到数据模型类。