2015-06-02 68 views
13

所以,我有兩個主要對象,Member和Guild。一個會員可以擁有一個協會,一個協會可以擁有多個會員。不同DbContext和不同模式之間的實體框架關係

我有一個單獨的DbContext和單獨的類庫中的成員類。我打算在多個項目中重用這個類庫並幫助區分,我將數據庫模式設置爲「acc」。我已經廣泛地測試了這個庫,並且可以在acc.Members表中添加,刪除和更新成員。

公會類是這樣的:

public class Guild 
{ 
    public Guild() 
    { 
     Members = new List<Member>(); 
    } 

    public int ID { get; set; } 
    public int MemberID { get; set; } 
    public virtual Member LeaderMemberInfo { get; set; } 
    public string Name { get; set; } 
    public virtual List<Member> Members { get; set; } 
} 

用的映射:

internal class GuildMapping : EntityTypeConfiguration<Guild> 
{ 
    public GuildMapping() 
    { 
     this.ToTable("Guilds", "dbo"); 
     this.HasKey(t => t.ID); 
     this.Property(t => t.MemberID); 
     this.HasRequired(t => t.LeaderMemberInfo).WithMany().HasForeignKey(t => t.MemberID); 
     this.Property(t => t.Name); 
     this.HasMany(t => t.Members).WithMany() 
      .Map(t => 
      { 
       t.ToTable("GuildsMembers", "dbo"); 
       t.MapLeftKey("GuildID"); 
       t.MapRightKey("MemberID"); 
      }); 
    } 
} 

但是,當我嘗試創建一個新的公會,它說,沒有dbo.Members 。

我得到了成員的EF項目的引用,並將成員類的映射添加到了Guild類所屬的DbContext中。 modelBuilder.Configurations.Add(new MemberMapping());

這導致這個錯誤(不知道這是最好的方式。):

{"The member with identity 'GuildProj.Data.EF.Guild_Members' does not exist in the metadata collection.\r\nParameter name: identity"} 

我如何可以利用這兩個表交叉DbContexts之間和不同的數據庫模式外鍵?

UPDATE

我縮小了錯誤的原因。當我創建一個新公會時,我將公會領導的會員ID設置爲MemberID。這工作正常。但是,當我嘗試將該領導的會員對象添加到會員(會員)的協會名單時,這就是導致錯誤的原因。

更新2

下面是如何創建的背景下,行業協會類是在代碼中。(根據要求由侯賽因·哈利勒)

public class FSEntities : DbContext 
{ 
    public FSEntities() 
    { 
     this.Configuration.LazyLoadingEnabled = false; 
     Database.SetInitializer<FSEntities>(null); 
    } 

    public FSEntities(string connectionString) 
     : base(connectionString) 
    { 
    } 

    protected override void OnModelCreating(DbModelBuilder modelBuilder) 
    { 
     modelBuilder.Configurations.Add(new GuildMapping()); 
     modelBuilder.Configurations.Add(new KeyValueMappings()); 
     modelBuilder.Configurations.Add(new LocaleMappings()); 

     modelBuilder.Configurations.Add(new MemberMapping()); 
    } 

    public DbSet<Guild> Guilds { get; set; } 
    public DbSet<KeyValue> KeyValues { get; set; } 
    public DbSet<Locale> Locales { get; set; } 
} 

這是我現在的儲蓄它在回購:

public async Task CreateGuildAsync(Guild guild) 
    { 
     using (var context = new FSEntities(_ConnectionString)) 
     { 
      context.Entry(guild.Members).State = EntityState.Unchanged; 
      context.Entry(guild).State = EntityState.Added; 
      await context.SaveChangesAsync(); 
     } 
    } 

最終解決

所以,我不得不在DbContext中添加映射到MemberRolePermission,其中包含Guild。我必須添加角色和權限,因爲成員有List<Role> Roles,每個角色有List<Permission> Permissions

這讓我更接近解決方案。我仍然得到這樣的錯誤:

{"The member with identity 'GuildProj.Data.EF.Member_Roles' does not exist in the metadata collection.\r\nParameter name: identity"} 

在這裏,當你從Session拉會員,你得到的東西是這樣的:

System.Data.Entity.DynamicProxies.Member_FF4FDE3888B129E1538B25850A445893D7C49F878D3CD40103BA1A4813EB514C 

