1. 程式人生 > >EF6.0 生成的程式碼中沒有註釋的解決方法

EF6.0 生成的程式碼中沒有註釋的解決方法

目錄

初試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&lt;T&gt; 屬性的 .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>
        /// 編碼規則:&#xA;U開頭+4位數字順序碼&#xA;若4位順序碼達到9999則為&#xA;UA開頭+3為數字順序碼&#xA;以此類推&#xA;
        /// </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生成器”

步驟:

  1. 在vs中開啟edmx
  2. 隨便找個空白的地方右鍵,然後選擇“新增程式碼生成項”
  3. 然後在隨後出現的視窗中選擇“EF6.x DBContext生成器”,確定
  4. 驗證:在解決方案資源管理器中,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生成內容中無註釋的問題了。
  再次大吼“註釋有用!”

總結

  總之,我個人覺得,在使用一個(別人能夠歸納與一個系列的)新東西的時候,一定要從設計者的角度去考慮,每一樣東西都是有用的。