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生成內容中無註釋的問題了。
  再次大吼“註釋有用!”

總結

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章