EF Code First: от многих до многих. Ссылка на отношения с дополнительными полями в таблице объединений [duplicate]

Создайте службу переднего плана с флагом START_STICKY.

@Override
public int onStartCommand(Intent startIntent, int flags, int startId) {
   if (startIntent != null) {
       String action = startIntent.getAction();
       String command = startIntent.getStringExtra(CMD_NAME);
       if (ACTION_CMD.equals(action)) {
           if (CMD_PAUSE.equals(command)) {
               if (mPlayback != null && mPlayback.isPlaying()) {
                   handlePauseRequest();
               }
           } else if (CMD_PLAY.equals(command)) {
               ArrayList queue = new ArrayList<>();
               for (Parcelable input : startIntent.getParcelableArrayListExtra(ARG_QUEUE)) {
                   queue.add((Track) Parcels.unwrap(input));
               }
               int index = startIntent.getIntExtra(ARG_INDEX, 0);
               playWithQueue(queue, index);
           }
       }
   }

   return START_STICKY;
}

Затем это можно вызвать из любой активности для воспроизведения музыки

Intent intent = new Intent(MusicService.ACTION_CMD, fileUrlToPlay, activity, MusicService::class.java)
intent.putParcelableArrayListExtra(MusicService.ARG_QUEUE, tracks)
intent.putExtra(MusicService.ARG_INDEX, position)
intent.putExtra(MusicService.CMD_NAME, MusicService.CMD_PLAY)
activity.startService(intent)

. Вы можете привязываться к службы с помощью bindService и сделать паузу / остановку службы из соответствующих методов жизненного цикла активности.

Вот хороший учебник о Воспроизведение музыки в фоновом режиме на Android

254
задан Peter Mortensen 12 February 2016 в 23:33
поделиться

6 ответов

Невозможно создать отношения «многие ко многим» с персонализированной таблицей соединений. В отношениях «многие ко многим» EF управляет таблицей соединений внутри и снаружи. Это таблица без класса Entity в вашей модели. Чтобы работать с такой таблицей соединений с дополнительными свойствами, вам нужно будет создать фактически два отношения «один ко многим». Это может выглядеть так:

public class Member
{
    public int MemberID { get; set; }

    public string FirstName { get; set; }
    public string LastName { get; set; }

    public virtual ICollection<MemberComment> MemberComments { get; set; }
}

public class Comment
{
    public int CommentID { get; set; }
    public string Message { get; set; }

    public virtual ICollection<MemberComment> MemberComments { get; set; }
}

public class MemberComment
{
    [Key, Column(Order = 0)]
    public int MemberID { get; set; }
    [Key, Column(Order = 1)]
    public int CommentID { get; set; }

    public virtual Member Member { get; set; }
    public virtual Comment Comment { get; set; }

    public int Something { get; set; }
    public string SomethingElse { get; set; }
}

Если теперь вы хотите найти все комментарии членов с LastName = «Смит», например, вы можете написать такой запрос:

var commentsOfMembers = context.Members
    .Where(m => m.LastName == "Smith")
    .SelectMany(m => m.MemberComments.Select(mc => mc.Comment))
    .ToList();

... или ...

var commentsOfMembers = context.MemberComments
    .Where(mc => mc.Member.LastName == "Smith")
    .Select(mc => mc.Comment)
    .ToList();

Или создать список членов с именем «Смит» (мы предполагаем, что их более одного) вместе с их комментариями вы можете использовать project:

var membersWithComments = context.Members
    .Where(m => m.LastName == "Smith")
    .Select(m => new
    {
        Member = m,
        Comments = m.MemberComments.Select(mc => mc.Comment)
    })
    .ToList();

Если вы хотите найти все комментарии члена с MemberId = 1:

var commentsOfMember = context.MemberComments
    .Where(mc => mc.MemberId == 1)
    .Select(mc => mc.Comment)
    .ToList();

Теперь вы также можете фильтровать по свойствам в вашем соединении table (что было бы невозможно во многих отношениях), например: Отфильтровать все комментарии элемента 1, которые имеют свойство 99 в Something:

var filteredCommentsOfMember = context.MemberComments
    .Where(mc => mc.MemberId == 1 && mc.Something == 99)
    .Select(mc => mc.Comment)
    .ToList();

Из-за ленивой загрузки все может стать проще. Если у вас есть загруженный Member, вы можете получить комментарии без эксплицитного запроса:

var commentsOfMember = member.MemberComments.Select(mc => mc.Comment);

Я полагаю, что ленивая загрузка будет автоматически получать комментарии за кулисами.

