none
大型資料庫應用entity framework 該不該分多個Context呢 RRS feed

  • 問題

  • 請問各位前輩:
    最近想使用EF開發系統,可是現在有一個疑問,就是上百個Table(接近千個)的資料庫,而且彼此之前的關聯還蠻多的,舉個例子:
    一個Database,裡面含了多套的子系統,而核心的資料表員工資料表(Employee)分別和多個子系統相關聯,所以我在規劃時該做成一個Context(一個emx檔)還是多個呢?
    一個的考量:彼此都關聯到核心Table(Employee)
    多個考量:是否有效能上的問題,因為如果是一個的話,檔案會變得蠻大的

    另外多個的話Context之間的Join及Update該如何做到,謝謝
    2009年4月14日 下午 02:40

解答

  • 這個問題已我的經驗來回答.

    其實這個問題是物件導向設計上的問題,只要架構能夠做好會發現這些問題就會解決.
    強調一點,很多人只把EF當作是一種簡化資料存取的工具,但不幸在目前情況下所有的ORM都與物件導向設計有很大之相關性(不然就不會簡稱是O R M),因此如果本身程式架構上並不是採用物件導向設計,使用ORM會衍生出很多問題無法克服,尤其當系統越大時這個問題越明顯.

    而在EDM設計上,工具針對這個問題並沒辦法處理,必須手動編輯CSDL,SSDL,MSL.
    大概簡述一下做法
    1.自行建立關於Employee的Class.
    2.切模組,各模組應該都會有對應的Context.
    3.為這些Context賺寫CSDL,SSDL,MSL
    4.這些Context的CSDL中的Employee都會指向第一點建立的Class.
    也就是說Employee Class只會有一個,但會重複定義在各Context的EDM中.

    這是第一種做法,當需要關聯到Employee物件時做Join狀況,譬如說需要用Employee.Name作排序

    而大多數情況其實並不需要由Context直接關聯到Employee物件而是由Bussiness Logic或 Modal來對應.這部分牽連到物件導向上之設計,比較無法簡易描述.
    • 已標示為解答 Lolota Lee 2009年4月21日 上午 07:39
    2009年4月14日 下午 11:51
  • 你可以用VS 2008開啟EDM File(用XML編輯模式來看).
    VS 2008產生的EDM實際上是把CSDL,SSDL與MSL結合再相同檔案內(為了EDM Tools).而工具本身會依UI來產生Entity Class的C#檔案.
    所以VS 2008(EDM Tools),如果用工具是無法達成此目的的,你必須手動撰寫這些設定檔與相關的Class
    這代表你必須拋棄工具,先研讀CSDL,SSDL與MSL結構與如何手動產生DataContext Class與Entity Class.
    如果了解這部分了自然就會知道該如何做到第4點.

    國內這部分書籍除了黃忠成那邊極易之道有簡介這些資訊外,已經沒有其他書集了,而詳細相關資訊可以參考MSDN的說明文件會比較詳細,
    http://msdn.microsoft.com/en-us/library/bb399572.aspx

    CSDL,SSDL,MSL
    http://msdn.microsoft.com/en-us/library/bb399604.aspx
    • 已標示為解答 Lolota Lee 2009年4月21日 上午 07:39
    2009年4月15日 下午 11:30

