Как вставить данные в таблицу сопоставления FluentAPI
У меня есть таблица A, таблица B и AB (таблица сопоставления)
public class A
{
public int AID{ get; set; }
[JsonIgnore]
public virtual ICollection<B> Bs { get; set; }
}
В
public class B
{
public int BID { get; set; }
[JsonIgnore]
public virtual ICollection<A> As { get; set; }
}
ApplicationDbContext
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<B>()
.HasMany(s => s.As)
.WithMany(c => c.Bs)
.Map(cs =>
{
cs.MapLeftKey("AID");
cs.MapRightKey("BID");
cs.ToTable("AB");
});
}
Теперь все отлично, но как мне вставить эту таблицу сопоставления AB?
Если я пытаюсь создать AB, как показано ниже, он генерирует две таблицы, AB и AB1 с тем же именем столбца и все.
public class AB
{
public int ABID { get; set; }
public string AID { get; set; }
public int BID { get; set; }
}
- Так есть ли способ сделать CRUD в таблице сопоставления FluentAPI?
- Если нет, то можно ли заставить FluentAPI отображать из существующей таблицы? В этом случае я буду вручную управлять Employee и буду изменять код отображения для использования существующей таблицы.
Я не могу найти ни одного решения.
2 ответа
Изменить: Поскольку вопрос был изменен, я пишу более подробный ответ. Ответ на ваш вопрос остается прежним, однако:
Теперь все отлично, но как мне вставить эту таблицу сопоставления AB?
Вы нет!
Это именно то, что хорошо в EF. Вместо того, чтобы самостоятельно управлять таблицей ссылок, теперь вы просто получаете нужный объект. Итак, если вы хотите добавить ссылку между A
а также B
все, что вам нужно сделать, это добавить B
к Bs
коллекция на что A
, Вы никогда не вставляете прямо в AB
стол, потому что кого это волнует? Эта таблица есть, поэтому мы можем иметь отношения между различными A
с и B
s, вот и все. Итак, Entity Framework создаст таблицу для собственного использования, но не представит ее вам, потому что EF работает не так: вы работаете со своими объектами и позволяете EF обрабатывать базу данных.
Вот почему, когда вы пытаетесь определить таблицу самостоятельно, она создает две: она уже создает таблицу с именем AB, но вы запрашиваете другую. У него не может быть точно такого же имени, поэтому он добавляет "1" в конце. Поскольку вы уже использовали FluentAPI для определения приложения, позвольте EF позаботиться о том, как реализовать отображение: все, что вам нужно, это то, что у вас теперь есть способ получить A
с набором B
с или наоборот.
Так как это по-прежнему путает с именами "A" и "B", ниже Program
класс для консольного приложения, которое проиллюстрирует это; все, что вам нужно сделать, это запустить новое консольное приложение, заменить класс Program на него, установить пакет Entity Framework и запустить enable-migrations -enableautomaticmigrations -force
, Я рекомендую вам использовать это, чтобы добавить некоторые объекты и связать их, а затем взглянуть на вашу базу данных: вы увидите таблицу "AB" с записями, которые были добавлены. Это может помочь объяснить это лучше.
class Program
{
static bool quit = false;
static void Main(string[] args)
{
string s = "Please select an option:" +
"\n1: Insert an A" +
"\n2: Insert a B" +
"\n3: Add a B to an A" +
"\n4: Add an A to a B" +
"\n5: Print all As" +
"\n6: Print all Bs" +
"\n7: Print AB Table" +
"\nx: Quit.";
while (!quit)
{
Console.WriteLine();
Console.WriteLine(s);
var k = Console.ReadKey();
DoStuff(k);
}
}
private static void DoStuff(ConsoleKeyInfo i)
{
switch (i.Key)
{
case ConsoleKey.D1:
//add an A
AddA(GetName());
break;
case ConsoleKey.D2:
//add a B
AddB(GetName());
break;
case ConsoleKey.D3:
// link a B to an A
LinkB(GetBtoLink(),GetAtoLink());
break;
case ConsoleKey.D4:
//link an A to an B
LinkA(GetAtoLink(), GetBtoLink());
break;
case ConsoleKey.D5:
// print As
WriteA();
break;
case ConsoleKey.D6:
//print Bs
WriteB();
break;
case ConsoleKey.D7:
// print AB
WriteAB();
break;
case ConsoleKey.X:
quit = true;
break;
}
}
private static int GetAtoLink()
{
string x;
int z;
do
{
Console.Clear();
Console.WriteLine("Please enter the ID of the A you want to use and then press enter.");
WriteA();
x = Console.ReadLine();
} while (!int.TryParse(x, out z));
return z;
}
private static int GetBtoLink()
{
string x;
int z;
do
{
Console.Clear();
Console.WriteLine("Please enter the ID of the B you want to use and then press enter.");
WriteB();
x = Console.ReadLine();
} while (!int.TryParse(x, out z));
return z;
}
private static void WriteB()
{
Console.WriteLine("{0,10}{1,15}", "ID", "Name");
using (var db = new Context())
{
foreach (var a in db.Bs)
{
Console.WriteLine("{0,10}{1,15}", a.BID, a.Name);
}
}
}
private static void WriteA()
{
Console.WriteLine("{0,10}{1,15}", "ID", "Name");
using (var db = new Context())
{
foreach (var a in db.As)
{
Console.WriteLine("{0,10}{1,15}", a.AID, a.Name);
}
}
}
private static void WriteAB()
{
Console.WriteLine("{0,10}{1,10}", "AID", "BID");
using (var db = new Context())
{
// this is the only way we need to do this, because it's many to many,
// if an A is linked to a B, then that B is by definition linked to that A as well.
foreach (var a in db.As)
{
foreach (var b in a.Bs)
{
Console.WriteLine("{0,10}{1,10}", a.AID, b.BID);
}
}
}
}
private static void LinkB(int bToUse, int aToUse)
{
using (var db = new Context())
{
var a = db.As.First(x => x.AID == aToUse);
var b = db.Bs.First(y => y.BID == bToUse);
a.Bs.Add(b);
db.SaveChanges();
}
}
private static void LinkA(int aToUse, int bToUse)
{
using (var db = new Context())
{
var a = db.As.First(x => x.AID == aToUse);
var b = db.Bs.First(y => y.BID == bToUse);
b.As.Add(a);
db.SaveChanges();
}
}
private static string GetName()
{
Console.WriteLine("Please enter a name");
return Console.ReadLine();
}
private static void AddA(string input)
{
using (var db = new Context())
{
db.As.Add(new A {Name = input});
db.SaveChanges();
}
}
private static void AddB(string input)
{
using (var db = new Context())
{
db.Bs.Add(new B { Name = input });
db.SaveChanges();
}
}
}
public class A
{
public int AID { get; set; }
public string Name { get; set; }
public virtual ICollection<B> Bs { get; set; }
}
public class B
{
public int BID { get; set; }
public string Name { get; set; }
public virtual ICollection<A> As { get; set; }
}
public class Context : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<B>()
.HasMany(s => s.As)
.WithMany(c => c.Bs)
.Map(cs =>
{
cs.MapLeftKey("AID");
cs.MapRightKey("BID");
cs.ToTable("AB");
});
}
public DbSet<A> As { get; set; }
public DbSet<B> Bs { get; set; }
}
Старый ответ: Вы определили ICollection<ApplicationUser>
называется Employees
в Company
и сопоставлены с ним с помощью FluentAPI. Это создает таблицу с именем "Сотрудники", как и ожидалось. Вам не нужно создавать другой класс с именем Employees; Что касается Entity Framework, вы уже сказали ему создать таблицу с именем Employees. Вот почему я думаю, что шаг, который вы пропускаете, определяет ваш DbSet<>
,
Использование вашего кода и запуск Add-Migration
это определение, которое я получаю для таблицы Employees:
CreateTable(
"dbo.Employees",
c => new
{
UserID = c.Int(nullable: false),
CompanyID = c.Int(nullable: false),
})
.PrimaryKey(t => new { t.UserID, t.CompanyID })
.ForeignKey("dbo.ApplicationUsers", t => t.UserID, cascadeDelete: true)
.ForeignKey("dbo.Companies", t => t.CompanyID, cascadeDelete: true)
.Index(t => t.UserID)
.Index(t => t.CompanyID);
Который, кажется, соотносится с тем, что вы хотели.
Чтобы закончить, добавьте (если вы этого еще не сделали) это к вашему ApplicationDbContext
файл:
public DbSet<ApplicationUser> Employees;
public DbSet<Company> Companies;
Затем, чтобы добавить сотрудника, вы создаете новый ApplicationUser
и добавить это как
ApplicationUser user = new ApplicationUser();
// do whatever here to give it the right data
ApplicationDbContext ctx = new ApplicationDbContext();
ctx.Employees.Add(user);
Employees
Сам стол, с которым вам никогда не придется взаимодействовать.
EF справится с тем, что вам не нужно напрямую вставлять в таблицу сопоставления, взгляните на этот пример, который у меня есть в моем проекте:
public class Organization : Entity<int>
{
public string Name { get; set; }
public string Address { get; set; }
public string MainContact { get; set; }
public string Phone { get; set; }
public string Website { get; set; }
//navigation property
public virtual ICollection<DevelopmentalGoal> DevelopmentalGoals { get; set; }
public virtual ICollection<ServiceActivity> ServiceActivities { get; set; }
}
public class DevelopmentalGoal : Entity<int>
{
public string Name { get; set; }
public string Icon { get; set; }
//navigation property
public virtual ICollection<Organization> Organizations { get; set; }
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Organization>().ToTable("Organization", "ServiceLearning")
.HasKey(t => t.ID);
modelBuilder.Entity<DevelopmentalGoal>().ToTable("DevelopmentalGoal", "ServiceLearning")
.HasKey(t => t.ID);
modelBuilder.Entity<Organization>()
.HasMany(t => t.DevelopmentalGoals)
.WithMany(t=> t.Organizations)
.Map(m =>
{
m.ToTable("OrganizationDevelopmentalGoal", "ServiceLearning");
m.MapLeftKey("OrganizationID");
m.MapRightKey("DevelopmentalGoalID");
});
}
public int SaveOrganization(OrganizationViewModel viewModel, IUserContext currentUser)
{
Organization organization;
{
if (viewModel.ID == 0)
{
organization = ObjectMapper.MapTo<Organization>(viewModel);
_context.Set<Organization>().Add(organization);
}
else
{
organization = _context.Set<Organization>()
.SingleOrDefault(t =>
t.ID == viewModel.ID
);
organization.Name = viewModel.Name;
organization.Address = viewModel.Address;
organization.MainContact = viewModel.MainContact;
organization.Phone = viewModel.Phone;
organization.Website = viewModel.Website;
UpdateOrganizationDevelopmentalGoals(organization, viewModel);
}
try
{
CommitChanges();
}
catch (DbUpdateException ex)
{
if (ex.IsDuplicateException())
throw new KeystoneDuplicateException("A Organization with the same name already exists.");
throw ex;
}
}
return organization.ID;
}
private void UpdateOrganizationDevelopmentalGoals(Organization organization, OrganizationViewModel viewModel)
{
var originalIdList = organization.DevelopmentalGoals.Select(d => d.ID).Distinct().ToList();
var modifiedIdList = viewModel.DevelopmentalGoal.Where(d => d.Selected == true).Select(d => d.ID).Distinct().ToList();
//Remove deleted Developmetal Goals.
foreach (var id in originalIdList.Except(modifiedIdList))
organization.DevelopmentalGoals.Remove(organization.DevelopmentalGoals.Single(d => d.ID == id));
//Add new Developmetal Goals.
foreach (var id in modifiedIdList.Except(originalIdList))
{
//Add director relationship without having to load entity.
var d = new DevelopmentalGoal { ID = id };
_context.Set<DevelopmentalGoal>().Attach(d);
organization.DevelopmentalGoals.Add(d);
}
}
Как вы можете видеть в методе UpdateOrganizationDevelopmentalGoals, я не вставляю и не удаляю данные из таблицы сопоставления напрямую, я вставляю и удаляю их изorganization.DevelopmentalGoals, и, поскольку я уже определил таблицу сопоставления в свободном API в «OnModelCreating», тогда EF знает как управлять отношениями.