目錄
初試Entity Framework6.0
之前一直在使用vs2010或者是vs2008,也一直使用的EF4.0一下的版本……在之前,也習慣了Model First的EF設計方式,因爲感覺,在設計界面中可以更好的幫助構思;同時,在設計界面中也很容易的增加一些文字說明(這些說明會存在與最終生成的實體類中)。
發現問題
在安裝的vs2013之後,我趕緊試了EF6.0……此處省略好幾萬字。
打開了一個以前設計並完成的項目,當中包含了edmx文件,我想看看ef6.0生成的代碼到底和ef4.0有什麼不同。
結果發現:ef6.0(T4模板)生成的cs文件中,竟然沒有包含註釋(edmx中的文檔),這個讓我情何以堪啊。
先來回顧一下ef4.0生成的內容
ef4.0 關係元數據(不知道大家看不看,反正我沒仔細看過)
#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 聲明以
ef4.0中Container繼承自ObjectContext
構造函數(默認生成的)有三個。
/// <summary>
/// 沒有元數據文檔可用。
/// </summary>
public partial class Container : ObjectContext
ef4.0 實體集合
有公共的帶有get訪問器的屬性,也有對應的私有字段。完全面向對象的寫法;唯一不好的就是,沒有自動生成實體集合的註釋內容(summary)。
/// <summary>
/// 沒有元數據文檔可用。
/// </summary>
public ObjectSet<User> Users
{
get
{
if ((_Users == null))
{
_Users = base.CreateObjectSet<User>("Users");
}
return _Users;
}
}
private ObjectSet<User> _Users;
ef4.0 AddTo方法
雖然表明了“已棄用”,但是,在其他地方調用的時候還是很方便,起碼很直接。
/// <summary>
/// 用於向 Users EntitySet 添加新對象的方法,已棄用。請考慮改用關聯的 ObjectSet<T> 屬性的 .Add 方法。
/// </summary>
public void AddToUsers(User user)
{
base.AddObject("Users", user);
}
ef4.0 實體聲明
加入了很多Attribute的聲明,說實在的,我從來沒有注意過這些東西,也不知道做什麼用的。
/// <summary>
/// 用戶基類(同時描述總部人員)
/// </summary>
[EdmEntityTypeAttribute(NamespaceName="Models", Name="User")]
[Serializable()]
[DataContractAttribute(IsReference=true)]
[KnownTypeAttribute(typeof(ProjectManager))]
[KnownTypeAttribute(typeof(HospitalServicer))]
[KnownTypeAttribute(typeof(NursingWorker))]
public partial class User : EntityObject
ef4.0 實體構造函數
龐大到不敢看,參數是所有屬性的排列,然後加上了個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)
ef4.0 基元屬性
同實體集合一般,完全符合面向對象的寫法,有屬性,就有對應的字段存在。另外再set訪問器中還顯示的觸發了“自跟蹤實體”的四個事件(具體什麼是自跟蹤實體,這裏不做複述。)
/// <summary>
/// 用戶編碼
/// </summary>
/// <LongDescription>
/// 編碼規則:
U開頭+4位數字順序碼
若4位順序碼達到9999則爲
UA開頭+3爲數字順序碼
以此類推