Изменить

Просто для удовольствия еще несколько примеров, как добавить сущности и отношения и как их удалить в этой модели:

1) Создайте один участник и два комментария этого участника :

var member1 = new Member { FirstName = "Pete" };
var comment1 = new Comment { Message = "Good morning!" };
var comment2 = new Comment { Message = "Good evening!" };
var memberComment1 = new MemberComment { Member = member1, Comment = comment1,
                                         Something = 101 };
var memberComment2 = new MemberComment { Member = member1, Comment = comment2,
                                         Something = 102 };

context.MemberComments.Add(memberComment1); // will also add member1 and comment1
context.MemberComments.Add(memberComment2); // will also add comment2

context.SaveChanges();

2) Добавить третий комментарий участника1:

var member1 = context.Members.Where(m => m.FirstName == "Pete")
    .SingleOrDefault();
if (member1 != null)
{
    var comment3 = new Comment { Message = "Good night!" };
    var memberComment3 = new MemberComment { Member = member1,
                                             Comment = comment3,
                                             Something = 103 };

    context.MemberComments.Add(memberComment3); // will also add comment3
    context.SaveChanges();
}

3) Создать новый член и связать его с существующим комментарием2:

var comment2 = context.Comments.Where(c => c.Message == "Good evening!")
    .SingleOrDefault();
if (comment2 != null)
{
    var member2 = new Member { FirstName = "Paul" };
    var memberComment4 = new MemberComment { Member = member2,
                                             Comment = comment2,
                                             Something = 201 };

    context.MemberComments.Add(memberComment4);
    context.SaveChanges();
}

4) Создайте связь между существующим элементом2 и комментарием 3:

var member2 = context.Members.Where(m => m.FirstName == "Paul")
    .SingleOrDefault();
var comment3 = context.Comments.Where(c => c.Message == "Good night!")
    .SingleOrDefault();
if (member2 != null && comment3 != null)
{
    var memberComment5 = new MemberComment { Member = member2,
                                             Comment = comment3,
                                             Something = 202 };

    context.MemberComments.Add(memberComment5);
    context.SaveChanges();
}

5) Удалите это отношение еще раз:

var memberComment5 = context.MemberComments
    .Where(mc => mc.Member.FirstName == "Paul"
        && mc.Comment.Message == "Good night!")
    .SingleOrDefault();
if (memberComment5 != null)
{
    context.MemberComments.Remove(memberComment5);
    context.SaveChanges();
}

6) Удалите член1 и все его отношения к Комментарии:

var member1 = context.Members.Where(m => m.FirstName == "Pete")
    .SingleOrDefault();
if (member1 != null)
{
    context.Members.Remove(member1);
    context.SaveChanges();
}

Это также удаляет отношения в MemberComments, потому что отношения «один ко многим» между Member и MemberComments и между Comment и MemberComments настроены с каскадным удалить по соглашению. И это происходит потому, что MemberId и CommentId в MemberComment обнаружены как свойства внешнего ключа для свойств навигации Member и Comment, и поскольку свойства FK имеют тип, не имеющий значения null int, отношение , который, наконец, вызывает каскадное удаление-установку. Я думаю, что имеет смысл в этой модели.

462
ответ дан Slauma 15 August 2018 в 22:30
поделиться
  • 1
    @hgdean: Я spammed еще несколько примеров, извините, но это интересная модель, и вопросы о многих-ко-многим с дополнительными данными в таблице соединений происходят время от времени здесь. Теперь в следующий раз мне нужно что-то связать ... :) – Slauma 14 August 2011 в 14:08
  • 2
    @Esteban: нет переопределенного OnModelCreating. В этом примере используются только условные обозначения и аннотации данных. – Slauma 10 September 2012 в 22:08
  • 3
    @Simon_Weaver: Да, вам все равно нужно разоблачить & quot; настроенные таблицы соединений & quot; как сущности и не могут использовать отношения «многие ко многим». – Slauma 9 May 2013 в 11:20
  • 4
    Примечание: если вы используете этот подход без Fluent API, убедитесь, что вы проверили в своей базе данных, что у вас есть только составной ключ с столбцами MemberId и CommentId, а не дополнительный третий столбец Member_CommentId (или что-то в этом роде) - который означает, что у вас не было точных совпадающих имен между объектами для ваших ключей – Simon_Weaver 11 May 2013 в 06:17
  • 5
    @Simon_Weaver (или любой, кто может знать ответ) У меня похожая ситуация, но я хотел бы иметь «MemberCommentID». первичный ключ для этой таблицы, возможно ли это или нет? Я в настоящее время получаю исключение, пожалуйста, взгляните на мой вопрос, мне действительно нужна помощь ... stackoverflow.com/questions/26783934/… – duxfox-- 7 November 2014 в 01:26