所有回覆

  • 這個問題已我的經驗來回答.

    其實這個問題是物件導向設計上的問題,只要架構能夠做好會發現這些問題就會解決.
    強調一點,很多人只把EF當作是一種簡化資料存取的工具,但不幸在目前情況下所有的ORM都與物件導向設計有很大之相關性(不然就不會簡稱是O R M),因此如果本身程式架構上並不是採用物件導向設計,使用ORM會衍生出很多問題無法克服,尤其當系統越大時這個問題越明顯.

    而在EDM設計上,工具針對這個問題並沒辦法處理,必須手動編輯CSDL,SSDL,MSL.
    大概簡述一下做法
    1.自行建立關於Employee的Class.
    2.切模組,各模組應該都會有對應的Context.
    3.為這些Context賺寫CSDL,SSDL,MSL
    4.這些Context的CSDL中的Employee都會指向第一點建立的Class.
    也就是說Employee Class只會有一個,但會重複定義在各Context的EDM中.

    這是第一種做法,當需要關聯到Employee物件時做Join狀況,譬如說需要用Employee.Name作排序

    而大多數情況其實並不需要由Context直接關聯到Employee物件而是由Bussiness Logic或 Modal來對應.這部分牽連到物件導向上之設計,比較無法簡易描述.
    • 已標示為解答 Lolota Lee 2009年4月21日 上午 07:39
    2009年4月14日 下午 11:51
  • 謝謝programlin前輩的回答

    您提到
    4.這些Context的CSDL中的Employee都會指向第一點建立的Class.

    請問該如何實作指向Class呢?

    謝謝
    2009年4月15日 上午 04:43
  • 你可以用VS 2008開啟EDM File(用XML編輯模式來看).
    VS 2008產生的EDM實際上是把CSDL,SSDL與MSL結合再相同檔案內(為了EDM Tools).而工具本身會依UI來產生Entity Class的C#檔案.
    所以VS 2008(EDM Tools),如果用工具是無法達成此目的的,你必須手動撰寫這些設定檔與相關的Class
    這代表你必須拋棄工具,先研讀CSDL,SSDL與MSL結構與如何手動產生DataContext Class與Entity Class.
    如果了解這部分了自然就會知道該如何做到第4點.

    國內這部分書籍除了黃忠成那邊極易之道有簡介這些資訊外,已經沒有其他書集了,而詳細相關資訊可以參考MSDN的說明文件會比較詳細,
    http://msdn.microsoft.com/en-us/library/bb399572.aspx

    CSDL,SSDL,MSL
    http://msdn.microsoft.com/en-us/library/bb399604.aspx
    • 已標示為解答 Lolota Lee 2009年4月21日 上午 07:39
    2009年4月15日 下午 11:30
  • programlin前輩:
    是不是最後做出來會是只有一個Employee的class
    然後每個模組裡的csdl、ssdl、msl都會有employee的相關描述?
    請問這樣和我做出多個emx檔,裡面分別都定義了employee,的作法有是否有效能上的差別呢?還是只是是程式碼較簡單呢?
    謝謝

    另外,我在其他的物件用EmployeeReference.Load()會發生:已經開啟一個與這個 Command 相關的 DataReader,必須先將它關閉的錯誤訊息,請問這樣的錯誤大概是哪一個環節沒設定好(找了蠻久的問題)謝謝

    2009年4月21日 上午 09:07
  • 是的
    而且這個employee所在組件不一定要與context所在組件相同.

    這樣做的目的很簡單,試想一個問題如果每個context都有對應的employee class對於程式而言無法直接做轉換即使欄位及屬性都相同也是一樣.

    EmployeeReference.Load()的問題其實很容易理解.
    這是因為EF內部是使用DataReader作資料存取,所以如果如下就會產生Error,因為DataReader用同一個Connection.

    foreach(Employee emp in context.Employees)
    {
      emp .Customer .... //Error因connection已開啟尚未關閉
    }


    處理方式有兩種
    1,設定ConnectionString加上MultipleActiveResultSets=true,但只適用於SQL 2005以後之版本
    2.先讀出放置在List中
    foreach(Employee emp in context.Employees.ToList())
    {
      emp .Customer .... //通過,因emp這時候已經不是context中的參考物件,而是獨立的instance,與context脫鉤
    }


    2009年4月22日 上午 01:13
  • programlin前輩:
    是我物件設計錯誤嗎
    會出現 概念端的employee和物件端的employee型態不符耶?
    Why?
    謝謝

    我是將概念端的Employee的EntityType設定成Employee的Namespace,請問這樣設定對嗎

    2009年4月22日 下午 05:42
  • 你是用VS中的EDM Tools來設定嗎?
    EDM Tools無法達成此需求,這類修訂要修改CSDL來達成

    如是直接編輯CSDL出現錯誤,你可以把CSDL PO上來我幫你確認一下.
    2009年4月23日 上午 12:18
  • 怎麼看都不可能是同一支Class.
    因為你去設計EDM時的NameSpace就不同了.
    你在寫程式時也沒辦法將二支EDM 轉到同一個NameSpace中.
    在.net 不管你Class 內容是否相同.只要Namespace 或 Class Name 不同,他就是不同的Class.
    2009年4月23日 上午 01:42
  • 是的
    如好說所說的EDM Tools是沒辦法修改對道不同組件中的Class.
    原因很簡單,EDM Tools對應的Class是由EDM Tools產生的.

    但如果不用EDM Tools則有辦法做到,就是修改CSDL File.
    至於該如何修改請先研讀上面的MSDN文件.

    http://msdn.microsoft.com/en-us/library/bb399572.aspx

    CSDL,SSDL,MSL
    http://msdn.microsoft.com/en-us/library/bb399604.aspx
    2009年4月23日 上午 01:47
  • 我是把它分成兩個專案來做,其中一個專案為Entity.Core,就是Employee的專案,另一個專案為Entity.Portal,就是其他模組的專案
    我的CSDL如下
    Employee.csdl
    ================================================================================
      <Schema Namespace="Entity.Core" Alias="Self" xmlns="http://schemas.microsoft.com/ado/2006/04/edm">
        <EntityContainer Name="EhrCoreEntities">
          <EntitySet Name="Employees" EntityType="Entity.Core.Employee" />
        </EntityContainer>
        <EntityType Name="Employee">
          <Key>
            <PropertyRef Name="EMPLOYEE_ID" />
          </Key>
          <Property Name="EMPLOYEE_ID" Type="Int32" Nullable="false" />
          <Property Name="EMPLOYEE_NO" Type="String" Nullable="false" MaxLength="10" Unicode="true" FixedLength="false" />
          <Property Name="EMPLOYEE_CNAME" Type="String" Nullable="false" MaxLength="20" Unicode="true" FixedLength="false" />
          <Property Name="EMPLOYEE_LASTNAME" Type="String" Nullable="false" MaxLength="30" Unicode="true" FixedLength="false" />
          <Property Name="EMPLOYEE_FIRSTNAME" Type="String" Nullable="false" MaxLength="20" Unicode="true" FixedLength="false" />
          <Property Name="EMPLOYEE_BIRTHDAY" Type="DateTime" Nullable="false" />
          <Property Name="EMPLOYEE_SEX" Type="String" Nullable="false" MaxLength="1" Unicode="true" FixedLength="false" />
          <Property Name="EMPLOYEE_OFFICE_TEL" Type="String" Nullable="false" MaxLength="20" Unicode="true" FixedLength="false" />
          <Property Name="EMPLOYEE_EMAIL" Type="String" Nullable="false" MaxLength="50" Unicode="true" FixedLength="false" />
        </EntityType>
      </Schema>
    ================================================================================


    Portal.csdl
    ================================================================================
     <Schema Namespace="Entity.Portal" Alias="Self" xmlns="http://schemas.microsoft.com/ado/2006/04/edm">

        <EntityContainer Name="PortalBulletinEntities">
          <EntitySet Name="Employees" EntityType="Entity.Core.Employee" />
          <EntitySet Name="Bulletins" EntityType="Entity.Portal.Bulletin" />
         
          <AssociationSet Name="FK_Bulletin_Employee" Association="Entity.Portal.FK_Bulletin_Employee">
            <End Role="Bulletin" EntitySet="Bulletins" />
            <End Role="Employee" EntitySet="Employees" />
          </AssociationSet>
        </EntityContainer>

        <EntityType Name="Bulletin">
          <Key>
            <PropertyRef Name="BULLETIN_ID" />
          </Key>
          <Property Name="BULLETIN_ID" Type="Int32" Nullable="false" />
          <Property Name="SUBJECT" Type="String" Nullable="false" MaxLength="50" Unicode="true" FixedLength="false" />
          <Property Name="CONTENT" Type="String" Nullable="false" MaxLength="Max" Unicode="true" FixedLength="false" />
          <Property Name="BULLETIN_LINK" Type="String" Nullable="false" MaxLength="300" Unicode="true" FixedLength="false" />
          <Property Name="CREATEDATE" Type="DateTime" Nullable="false" />
          <!--<Property Name="EMPLOYEE_ID" Type="Int32" Nullable="false" />-->
          <Property Name="UPDATE_EMPLOYEE_ID" Type="Int32" Nullable="false" />
          <Property Name="UPDATE_DATE" Type="DateTime" Nullable="false" />
          <NavigationProperty Name="Employee" Relationship="Entity.Portal.FK_Bulletin_Employee" FromRole="Bulletin" ToRole="Employee" />
        </EntityType>

        <Association Name="FK_Bulletin_Employee">
          <End Type="Entity.Portal.Bulletin" Role="Bulletin" Multiplicity="0..1" />
          <End Type="Entity.Core.Employee" Role="Employee" Multiplicity="1" />
        </Association>
      </Schema>
    ================================================================================

    然後在Entity.Core專案有一個Employee的class
    而Entity.Portal我在Bulletin class設定了一個屬性,如下:
    ================================================================================
            [global::System.Data.Objects.DataClasses.EdmRelationshipNavigationPropertyAttribute("Entity.Portal", "FK_Bulletin_Employee", "Employee")]
            public System.Data.Objects.DataClasses.EntityCollection<Entity.Core.Employee> Employee
            {
                get
                {
                    return ((global::System.Data.Objects.DataClasses.IEntityWithRelationships)(this)).RelationshipManager.
                        GetRelatedCollection<Entity.Core.Employee>("FK_Bulletin_Employee", "Employee");
                }
            }
    ================================================================================

    就是錯在這裡,請問是有需要設定成同一個專案嗎?還是又哪個地方設定錯了呢?謝謝

    2009年4月23日 上午 03:54
  • 兩個CSDL指向的Namespace不同於Class Employee的宣告

    你看一下你的Employee Class應該有類似底下的Attribute在class之上
    [EdmEntityTypeAttribute(NamespaceName = "Entity.Core", Name = "Employee")]

    其中的Entity.Core必須與你的CSDL一致


    你是不是建了兩個一模一樣的Employee Class?但NameSpace宣告不同?
    這樣不是正確的做法

    你應該是要將
    Employee Class組件獨立出
    讓其他Context所在組件引用,這樣才會一致.

    2009年4月23日 上午 05:49
  • 抱歉,我不是很懂
    我有兩個專案,一個是Entity.Core、一個是Entity.Portal
    而在Entity.Core裡有一個Employee的class,也有Employee的cdsl、ssdl、msl
    而在Entity.Portal裡有一個PortalBulletin的class,也有PortalBulletin的cdsl、ssdel、msl
    我在Entity.Portal的PortalBulletin.cdsl裡有描述Employee如上的Portal.csdl(前次貼時名字打錯了)

    我的Employee的class 的NameSpace是Entity.Core
    而另一個專案Entity.Portal的NameSpace是Entity.Portal,該專案裡沒有Employee的class只有Get他的屬性(前次貼時說出錯的地方)

    所以我不是很清楚我應該要如何去設定耶,我的解讀是不是所有的csdl、ssdl、msl要放在同一個專案,而Employee的class可以獨力在另外一個專案,那照這樣的話,我的csdl、ssdl、msl的NameSpace要設定成一樣的嗎?還是可以有自己的NameSpace如我目前的設定方式(Entity.Portal)呢?

    謝謝
    2009年4月23日 下午 01:50