實體框架似乎不玩這個很好。爲什麼?我不確定,但我認爲這是因爲ContextM創建了成員代理,並且通過將成員克隆到新成員對象中,ContextM不再具有關聯。我認爲,這允許ContextG自由使用新的成員對象。我嘗試在我的DbContexts中設置ProxyCreationEnabled = false,但從Session中退出的Member對象保持爲System.Data.Entity.DynamicProxies.Member類型。

所以,我所做的就是:

Member member = new Member((Member)Session[Constants.UserSession]); 

我不得不克隆每個Role每個Permission以及內部的各自的構造函數。

這讓我有99%的出路。我不得不改變我的回購和我如何保存Guild對象。

  context.Entry(guild.LeaderMemberInfo).State = EntityState.Unchanged; 
      foreach(var member in guild.Members) 
      { 
       context.Entry(member).State = EntityState.Unchanged; 
      } 
      context.Entry(guild).State = EntityState.Added; 
      await context.SaveChangesAsync(); 

回答

8

這是工作代碼標註爲:

在裝配 「M」:

public class Member 
{ 
    public int Id { get; set; } 
    public string Name { get; set; } 
} 

public class MemberMapping : EntityTypeConfiguration<Member> 
{ 
    public MemberMapping() 
    { 
     this.HasKey(m => m.Id); 
     this.Property(m => m.Name).IsRequired(); 
    } 
} 

在assemby 「G」:

  • Guild
  • 您的Guild映射,儘管LeaderMemberInfo映射中有WillCascadeOnDelete(false)
  • modelBuilder.Configurations.Add(new GuildMapping());modelBuilder.Configurations.Add(new MemberMapping());

代碼:

var m = new Member { Name = "m1" }; 
var lm = new Member { Name = "leader" }; 
var g = new Guild { Name = "g1" }; 
g.LeaderMemberInfo = lm; 
g.Members.Add(lm); 
g.Members.Add(m); 
c.Set<Guild>().Add(g); 
c.SaveChanges(); 

執行的SQL:

INSERT [dbo].[Members]([Name]) 
VALUES (@0) 
SELECT [Id] 
FROM [dbo].[Members] 
WHERE @@ROWCOUNT > 0 AND [Id] = scope_identity() 
-- @0: 'leader' (Type = String, Size = -1) 

INSERT [dbo].[Guilds]([MemberID], [Name]) 
VALUES (@0, @1) 
SELECT [ID] 
FROM [dbo].[Guilds] 
WHERE @@ROWCOUNT > 0 AND [ID] = scope_identity() 
-- @0: '1' (Type = Int32) 
-- @1: 'g1' (Type = String, Size = -1) 

INSERT [dbo].[GuildsMembers]([GuildID], [MemberID]) 
VALUES (@0, @1) 
-- @0: '1' (Type = Int32) 
-- @1: '1' (Type = Int32) 

INSERT [dbo].[Members]([Name]) 
VALUES (@0) 
SELECT [Id] 
FROM [dbo].[Members] 
WHERE @@ROWCOUNT > 0 AND [Id] = scope_identity() 
-- @0: 'm1' (Type = String, Size = -1) 

INSERT [dbo].[GuildsMembers]([GuildID], [MemberID]) 
VALUES (@0, @1) 
-- @0: '1' (Type = Int32) 
-- @1: '2' (Type = Int32) 

存在關聯對象時,這也適用。


更普遍的情況下,原來的答覆:

你不能在不同的上下文類型組合成一個對象圖。這意味着,你不能這樣做

from a in context.As 
join b in context.Bs on ... 

...因爲總有應該創造整個SQL查詢的一個方面,所以它應該有所有必需的映射信息。

即使是來自不同的程序集,您也可以將相同的類型註冊到兩個不同的上下文中。所以,你可以在Guild的組裝語境映射Member,姑且稱之爲contextG,但只有當

  1. Member並非指其他類型的contextG映射。這可能意味着Member中的導航屬性必須明確忽略。
  2. Member不能引用contextG中的類型,因爲這些類型不是Member的上下文的一部分。

如果這些條件不能得到滿足,你能做的最好的是創造Guild的組裝新Member類,然後在上下文註冊的映射。也許你想用一個不同的名字來防止歧義,但這是唯一的選擇。

+1

使用相同的「成員」類是不可能的?上下文之間的映射會有所不同,並且「成員」對象不能在上下文之間傳輸,而不會脫離第一個上下文,但似乎可能。 – jjj

+0

@jjj是的,但我認爲'會員'將有其他協會,不應該被映射。如果'Member'是一個POCO並且映射是通過流暢的映射,那麼應該可以通過明確地將導航屬性保持未映射。但是我認爲在一個應用程序中有'Member'對象可以通過不同的上下文來實現是令人困惑的。 –