Один из способов решить эту ошибку - поместить атрибут ForeignKey поверх свойства, которое вы хотите в качестве внешнего ключа, и добавить свойство навигации.

Примечание. В атрибуте ForeignKey между скобками и двойными кавычками, поместите имя класса, упомянутого таким образом.

enter image description here [/g0]

0
ответ дан aaron 15 August 2018 в 22:30
поделиться
  • 1
    Пожалуйста, добавьте минимальное объяснение в самом ответе, так как ссылка может оказаться недоступной в будущем. – n4m31ess_c0d3r 29 December 2017 в 19:01
  • 2
    Это должно быть имя свойства навигации , а не класса. – aaron 29 December 2017 в 19:10
[Д0] TLDR; (частично связанный с ошибкой редактора EF6 в EF6 / VS2012U5), если вы создаете модель из БД, и вы не можете увидеть атрибутную таблицу m: m: удалить две связанные таблицы -> Сохранить .edmx -> Сгенерировать / добавить из базы данных - > Сохранить.

Для тех, кто пришел сюда, интересно узнать, как получить отношение «многие ко многим» с столбцами атрибутов для отображения в файле EF .edmx (так как в настоящее время он не отображается и не рассматривается как набор навигационных свойств), И вы сгенерировали эти классы из своей таблицы базы данных (или, по-моему, сначала базы данных в MS lingo).

Удалите 2 таблицы (вопрос OP, член и комментарий ) в вашем .edmx и добавить их снова через «Сгенерировать модель из базы данных». (т. е. не пытайтесь обновить Visual Studio - удалить, сохранить, добавить, сохранить)

Затем он создаст третью таблицу в соответствии с тем, что предлагается здесь.

имеет значение в тех случаях, когда сначала добавляются отношения «чисто-многие-многие», а атрибуты создаются в БД позже.

Это не было сразу видно из этого потока / Googling. Так что просто поместите его там, так как это ссылка № 1 в Google, которая ищет проблему, но, прежде всего, из стороны БД.

0
ответ дан Andy S 15 August 2018 в 22:30
поделиться

@Esteban, код, который вы указали, прав, спасибо, но неполный, я его протестировал. В классе «UserEmail» отсутствуют свойства:

    public UserTest UserTest { get; set; }
    public EmailTest EmailTest { get; set; }

Я отправляю код, который я тестировал, если кто-то заинтересован. С уважением

using System.Data.Entity;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Web;

#region example2
public class UserTest
{
    public int UserTestID { get; set; }
    public string UserTestname { get; set; }
    public string Password { get; set; }

    public ICollection<UserTestEmailTest> UserTestEmailTests { get; set; }

    public static void DoSomeTest(ApplicationDbContext context)
    {

        for (int i = 0; i < 5; i++)
        {
            var user = context.UserTest.Add(new UserTest() { UserTestname = "Test" + i });
            var address = context.EmailTest.Add(new EmailTest() { Address = "address@" + i });
        }
        context.SaveChanges();

        foreach (var user in context.UserTest.Include(t => t.UserTestEmailTests))
        {
            foreach (var address in context.EmailTest)
            {
                user.UserTestEmailTests.Add(new UserTestEmailTest() { UserTest = user, EmailTest = address, n1 = user.UserTestID, n2 = address.EmailTestID });
            }
        }
        context.SaveChanges();
    }
}

public class EmailTest
{
    public int EmailTestID { get; set; }
    public string Address { get; set; }

    public ICollection<UserTestEmailTest> UserTestEmailTests { get; set; }
}

public class UserTestEmailTest
{
    public int UserTestID { get; set; }
    public UserTest UserTest { get; set; }
    public int EmailTestID { get; set; }
    public EmailTest EmailTest { get; set; }
    public int n1 { get; set; }
    public int n2 { get; set; }


