Как вставить данные в таблицу сопоставления 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; }
}
  1. Так есть ли способ сделать CRUD в таблице сопоставления FluentAPI?
  2. Если нет, то можно ли заставить FluentAPI отображать из существующей таблицы? В этом случае я буду вручную управлять Employee и буду изменять код отображения для использования существующей таблицы.

Я не могу найти ни одного решения.

2 ответа

Изменить: Поскольку вопрос был изменен, я пишу более подробный ответ. Ответ на ваш вопрос остается прежним, однако:

Теперь все отлично, но как мне вставить эту таблицу сопоставления AB?

Вы нет!

Это именно то, что хорошо в EF. Вместо того, чтобы самостоятельно управлять таблицей ссылок, теперь вы просто получаете нужный объект. Итак, если вы хотите добавить ссылку между A а также Bвсе, что вам нужно сделать, это добавить B к Bs коллекция на что A, Вы никогда не вставляете прямо в AB стол, потому что кого это волнует? Эта таблица есть, поэтому мы можем иметь отношения между различными Aс и Bs, вот и все. Итак, 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 знает как управлять отношениями.

Другие вопросы по тегам