+0

成員類型的屬性GuildLeader正確保存到數據庫沒有意義,但列表屬性不起作用。兩者都只保存一個ID。 – ScubaSteve

1

我發現,當我有實體和建立關係是經常因爲我違背了框架的流量或試圖建立在技術上不充分形成的關係或抽象的問題。我在這裏建議的是在深入研究這個特定問題之前快速回退並分析一些事情。

首先,我很好奇你爲什麼在這裏使用不同的模式來訪問訪問對象圖的單個應用程序。多個模式在某些情況下可能有用,但我認爲Brent Ozar在這個article中提出了一個非常重要的觀點。鑑於這些信息,我傾向於首先建議您將多個模式合併爲一個,並推薦使用數據庫的單個數據庫上下文。

接下來要處理的是對象圖。至少對我來說,我在數據建模方面遇到的最大困難是,當我沒有先弄清楚應用程序對數據庫有什麼問題時。我的意思是指出應用程序首先在各種上下文中需要什麼數據,然後查看如何優化這些數據結構以實現關係上下文中的性能。讓我們看看如何可能做...基於模型

上面我可以看到,我們在域中的一些關鍵術語/對象:

  • 會員的行業協會
  • 收藏的收藏
  • 公會領導人的集合。

此外,我們還需要實現一些業務規則:

  • 一個公會可以有1個領袖(?而且可能不止1)
  • 一個公會的領導者必須是成員
  • 公會具有0個或多個成員的列表
  • 成員可以屬於一個公會(並且可能大於1?)

因此,考慮到這些信息,我們來研究一下您的應用程序可能對這個數據模型有什麼問題。我的應用程序可以:

  • 查找成員並查看其屬性
  • 查找成員並查看其屬性,他們是一個公會會長
  • 查找公會和看到所有的列表其成員
  • 查找公會和公會看到領導人的名單
  • 查找所有公會領袖

確定的名單,現在我們可以坐下來BR正如他們所說......

在這種情況下,使用公會和成員之間的連接表是最佳選擇。它將爲您提供在多個行會或無行會中擁有成員的能力,並提供低鎖定更新策略 - 這麼好的召喚!

轉向公會領導者有幾個選擇可能有意義。儘管公會軍士長可能永遠不會有案例,但我認爲考慮一個名爲公會領袖的新實體是有意義的。這種方法允許多少倍。您可以在應用程序中緩存公會隊長ID列表,因此,您可以在本地應用程序緩存中僅使用領導者ID列表而不是整個領導者對象,而不是進行數據庫訪問以授權領導者採取的公會行動。相反,你可以得到一個公會的領導者名單,不管你的查詢方向如何,你都可以在覈心實體上創建聚集索引,或者在聯合實體上創建易於維護的中間索引。

就像我在這種方式開始時指出的那樣,長時間「回答」,當我碰到像你這樣的問題時,通常是因爲我違背了實體的粒度。我鼓勵你重新思考你的數據模型,以及如何用更低摩擦的方法 - 釋放多個模式並添加一箇中間的guild_leader對象。乾杯!

1

除非你明確地說,是Member實體應該映射到acc.Members,EF會希望它是在dbo模式Members表。爲此,您需要爲這種類型提供要麼EntityTypeConfigurationSystem.ComponentModel.DataAnnotations.Schema.TableAttribute[Table("acc.Members")]

+0

在我的會員映射類​​,我不告訴它使用acc架構。原始成員代碼中的所有內容都適用,例如:添加,刪除和更新成員。這是當我試圖把它拉到另一個DbContext,我有問題。 – ScubaSteve

+0

您不能將來自不同上下文的類型組合到一個圖中。如果你想使用一個使用來自兩個獨立上下文表格的圖表樹,你只需在其中一個表格中創建缺少的類型。 –

0

我回答你更新的問題:

使用該行更新上下文之前嘗試

context.Entry(Guild.Members).State = Entity.EntityState.Unchanged 

這樣便解決了錯誤,你有

+0

你可以用你的代碼更新你的問題,我會讓你知道到底是放置這一行ocde –

+0

我試過這個,但我得到了這個錯誤:{「實體類型List'1不是模型的一部分「} – ScubaSteve

+0

請更新你的問題與你如何創建你的上下文,所以我可以提供幫助,你面臨的錯誤是由於在創建上下文中的錯誤 –

相關問題