    //Call this code from ApplicationDbContext.ConfigureMapping
    //and add this lines as well:
    //public System.Data.Entity.DbSet<yournamespace.UserTest> UserTest { get; set; }
    //public System.Data.Entity.DbSet<yournamespace.EmailTest> EmailTest { get; set; }
    internal static void RelateFluent(System.Data.Entity.DbModelBuilder builder)
    {
        // Primary keys
        builder.Entity<UserTest>().HasKey(q => q.UserTestID);
        builder.Entity<EmailTest>().HasKey(q => q.EmailTestID);

        builder.Entity<UserTestEmailTest>().HasKey(q =>
            new
            {
                q.UserTestID,
                q.EmailTestID
            });

        // Relationships
        builder.Entity<UserTestEmailTest>()
            .HasRequired(t => t.EmailTest)
            .WithMany(t => t.UserTestEmailTests)
            .HasForeignKey(t => t.EmailTestID);

        builder.Entity<UserTestEmailTest>()
            .HasRequired(t => t.UserTest)
            .WithMany(t => t.UserTestEmailTests)
            .HasForeignKey(t => t.UserTestID);
    }
}
#endregion
10
ответ дан LeonardoX 15 August 2018 в 22:30
поделиться

Я хочу предложить решение, в котором могут быть достигнуты оба варианта конфигурации «многие-ко-многим».

«catch» - нам нужно создать представление, которое нацелено на таблицу объединения, поскольку EF проверяет, что таблица схемы может отображаться не более одного раза за EntitySet.

ответ добавляет к тому, что уже было сказано в предыдущих ответах, и не отменяет ни один из этих подходов, он основывается на них.

Модель:

public class Member
{
    public int MemberID { get; set; }

    public string FirstName { get; set; }
    public string LastName { get; set; }

    public virtual ICollection<Comment> Comments { get; set; }
    public virtual ICollection<MemberCommentView> MemberComments { get; set; }
}

public class Comment
{
    public int CommentID { get; set; }
    public string Message { get; set; }

    public virtual ICollection<Member> Members { get; set; }
    public virtual ICollection<MemberCommentView> MemberComments { get; set; }
}

public class MemberCommentView
{
    public int MemberID { get; set; }
    public int CommentID { get; set; }
    public int Something { get; set; }
    public string SomethingElse { get; set; }

    public virtual Member Member { get; set; }
    public virtual Comment Comment { get; set; }
}

Конфигурация:

using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.ModelConfiguration;

public class MemberConfiguration : EntityTypeConfiguration<Member>
{
    public MemberConfiguration()
    {
        HasKey(x => x.MemberID);

        Property(x => x.MemberID).HasColumnType("int").IsRequired();
        Property(x => x.FirstName).HasColumnType("varchar(512)");
        Property(x => x.LastName).HasColumnType("varchar(512)")

        // configure many-to-many through internal EF EntitySet
        HasMany(s => s.Comments)
            .WithMany(c => c.Members)
            .Map(cs =>
            {
                cs.ToTable("MemberComment");
                cs.MapLeftKey("MemberID");
                cs.MapRightKey("CommentID");
            });
    }
}

public class CommentConfiguration : EntityTypeConfiguration<Comment>
{
    public CommentConfiguration()
    {
        HasKey(x => x.CommentID);

        Property(x => x.CommentID).HasColumnType("int").IsRequired();
        Property(x => x.Message).HasColumnType("varchar(max)");
    }
}

public class MemberCommentViewConfiguration : EntityTypeConfiguration<MemberCommentView>
{
    public MemberCommentViewConfiguration()
    {
        ToTable("MemberCommentView");
        HasKey(x => new { x.MemberID, x.CommentID });

        Property(x => x.MemberID).HasColumnType("int").IsRequired();
        Property(x => x.CommentID).HasColumnType("int").IsRequired();
        Property(x => x.Something).HasColumnType("int");
        Property(x => x.SomethingElse).HasColumnType("varchar(max)");

        // configure one-to-many targeting the Join Table view
        // making all of its properties available
        HasRequired(a => a.Member).WithMany(b => b.MemberComments);
        HasRequired(a => a.Comment).WithMany(b => b.MemberComments);
    }
}

Контекст:

using System.Data.Entity;

public class MyContext : DbContext
{
    public DbSet<Member> Members { get; set; }
    public DbSet<Comment> Comments { get; set; }
    public DbSet<MemberCommentView> MemberComments { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        modelBuilder.Configurations.Add(new MemberConfiguration());
        modelBuilder.Configurations.Add(new CommentConfiguration());
        modelBuilder.Configurations.Add(new MemberCommentViewConfiguration());

        OnModelCreatingPartial(modelBuilder);
     }
}

Из альбома Saluma (@ Saluma) answer

Если вы сейчас хотите найти все комментарии членов с LastName = «Смит», например, вы можете написать такой запрос:

Это все еще работает ...

var commentsOfMembers = context.Members
    .Where(m => m.LastName == "Smith")
    .SelectMany(m => m.MemberComments.Select(mc => mc.Comment))
    .ToList();

