之前一直在使用vs2010或者是vs2008,也一直使用的EF4.0一下的版本……在之前,也习惯了Model First的EF设计方式,因为感觉,在设计界面中可以更好的帮助构思;同时,在设计界面中也很容易的增加一些文字说明(这些说明会存在与最终生成的实体类中)。
在安装的vs2013之后,我赶紧试了EF6.0……此处省略好几万字。
打开了一个以前设计并完成的项目,当中包含了edmx文件,我想看看ef6.0生成的代码到底和ef4.0有什么不同。
结果发现:ef6.0(T4模板)生成的cs文件中,竟然没有包含注释(edmx中的文档),这个让我情何以堪啊。
#region EDM 关系源元数据
[assembly: EdmRelationshipAttribute("Models", "HospitalProject", "Hospital", System.Data.Metadata.Edm.RelationshipMultiplicity.One, typeof(HNWMS.EFData.Hospital), "Project", System.Data.Metadata.Edm.RelationshipMultiplicity.One, typeof(HNWMS.EFData.Project))]
///... ...
[assembly: EdmRelationshipAttribute("Models", "UserMyDisk", "User", System.Data.Metadata.Edm.RelationshipMultiplicity.One, typeof(HNWMS.EFData.User), "MyDisk", System.Data.Metadata.Edm.RelationshipMultiplicity.Many, typeof(HNWMS.EFData.MyDisk))]
#endregion
ef4.0中Container继承自ObjectContext
构造函数(默认生成的)有三个。
///
/// 没有元数据文档可用。
///
public partial class Container : ObjectContext
有公共的带有get访问器的属性,也有对应的私有字段。完全面向对象的写法;唯一不好的就是,没有自动生成实体集合的注释内容(summary)。
///
/// 没有元数据文档可用。
///
public ObjectSet Users
{
get
{
if ((_Users == null))
{
_Users = base.CreateObjectSet("Users");
}
return _Users;
}
}
private ObjectSet _Users;
虽然表明了“已弃用”,但是,在其他地方调用的时候还是很方便,起码很直接。
///
/// 用于向 Users EntitySet 添加新对象的方法,已弃用。请考虑改用关联的 ObjectSet<T> 属性的 .Add 方法。
///
public void AddToUsers(User user)
{
base.AddObject("Users", user);
}
加入了很多Attribute的声明,说实在的,我从来没有注意过这些东西,也不知道做什么用的。
///
/// 用户基类(同时描述总部人员)
///
[EdmEntityTypeAttribute(NamespaceName="Models", Name="User")]
[Serializable()]
[DataContractAttribute(IsReference=true)]
[KnownTypeAttribute(typeof(ProjectManager))]
[KnownTypeAttribute(typeof(HospitalServicer))]
[KnownTypeAttribute(typeof(NursingWorker))]
public partial class User : EntityObject
庞大到不敢看,参数是所有属性的排列,然后加上了个global::用以排除类型冲突;在开发的过程中,我似乎没有使用过实体构造函数来构造实体的实例。
public static User CreateUser(global::System.String code, global::System.String loginPassword, global::System.String realName, global::System.String mobilePhoneNO, global::System.String xType, global::System.String iDCordNO)
同实体集合一般,完全符合面向对象的写法,有属性,就有对应的字段存在。另外再set访问器中还显示的触发了“自跟踪实体”的四个事件(具体什么是自跟踪实体,这里不做复述。)
///
/// 用户编码
///
///
/// 编码规则:
U开头+4位数字顺序码
若4位顺序码达到9999则为
UA开头+3为数字顺序码
以此类推
///
[EdmScalarPropertyAttribute(EntityKeyProperty=true, IsNullable=false)]
[DataMemberAttribute()]
public global::System.String Code
{
get
{
return _Code;
}
set
{
if (_Code != value)
{
OnCodeChanging(value);
ReportPropertyChanging("Code");
_Code = StructuralObject.SetValidValue(value, false);
ReportPropertyChanged("Code");
OnCodeChanged();
}
}
}
private global::System.String _Code;
虽然,我在上边的描述中说了提到了很多关于ef4.0中的“不好”,但是这并不表示我不认同ef4.0。下面说几点我理解的ef的好处:
因为解决方案中存在有一个edmx文件,在其他人接手改解决方案时,很容易就可以看到数据设计,很容易就可以理解数据关系。
所以,在大家都在“喊叫”EF code first的时候,我仍然使用着model or db first。所谓“仁者见仁智者见智,萝卜白菜各有所爱”,大牛们别喷我。谢谢^_&.
不同于EF4.0,在生成的Container上方,并没有出现很多的“元数据”内容。打开这个类的时候,给我的第一感觉就是,啊!好简单啊。
Container继承自DBContext,至于与ObjectContext有什么不同,请大家找度娘或者MSDN。
public partial class Container : DbContext
与ef4.0首先不同在,实体集不再是ObjectSet类型的了,而是DBSet类型的了;其次,它简化了get和set访问器(同时少了对应字段的声明,到时符合了隐式属性访问器的声明方法)
不好再那里呢???注释那里去了,ef4.0最起码还有个 “没有元数据文档可用。”的注释。
public virtual DbSet Users { get; set; }
与ef4.0不同在,实体的构造函数不再那么臃肿,无参数,在内部也仅仅是为导航属性赋予了初始值;同时,属性的get和set也简化了,乍一看,这样一个类好像是谁写出来的Demo呢。
不过,与实体集声明 一般,注释那里去了,ef4.0中,可是把edmx中的“文档以及长说明”都生成在了summary中的。
public partial class User
{
public User()
{
this.SignIns = new HashSet();
this.MyMenus = new HashSet();
this.MyDisks = new HashSet();
}
public string Code { get; set; }
public string LoginPassword { get; set; }
public string RealName { get; set; }
public string MobilePhoneNO { get; set; }
public string xType { get; set; }
public string IDCordNO { get; set; }
public virtual ICollection SignIns { get; set; }
public virtual ICollection MyMenus { get; set; }
public virtual ICollection MyDisks { get; set; }
}
上面非常简单的和肤浅的叙述了ef4.0和ef6.0之间的区别,并阐述了我个人的好恶。
看到这里,很多大牛可能会说“老弟,out man,大家现在都在2015,你还2013呢??”“不就是一个注释么?有那个必要么?”
小弟我解释一下,呵呵,有几年了,没有工作在第一线,没有进行过code的工作,所以对于vs和其他编辑器等,都不甚熟悉了……注释可是很重要的,尤其是符合vs要求的/// summary 注释,因为vs有智能提示啊,我想大家都见过也用过,当.的时候,就会出现很多该类型下的属性啊,方法啊…….拜托都是英文,如何分辨?就算你E文好,请问,你一个人开发应用系统的?
呵呵,有点强词夺理了。
总之,我要大吼一声“我要注释!不管是我写的代码的注释,还是生成的代码的注释,我都要!还要看的懂得注释。”
悄悄的告诉一些比我还菜的小鸟们,其实vs中代码的(符合规范的)注释,相当有用的,比如开发完成后,你可以通过注释生成chm类型或者help view2.x类型的帮助文件的(这里不多说)。
步骤:
找到内容:
public string DbSet(EntitySet entitySet,System.Collections.Generic.IEnumerable.Data.Entity.Core.Metadata.Edm.GlobalItem> itemCollection)
{
return string.Format(
CultureInfo.InvariantCulture,
"{0} virtual DbSet<{1}> {2} {{ get; set; }}",
Accessibility.ForReadOnlyProperty(entitySet),
_typeMapper.GetTypeName(entitySet.ElementType),
_code.Escape(entitySet), summary);
}
这个方法是干什么的呢?其实就是返回一段类似下发代码的文本
public virtual DbSet Users { get; set; }
更改成为:
public string DbSet(EntitySet entitySet,System.Collections.Generic.IEnumerable.Data.Entity.Core.Metadata.Edm.GlobalItem> itemCollection)
{
string summary = entitySet.ElementType.Documentation == null ? "(error message:not have summary in edmx.)" : entitySet.ElementType.Documentation.Summary;
return string.Format(
CultureInfo.InvariantCulture,
"/// \r\n\t/// {3}的集合\r\n\t/// \r\n\t{0} virtual DbSet<{1}> {2} {{ get; set; }}",
Accessibility.ForReadOnlyProperty(entitySet),
_typeMapper.GetTypeName(entitySet.ElementType),
_code.Escape(entitySet), summary);
}
呵呵,你可能觉得,我写的Format中,不该美观。。。。。。我的解释是,反正这个T4最终不会被编译到程序集中,无所谓啦。
这样,最终产生的内容就类似与:
///
/// 用户基类(同时描述总部人员)的集合
///
public virtual DbSet Users { get; set; }
找到内容:
foreach (var entity in typeMapper.GetItemsToGenerate(itemCollection))
{
fileManager.StartNewFile(entity.Name + ".cs");
BeginNamespace(code);
#>
这段代码,就是根据实体的类型名称,产生一个独立的cs文件,并产生Namespace声明,若为类型添加注释,不就是在Namespace下边么……
更改成为:
string summary=string.Empty;
foreach (var entity in typeMapper.GetItemsToGenerate(itemCollection))
{
fileManager.StartNewFile(entity.Name + ".cs");
BeginNamespace(code);
summary = entity.Documentation == null ? entity.Name : entity.Documentation.Summary;
#>
///
/// <#=summary#>
///
还没有完成,继续,找到
public string Property(EdmProperty edmProperty)
{
return string.Format(CultureInfo.InvariantCulture,
"{0} {1} {2} {{ {3}get; {4}set; }}",
Accessibility.ForProperty(edmProperty),
_typeMapper.GetTypeName(edmProperty.TypeUsage),
_code.Escape(edmProperty),
_code.SpaceAfter(Accessibility.ForGetter(edmProperty)),
_code.SpaceAfter(Accessibility.ForSetter(edmProperty)));
}
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 Property(EdmProperty edmProperty)
{
return string.Format(CultureInfo.InvariantCulture,
"/// \r\n\t/// {5}\r\n\t/// \r\n\t{0} {1} {2} {{ {3}get; {4}set; }}",
Accessibility.ForProperty(edmProperty),
_typeMapper.GetTypeName(edmProperty.TypeUsage),
_code.Escape(edmProperty),
_code.SpaceAfter(Accessibility.ForGetter(edmProperty)),
_code.SpaceAfter(Accessibility.ForSetter(edmProperty)),
edmProperty.Documentation == null ? "" : edmProperty.Documentation.Summary
);
}
public string NavigationProperty(NavigationProperty navProp)
{
var endType = _typeMapper.GetTypeName(navProp.ToEndMember.GetEntityType());
return string.Format(
CultureInfo.InvariantCulture,
"/// \r\n\t/// {5}\r\n\t/// \r\n\t{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)),
navProp.Documentation == null ? "" : navProp.Documentation.Summary
);
}
然后“保存”,产生的cs文件中就类似与下边了:
///
/// 用户基类(同时描述总部人员)
///
public partial class User
{
......
///
/// 用户编码
///
public string Code { get; set; }
......
}
OK!这样就搞定了edmx生成内容中无注释的问题了。
再次大吼“注释有用!”
总之,我个人觉得,在使用一个(别人能够归纳与一个系列的)新东西的时候,一定要从设计者的角度去考虑,每一样东西都是有用的。