不同的DbContext和不同模式之间的实体框架的关系 [英] Entity Framework relationships between different DbContext and different schemas

查看:348
本文介绍了不同的DbContext和不同模式之间的实体框架的关系的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以,我有两个主要目标,各会员协会和。一个成员可以拥有一个公会,一个公会可以有多个会员。



我在一个单独的DbContext和独立的类库中的成员类。我计划在多个项目中重用这个类库,并帮助区分,我设置的数据库模式是ACC。我已经广泛使用这个库,并可以添加,删除,并在acc.Members表更新成员



公会类是这样:

 公共类公会
{
公共协会()
{
会员=新的List<会员> ();
}

公众诠释ID {搞定;组; }
公众诠释MEMBERID {搞定;组; }
公共虚拟会员LeaderMemberInfo {搞定;组; }
公共字符串名称{;组; }
公共虚拟目录<&成员GT;成员{搞定;组; }
}



用的映射:

 内部类GuildMapping:EntityTypeConfiguration<&公会GT; 
{
公共GuildMapping()
{
this.ToTable(行,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项目并添加映射到成员类到的DbContext的协会类是部分。 modelBuilder.Configurations.Add(新MemberMapping());(不知道这是最好的方式)



这导致这个错误:

  {成员与身份GuildProj.Data.EF.Guild_Members在元数据collection.\r\\\
Parameter名不存在:身份}

如何能我利用外键之间的这两个表交叉DbContexts和不同的数据库模式?



更新



我缩小了错误的原因。当我创建一个新的公会,我设置了公会会长的会员ID来MEMBERID。这工作得很好。但是,当我再尝试在领导的成员对象添加到会员的协会的名单(会员),这是什么原因造成的错误。



更新2



下面是如何创建的背景下,行业协会类是在代码中。(如要求由侯赛因·哈利勒)

 公共类FSEntities:的DbContext 
{
公共FSEntities()
{
this.Configuration.LazyLoadingEnabled = FALSE;
Database.SetInitializer< FSEntities>(NULL);
}

公共FSEntities(字符串的connectionString)
:底座(的connectionString)
{
}

保护覆盖无效OnModelCreating (DbModelBuilder模型构建器)
{
modelBuilder.Configurations.Add(新GuildMapping());
modelBuilder.Configurations.Add(新KeyValueMappings());
modelBuilder.Configurations.Add(新LocaleMappings());

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

公共DbSet<&公会GT;公会{搞定;组; }
公共DbSet<键值>键值来获取{;组; }
公共DbSet<区域设置>语言环境{搞定;组; }
}

这是怎么了保存在回购:



 公共异步任务CreateGuildAsync(公会公会)
{
使用(VAR上下文=新FSEntities(_ConnectionString))
{
context.Entry(guild.Members).STATE = EntityState.Unchanged;
context.Entry(公会).STATE = EntityState.Added;
等待context.SaveChangesAsync();
}
}



最后决议



所以,我不得不映射添加到成员角色权限中的DbContext包含协会。我不得不添加角色和权限,因为会员有列表<的角色和GT;角色,每个角色有列表<&许可GT;权限



这让我更接近的解决方案。我仍然得到错误,如:

  {成员与身份GuildProj.Data.EF.Member_Roles'不存在中元数据collection.\r\\\
Parameter名称:认同}

在这里,当你拉会员从会话,你得到的东西是这样的:

  System.Data这.Entity.DynamicProxies.Member_FF4FDE3888B129E1538B25850A445893D7C49F878D3CD40103BA1A4813EB514C 

实体框架似乎不玩这个好。为什么?我不知道,但我认为这是因为ContextM创建会员的代理,并通过克隆会员到一个新的成员对象,ContextM不再有关联。这一点,我想,让ContextG自由地使用新的成员对象。我试着设置ProxyCreationEnabled =我DbContexts假的,但成员对象被拉出会议被保存类型System.Data.Entity.DynamicProxies.Member的。



所以,我所做的就是:

 会员成员=新会员((会员)会话[Constants.UserSession]); 



我不得不克隆每个角色,每权限以及内部各自的构造函数。



这让我对那里的方式99%。我不得不改变我的回购,以及如何我节省了协会对象。

  context.Entry(guild.LeaderMemberInfo).STATE = EntityState.Unchanged; 
的foreach(VAR成员guild.Members)
{
context.Entry(会员).STATE = EntityState.Unchanged;
}
context.Entry(公会).STATE = EntityState.Added;
等待context.SaveChangesAsync();


解决方案

这是工作的代码:



在组装M:

 公共类会员
{
酒店的公共INT标识{搞定;组; }
公共字符串名称{;组; }
}

公共类MemberMapping:EntityTypeConfiguration<&成员GT;
{
公共MemberMapping()
{
this.HasKey(M = GT; m.Id);
this.Property(M => m.Name).IsRequired();
}
}

在assembyG:




  • 协会

  • 公会映射,尽管 WillCascadeOnDelete(假) LeaderMemberInfo 映射。

  • modelBuilder.Configurations.Add(新GuildMapping()); modelBuilder.Configurations.Add(新MemberMapping( ));



代码:

  VAR M =新成员{名称=M1}; 
变种流明=新成员{名称=领头羊};
变种G =新的公会{名称=G1};
g.LeaderMemberInfo = LM;
g.Members.Add(LM);
g.Members.Add(米);
c.Set<公会方式>()加(G);
c.SaveChanges();



执行的SQL:



<预类=郎-sql prettyprint-覆盖> INSERT [DBO]。[会员]([名])
值(@ 0)
选择[ID]
FROM [DBO ] [会员]
,其中@@ ROWCOUNT过夜。; 0 AND [ID] = SCOPE_IDENTITY()
- @ 0:'领导者'(类型=字符串,大小= -1)

INSERT [DBO] [公会]([MEMBERID ],[名])
值(@ 0,@ 1)
选择[ID]
从[DBO] [公会]
,其中@@ ROWCOUNT过夜。; 0 AND [ID] = SCOPE_IDENTITY()
- @ 0:1(类型= Int32)已
- @ 1:G1(类型=字符串,大小= -1)

INSERT [DBO] [GuildsMembers]([GuildID],[MEMBERID])
值(@ 0,@ 1)
- @ 0:1(类型= INT32)
- @ 1:1(类型= INT32)

INSERT [DBO] [会员]([名])
值(@ 0)
选择[ID]
从[DBO] [会员]
,其中@@ ROWCOUNT过夜。; 0 AND [ID] = SCOPE_IDENTITY()
- @ 0:M1(类型=字符串,大小= -1)

INSERT [DBO] [GuildsMembers]([GuildID ],[MEMBERID])
值(@ 0,@ 1)
- @ 0:1(类型= Int32)已
- @ 1:'2'(类型= INT32)

这关联现有的对象时也适用。






有关更一般的情况下,原来的答复:



您不能在不同的上下文类型的组合成一个对象图。这意味着,你不能这样做从类似

 在context.As 
JOIN B在context.Bs对...

......因为总有应该创造整个SQL查询一个环境,所以它应该有所有必需的映射信息。



您可以在同一类型但即使从不同的组件注册成两个不同的上下文。所以,你可以映射成员协会的上下文中装配,姑且称之为 contextG ,但只有当




  1. 成员不参考其他类型的的在 contextG 映射。这可能意味着在会员的导航属性必须明确忽略。

  2. 成员不能引用类型 contextG ,因为这些类型不是会员的一部分上下文。



如果任何这些条件不能得到满足,你可以做的就是创建一个新的成员协会的组装和在上下文注册其映射。也许你想使用不同的名称,以避免歧义,但是这大约是唯一的选择离开。


So, I have two main objects, Member and Guild. One Member can own a Guild and one Guild can have multiple Members.

I have the Members class in a separate DbContext and separate class library. I plan to reuse this class library in multiple projects and to help differentiate, I set the database schema to be "acc". I have tested this library extensively and can add, delete, and update Members in the acc.Members table.

The Guild class is as such:

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; }
}

with a mapping of:

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

But, when I try to create a new Guild, it says that there is no dbo.Members.

I got reference to the Member's EF project and added the mapping to the Members class to the DbContext that the Guild class is a part of. modelBuilder.Configurations.Add(new MemberMapping()); (Not sure if that is the best way.)

This resulted with this error:

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

How can I utilize the foreign key between these two tables cross DbContexts and with different database schemas?

UPDATE

I narrowed down the cause of the error. When I create a new guild, I set the guild leader's Member ID to MemberID. This works fine. But, when I then try to add that leader's Member object to the Guild's List of Members (Members), that's what causes the error.

UPDATE 2

Here is the code of how I create the Context that the Guild class is in. (As requested by Hussein Khalil)

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; }
}

This is how I am saving it in the repo:

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

FINAL RESOLUTION

So, I had to add mappings to Member, Role, and Permission in DbContext that contained Guild. I had to add Role and Permission because Member had List<Role> Roles and each Role had List<Permission> Permissions.

This got me closer to the solution. I was still getting errors like:

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

Here, when you pull Member from the Session, you get something like this:

System.Data.Entity.DynamicProxies.Member_FF4FDE3888B129E1538B25850A445893D7C49F878D3CD40103BA1A4813EB514C

Entity Framework does not seem to play well with this. Why? I am not sure, but I think it is because ContextM creates a proxy of Member and by cloning the Member into a new Member object, ContextM no longer has association. This, I think, allows ContextG to use the new Member object freely. I tried setting ProxyCreationEnabled = false in my DbContexts, but the Member object being pulled out of Session kept being of type System.Data.Entity.DynamicProxies.Member.

So, what I did was:

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

I had to clone each Role and each Permission as well inside their respective constructors.

This got me 99% of the way there. I had to alter my repo and how I was saving the Guild object.

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

解决方案

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.

这篇关于不同的DbContext和不同模式之间的实体框架的关系的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