... но теперь также может быть ...

var commentsOfMembers = context.Members
    .Where(m => m.LastName == "Smith")
    .SelectMany(m => m.Comments)
    .ToList();

Или создать список членов с именем «Смит» (мы предполагаем, что их более одного) вместе с их комментариями можно использовать проекцию:

Это все еще работает ...

var membersWithComments = context.Members
    .Where(m => m.LastName == "Smith")
    .Select(m => new
    {
        Member = m,
        Comments = m.MemberComments.Select(mc => mc.Comment)
    })
    .ToList();

... но теперь также может быть ...

var membersWithComments = context.Members
    .Where(m => m.LastName == "Smith")
    .Select(m => new
    {
        Member = m,
        m.Comments
    })
        .ToList();

Если вы хотите удалить комментарий от пользователя

var comment = ... // assume comment from member John Smith
var member = ... // assume member John Smith

member.Comments.Remove(comment);

Если вы хотите Include() комментарии участника

var member = context.Members
    .Where(m => m.FirstName == "John", m.LastName == "Smith")
    .Include(m => m.Comments);

Все это похоже на синтаксический сахар, однако он дает вам несколько преимуществ, если вы захотите пройти дополнительную настройку. В любом случае, вы, похоже, сможете получить лучшее из обоих подходов.

0
ответ дан Mauricio Morales 15 August 2018 в 22:30
поделиться

Отличный ответ Slauma.

Я просто отправлю код, чтобы сделать это, используя свободное API отображение.

public class User {
    public int UserID { get; set; }
    public string Username { get; set; }
    public string Password { get; set; }

    public ICollection<UserEmail> UserEmails { get; set; }
}

public class Email {
    public int EmailID { get; set; }
    public string Address { get; set; }

    public ICollection<UserEmail> UserEmails { get; set; }
}

public class UserEmail {
    public int UserID { get; set; }
    public int EmailID { get; set; }
    public bool IsPrimary { get; set; }
}

В вашем производном классе DbContext вы можете сделать это:

public class MyContext : DbContext {
    protected override void OnModelCreating(DbModelBuilder builder) {
        // Primary keys
        builder.Entity<User>().HasKey(q => q.UserID);
        builder.Entity<Email>().HasKey(q => q.EmailID);
        builder.Entity<UserEmail>().HasKey(q => 
            new { 
                q.UserID, q.EmailID
            });

        // Relationships
        builder.Entity<UserEmail>()
            .HasRequired(t => t.Email)
            .WithMany(t => t.UserEmails)
            .HasForeignKey(t => t.EmailID)

        builder.Entity<UserEmail>()
            .HasRequired(t => t.User)
            .WithMany(t => t.UserEmails)
            .HasForeignKey(t => t.UserID)
    }
}

Он имеет тот же эффект, что и принятый ответ, с другим подходом, который не лучше и не хуже.

EDIT: я изменил CreateDate с bool на DateTime.

EDIT 2: Из-за нехватки времени я разместил пример из приложения, над которым я работаю, чтобы быть уверенным это работает.

83
ответ дан Peter Mortensen 15 August 2018 в 22:30
поделиться
  • 1
    Я думаю, что это неправильно. Вы создаете отношения M: M здесь, где для обоих объектов должно быть 1: M. – CHS 30 May 2013 в 00:16
  • 2
    @CHS In your classes you can easily describe a many to many relationship with properties that point to each other., взятый из: msdn.microsoft.com/en-us/data/hh134698.aspx . Джулия Лерман не может ошибаться. – Esteban 30 May 2013 в 03:55
  • 3
    Эстебан, сопоставление отношений действительно неверно. @CHS прав об этом. Джули Лерман говорит об «истине». отношения «многие ко многим», в то время как у нас есть пример модели, которая не может быть отображена как много-ко-многим. Ваше сопоставление даже не будет компилироваться, потому что у вас нет свойства Comments в Member. И вы не можете просто исправить это, переименовав вызов HasMany в MemberComments, потому что объект MemberComment не имеет обратную коллекцию для WithMany. На самом деле вам нужно настроить два отношения «один ко многим» , чтобы получить правильное отображение. – Slauma 7 October 2013 в 12:38
  • 4
    @Slauma Я изменил пример, чтобы убедиться, что это работает. – Esteban 7 October 2013 в 21:07
  • 5
    Спасибо. Я следовал этому решению, чтобы сделать сопоставление «многие ко многим». – Thomas.Benz 2 March 2017 в 09:06
Другие вопросы по тегам:

Похожие вопросы: