Entity Framework relationships between different DbContext and different schemas

淺唱寂寞╮ 提交于 2019-11-29 05:44:05

This is working code:

In assembly "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();
    }
}

In assemby "G":

  • your Guild class
  • your Guild mapping, albeit with WillCascadeOnDelete(false) in the LeaderMemberInfo mapping.
  • modelBuilder.Configurations.Add(new GuildMapping()); and modelBuilder.Configurations.Add(new MemberMapping());

Code:

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();

Executed 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)

This also works when associating existing objects.


Original answer for more general case:

You can't combine types in different contexts into one object graph. That means, you can't do something like

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

...because there's always one context that should create the whole SQL query, so it should have all required mapping information.

You can register the same type into two different contexts though, even from different assemblies. So you could map Member in the context in Guild's assembly, let's call it contextG, but only if

  1. Member doesn't refer to other types that aren't mapped in contextG. This may imply that navigation properties in Member must be ignored explicitly.
  2. Member can't refer to types in contextG, because these types are not part of Member's context.

If any of these conditions can't be fulfilled the best you can do is create a new Member class in Guild's assembly and register its mapping in the context. Maybe you want to use a different name to prevent ambiguity, but this is about the only alternative left.

I have found that when I am having problem with Entity and building relationships it's often because I am going against the flow of the framework or trying to build relationships or abstractions that are not technically well-formed. What I would suggest here is to take a quick step back and analyze a few things before looking deeper into this specific issue.

First off, I am curious why you are using different schema here for what is likely a single application accessing an object graph. Multiple schemas can be useful in some context but I think Brent Ozar makes a very salient point in this article. Given that information, I am inclined to first suggest that you merge the multiple schema into one and push over to using a single DB context for the database.

The next thing to tackle is the object graph. At least for me the biggest struggles I have had with data modeling are when I don't first figure out what questions the application has of the database. What I mean by this is figure out what data does the application want in its various contexts first and then look at how to optimize those data structures for performance in a relational context. Let's see how that might be done ...

Based on your model above I can see that we have a few key terms/objects in the domain:

  • Collection of Guilds
  • Collection of Members
  • Collection of Guild Leaders.

Additionally we have some business rules that need to be implemented:

  • A Guild can have 1 leader (and possibly more than 1?)
  • A Guild Leader must be a Member
  • A Guild has a list of 0 or more Members
  • A Member can belong to a Guild (and possibly more than 1?)

So given this information let's investigate what questions your application might have of this data model. I the application can:

  • look up a member and see their properties
  • look up a member and see their properties and that they are a guild leader
  • look up a guild and see a list of all its members
  • look up a guild and see a list of guild leaders
  • look up a list of all guild leaders

Ok, now we can get down to brass tacks, as they say...

The use of the join table between Guilds and Members is optimal in this instance. It will provide you the ability to have members in multiple guilds or no guilds and provide a low locking update strategy - so good call!

Moving onto Guild Leaders there are a few choices that might make sense. Even though there may never be a case for say guild sergeants, I think it makes sense to consider a new entity called a Guild Leader. What this approach allows for is several fold. You can cache in app the list of guild leader ids, so rather than make a db trip to authorize a guild action taken by a leader you can hit local application cache that only has the leader id list and not the whole leader object; conversely, you can get the list of leaders for a guild and regardless of the query direction you can hit clustered indexes on the core entities or the easy-to-maintain intermediate index on the join entity.

Like I noted at the start of this way to long "answer", when I run into issues like yours, it's typically because I am going against the grain of Entity. I encourage you to re-think your data model and how with a lower friction approach - loose the multiple schema and add an intermediate guild_leader object. Cheers!

Unless you explicitly say, that Member entity should be mapped to acc.Members, EF will expect it to be in dbo schema Members table. For this you need to provide either EntityTypeConfiguration for this type or annotate it with System.ComponentModel.DataAnnotations.Schema.TableAttribute like [Table("acc.Members")]

I am answering your updated question :

try using this line before updating your context

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

this will solve the error you have

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!