/// </LongDescription>
[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的好處:
- 我還是很喜歡ef的,因爲其簡單、易用,配合上mvc、WCF Data Service很容易就完成一個解決方案;
- 我更加喜歡vs提供的edmx設計器,在這裏我可以不借助其他“構思輔助”軟件,就可以完成“數據設計”以及“數據關係”的梳理工作。
- 通過edmx我可以很容易的爲“實體”“屬性”“導航屬性”添加“文檔”(在最終的代碼中爲summary,這也是我寫這篇文章的目的。)
因爲解決方案中存在有一個edmx文件,在其他人接手改解決方案時,很容易就可以看到數據設計,很容易就可以理解數據關係。
所以,在大家都在“喊叫”EF code first的時候,我仍然使用着model or db first。所謂“仁者見仁智者見智,蘿蔔白菜各有所愛”,大牛們別噴我。謝謝^_&.
再來看EF6.0生成的內容
ef6.0 構造函數
不同於EF4.0,在生成的Container上方,並沒有出現很多的“元數據”內容。打開這個類的時候,給我的第一感覺就是,啊!好簡單啊。
Container繼承自DBContext,至於與ObjectContext有什麼不同,請大家找度娘或者MSDN。
public partial class Container : DbContext
ef6.0 實體集聲明
與ef4.0首先不同在,實體集不再是ObjectSet類型的了,而是DBSet類型的了;其次,它簡化了get和set訪問器(同時少了對應字段的聲明,到時符合了隱式屬性訪問器的聲明方法)
不好再那裏呢???註釋那裏去了,ef4.0最起碼還有個 “沒有元數據文檔可用。”的註釋。
public virtual DbSet<User> Users { get; set; }
ef6.0 實體聲明
與ef4.0不同在,實體的構造函數不再那麼臃腫,無參數,在內部也僅僅是爲導航屬性賦予了初始值;同時,屬性的get和set也簡化了,乍一看,這樣一個類好像是誰寫出來的Demo呢。
不過,與實體集聲明 一般,註釋那裏去了,ef4.0中,可是把edmx中的“文檔以及長說明”都生成在了summary中的。
public partial class User
{
public User()
{
this.SignIns = new HashSet<SignIn>();
this.MyMenus = new HashSet<MyMenu>();
this.MyDisks = new HashSet<MyDisk>();
}
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<SignIn> SignIns { get; set; }
public virtual ICollection<MyMenu> MyMenus { get; set; }
public virtual ICollection<MyDisk> MyDisks { get; set; }
}
上面非常簡單的和膚淺的敘述了ef4.0和ef6.0之間的區別,並闡述了我個人的好惡。
問題解決的必要性
看到這裏,很多大牛可能會說“老弟,out man,大家現在都在2015,你還2013呢??”“不就是一個註釋麼?有那個必要麼?”
小弟我解釋一下,呵呵,有幾年了,沒有工作在第一線,沒有進行過code的工作,所以對於vs和其他編輯器等,都不甚熟悉了……註釋可是很重要的,尤其是符合vs要求的/// summary 註釋,因爲vs有智能提示啊,我想大家都見過也用過,當.的時候,就會出現很多該類型下的屬性啊,方法啊…….拜託都是英文,如何分辨?就算你E文好,請問,你一個人開發應用系統的?
呵呵,有點強詞奪理了。
總之,我要大吼一聲“我要註釋!不管是我寫的代碼的註釋,還是生成的代碼的註釋,我都要!還要看的懂得註釋。”
悄悄的告訴一些比我還菜的小鳥們,其實vs中代碼的(符合規範的)註釋,相當有用的,比如開發完成後,你可以通過註釋生成chm類型或者help view2.x類型的幫助文件的(這裏不多說)。
解決問題
第一步 爲edmx添加“代碼生成項”也就是“EF6.x DBContext生成器”
步驟:
- 在vs中打開edmx
- 隨便找個空白的地方右鍵,然後選擇“添加代碼生成項”
- 然後在隨後出現的窗口中選擇“EF6.x DBContext生成器”,確定
- 驗證:在解決方案資源管理器中,edmx文件下方就會出現你剛剛添加的“代碼生成內容”,一般叫做Model.Context.tt 和Model.tt(其實這兩個東西,就是T4模板;打開它,然後按“ctrl+s”的時候,它就會自動執行,並按照edmx文件中的設計,生成相關的Container 、DBSet、entity)
第二步 修改T4模板內容
Model.Context.tt文件
找到內容:
public string DbSet(EntitySet entitySet,System.Collections.Generic.IEnumerable<System.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<User> Users { get; set; }
更改成爲:
public string DbSet(EntitySet entitySet,System.Collections.Generic.IEnumerable<System.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,
"/// <summary>\r\n\t/// {3}的集合\r\n\t/// </summary>\r\n\t{0} virtual DbSet<{1}> {2} {{ get; set; }}",
Accessibility.ForReadOnlyProperty(entitySet),
_typeMapper.GetTypeName(entitySet.ElementType),
_code.Escape(entitySet), summary);
}
呵呵,你可能覺得,我寫的Format中,不該美觀。。。。。。我的解釋是,反正這個T4最終不會被編譯到程序集中,無所謂啦。
這樣,最終產生的內容就類似與:
/// <summary>
/// 用戶基類(同時描述總部人員)的集合
/// </summary>
public virtual DbSet<User> Users { get; set; }
Model.tt文件
找到內容:
foreach (var entity in typeMapper.GetItemsToGenerate<EntityType>(itemCollection))
{
fileManager.StartNewFile(entity.Name + ".cs");
BeginNamespace(code);
#>
這段代碼,就是根據實體的類型名稱,產生一個獨立的cs文件,併產生Namespace聲明,若爲類型添加註釋,不就是在Namespace下邊麼……
更改成爲:
string summary=string.Empty;
foreach (var entity in typeMapper.GetItemsToGenerate<EntityType>(itemCollection))
{
fileManager.StartNewFile(entity.Name + ".cs");
BeginNamespace(code);
summary = entity.Documentation == null ? entity.Name : entity.Documentation.Summary;
#>
/// <summary>
/// <#=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,
"/// <summary>\r\n\t/// {5}\r\n\t/// </summary>\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,
"/// <summary>\r\n\t/// {5}\r\n\t/// </summary>\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文件中就類似與下邊了:
/// <summary>
/// 用戶基類(同時描述總部人員)
/// </summary>
public partial class User
{
......
/// <summary>
/// 用戶編碼
/// </summary>
public string Code { get; set; }
......
}
OK!這樣就搞定了edmx生成內容中無註釋的問題了。
再次大吼“註釋有用!”
總結
總之,我個人覺得,在使用一個(別人能夠歸納與一個系列的)新東西的時候,一定要從設計者的角度去考慮,每一樣東西都是有用的。