Отношения Entity Framework между различными DbContext и различными схемами
у меня есть класс Members в отдельном DbContext и отдельной библиотеке классов. Я планирую повторно использовать эту библиотеку классов в нескольких проектах и, чтобы помочь дифференцировать, я установил схему базы данных как "acc". Я тщательно протестировал эту библиотеку и могу добавлять, удалять и обновлять членов в acc.Таблица элементов.
класс Гильдии как такие:
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.Члены.
я получил ссылку на проект EF члена и добавил отображение в класс Members в DbContext, частью которого является класс Guild. modelBuilder.Configurations.Add(new MemberMapping());
(Не уверен, что это лучший способ.)
это привело к этой ошибке:
{"The member with identity 'GuildProj.Data.EF.Guild_Members' does not exist in the metadata collection.rnParameter name: identity"}
как я могу использовать внешний ключ между этими двумя таблицы пересекают DbContexts и с различными схемами базы данных?
обновление
я сузил причину ошибки. Когда я создаю новую гильдию, я устанавливаю идентификатор члена лидера гильдии в 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();
}
}
ОКОНЧАТЕЛЬНОЕ РАЗРЕШЕНИЕ
Итак, мне пришлось добавить сопоставления в Member
, Role
и Permission
в DbContext, который содержится Guild
. Мне пришлось добавить роль и разрешение, потому что у члена было List<Role> Roles
и каждая роль была List<Permission> Permissions
.
это приблизило меня к решению. Я все еще получаю ошибки например:
{"The member with identity 'GuildProj.Data.EF.Member_Roles' does not exist in the metadata collection.rnParameter name: identity"}
здесь, когда вы вытаскиваете член из Session
, вы получаете что-то вроде этого:
System.Data.Entity.DynamicProxies.Member_FF4FDE3888B129E1538B25850A445893D7C49F878D3CD40103BA1A4813EB514C
Entity Framework, похоже, не очень хорошо с этим справляется. Почему? Я не уверен, но я думаю, что это потому, что ContextM создает прокси-сервер члена и, клонируя член в новый объект-член, ContextM больше не имеет ассоциации. Это, я думаю, позволяет ContextG свободно использовать новый объект-член. Я попытался установить ProxyCreationEnabled = false в моих DbContexts, но объект-член, извлеченный из сеанса, продолжал иметь тип System.Данные.Сущность.DynamicProxies.Член.
Итак, то, что я сделал, было:
Member member = new Member((Member)Session[Constants.UserSession]);
мне пришлось клонировать каждого Role
и друг Permission
а также внутри их соответствующих конструкторов.
это дало мне 99% пути туда. Мне пришлось изменить свое РЕПО и как я спасал
4 ответов
это рабочий код:
в сборке "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();
}
}
в сборке "G":
- код
Guild
класс - код
Guild
сопоставление, хотя и сWillCascadeOnDelete(false)
наLeaderMemberInfo
сопоставление. -
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-запрос, поэтому он должен иметь всю необходимую информацию о сопоставлении.
вы можете зарегистрировать один и тот же тип в двух разных контекстах, даже из разных сборок. Так что вы могли бы map Member
в контексте Guild
's ассамблеи, давайте назовите это contextG
, но только если
-
Member
не относится к другим типам, что не сопоставленные вcontextG
. Это может означать, что свойства навигации вMember
должно игнорироваться явно. -
Member
не может ссылаться на типы вcontextG
, потому что эти типы не являются частьюMember
's контекст.
если любое из этих условий не может быть выполнено, лучшее, что вы можете сделать, это создать новый Member
класс Guild
'ы сборка и регистрация ее отображения в контексте. Возможно, вы хотите использовать другое имя, чтобы предотвратить двусмысленность, но это единственная оставшаяся альтернатива.
Я обнаружил, что когда у меня проблемы с сущностью и построением отношений, это часто потому, что я иду против потока структуры или пытаюсь построить отношения или абстракции, которые технически не сформированы. Здесь я бы предложил сделать быстрый шаг назад и проанализировать несколько вещей, прежде чем углубляться в эту конкретную проблему.
во-первых, мне интересно, почему вы используете другую схему здесь для того, что, вероятно, одно приложение доступ к графу объектов. Несколько схем могут быть полезны в некотором контексте, но я думаю, что Брент Озар делает очень заметный момент в этом статьи. Учитывая эту информацию, я склонен сначала предложить объединить несколько схем в одну и перейти к использованию одного контекста БД для базы данных.
следующее, что нужно решить, это график объектов. По крайней мере, для меня самая большая борьба с моделированием данных-это когда я сначала не понимаю, какие вопросы приложение имеет базу данных. Я имею в виду, что сначала выясните, какие данные нужны приложению в его различных контекстах, а затем посмотрите, как оптимизировать эти структуры данных для производительности в реляционном контексте. Посмотрим, как это можно сделать ...
исходя из вашей модели я вижу, что у нас есть несколько ключевых терминов/объектов в домене:
- сбор Гильдии
- собрания участников
- коллекция лидеров гильдий.
кроме того, у нас есть некоторые правила, которые должны быть реализованы:
- Гильдия может иметь 1 лидера (и, возможно, более 1?)
- лидер Гильдии должен быть членом
- Гильдия имеет список из 0 или более членов
- член может принадлежать к гильдии (и, возможно, более 1?)
Итак, учитывая эту информацию, давайте рассмотрим, какие вопросы может иметь ваше приложение этой модели данных. Я приложение может:
- найдите элемент и посмотрите его свойства
- посмотрите на члена и увидеть их свойства, и что они являются лидером гильдии
- посмотрите гильдию и увидеть список всех ее членов
- посмотреть в гильдию и увидеть список лидеров гильдии
- посмотрите список всех лидеров гильдий
хорошо, теперь мы можем перейти к латунным гвоздям, как говорится...
в использование таблицы join между гильдиями и членами является оптимальным в этом случае. Это даст вам возможность иметь членов в нескольких гильдиях или без гильдий и обеспечить низкую стратегию обновления блокировки-так хорошо!
переход на лидеров гильдий есть несколько вариантов, которые могут иметь смысл. Даже при том, что, возможно, никогда не будет случая для сержантов гильдии, я думаю, что имеет смысл рассмотреть новую сущность, называемую лидером Гильдии. Этот подход допускает несколько вариантов. Вы можете кэш в приложение список лидеров гильдий идентификаторы, так что вместо того, чтобы сделать поездку в дБ разрешаю гильдии действия руководителя вы можете нажмите локального кэша приложений, который имеет только лидер списка идентификаторов и не вся руководителем объекта; и наоборот, можно попасть в список лидеров гильдии и в зависимости от запроса направление, вы можете нажать кластеризованных индексов на основных сущностей или удобной в обслуживании промежуточного индекса на совместную кооперацию.
Как я заметил в начале этого пути, чтобы долго "ответ", когда я сталкиваюсь с такими проблемами, как ваши, это обычно потому, что я иду против зерна сущности. Я призываю вас пересмотреть свою модель данных и как с более низким подходом трения - потерять множественную схему и добавить промежуточный объект guild_leader. Ура!
если вы явно не скажете, что Member
организация должна быть сопоставлена с acc.Members
, EF будет ожидать, что он будет в dbo
- схемы Members
таблица. Для этого вам нужно предоставить либо EntityTypeConfiguration
для этого типа или комментировать его с System.ComponentModel.DataAnnotations.Schema.TableAttribute
как [Table("acc.Members")]
я отвечаю на ваш обновленный вопрос :
попробуйте использовать эту строку перед обновлением контекста
context.Entry(Guild.Members).State = Entity.EntityState.Unchanged
это решит ошибку, которую вы