Как объединить две таблицы с разделенными запятыми значения столбца в c # linq asp.net mvc - PullRequest
0 голосов
/ 03 декабря 2018

У меня есть две таблицы Profile и CourseList.

Profile таблица:

ID   | Name   | CourseId
-----+--------+----------
 1   | Zishan |  1,2                                          
 2   | Ellen  |  2,3,4 

CourseList таблица:

courseid | coursename 
---------+--------------
   1     |  java
   2     |  C++
   3     |  oracle
   4     |  dot net

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

Если я назову ID = 1, на мой взгляд:

Имя: Zishan,

название курса: java, C ++

Если на мой взгляд, если я позвоню ID = 2:

Имя: Эллен,

название курса: java, C ++, oracle.

Поэтому я пишу этот запрос Linq в моем контроллере:

var account = db.Profile.Where(x => x.PId == pid).FirstOrDefault();

string  CourseIDS = string.Join(",",account.CourceId);

var courselist = (from p in db.profile
                  join co in db.CourceList on p.CourceId equals co.courseId 
                  .ToString()  into co1 from co2 in co1.Where(x => 
                   LanguageIDS.Contains(x.courseId.ToString()))
            select new ViewProfileVM
            {
               courseName = string.Join(",", co2.courseName)
            }).FirstOrDefault();

ViewBag.courselist = courselist;

Затем я передаю ViewBag для просмотра и отображения результатов .....

Пожалуйста, помогитемне запрос linq не работает, и в будущем я хочу добавить join LanguagesList и CountryList, как и CourceList, поэтому, пожалуйста, предложите мне простое решение этой проблемы ..

Спасибо,

Ответы [ 2 ]

0 голосов
/ 03 декабря 2018

Кажется, что каждый Profile имеет ноль или более Courses, а каждый Course принадлежит нулю или более Profiles: прямое отношение «многие ко многим».

Обычно вВ реляционной базе данных это будет реализовано с использованием соединительной таблицы: отдельной таблицы с двумя столбцами, которые действуют как внешние ключи: ProfileId и CourseId.В вашем примере:

  • Для Зишана, у которого есть Id 1, у нас были бы [1, 1] и [1, 2], что означает, что Зишан посещает курсы с Id 1 и 2;
  • Для Эллен с Id = 2 у нас будет [2, 2] [2, 3] [2,4].

Увы, ваш дизайнер баз данных решил не следоватьэто стандартный шаблон базы данных.И вы застряли с проблемами.

Лучшее решение зависит немного от того, как долго вы будете зависать с этой базой данных, как часто она будет меняться, как часто вы будете запрашивать «Профили с их курсами» и«Курсы с их помощниками».

Лучшее решение: добавить таблицу соединений в базу данных

Лучшим решением будет однократная миграция вашей базы данных: создайте таблицу соединений и добавьте[profile.Id, Course.Id] комбинаций в этом списке.После этого удалите столбец CourseId.

class ProfileCourse
{
    public int ProfileId {get; set;}
    public int CourseId {get; set;}
}

var ProfileIdCourseIdCombinations = db.Profiles
    .SelectMany(profile => profile.CourseIds.Split(',", StringSplitOptions.RemoveEmptyEntries),

    (profile, splitCourseId) => new ProfileCourse
    {
         ProfileId = profile.Id,
         CourseId = splitCourseId,
    });

Это нужно сделать только один раз.Подобные запросы в будущем будут достаточно стандартными.Каждый, кто свободно владеет LINQer, будет знать, как реализовать запросы.Недостаток: изменение базы данных.

Неоптимальное решение: имитировать соединительную таблицу

Довольно часто люди имеют промежуточный (адаптерный) уровень между вариантами использования и реальной базой данных.Таким образом, вы можете изменить базу данных без необходимости менять пользователей или наоборот.Шаблон для этого уровня адаптера довольно часто называется шаблоном репозитория.

Идея состоит в том, что вы создаете класс Repository, который представляет ваши таблицы как последовательность элементов IEnumerable.Соединительная таблица не будет реальной таблицей, она будет создаваться всякий раз, когда ее запрашивают.

class Repository
{
    public Respository(MyDbContext db)
    {
        this.db = db;
    }
    private readonly MyDbContext db;
    private IReadonlyCollection<ProfileCourse> profilesCourses = null;

    public IEnumerable<Profile> Profiles => this.db.Profiles;
    public IEnumerable<Course> Courses => this.db.Courses;

    public IEnumerable<ProfileCourse> ProfilesCourses
    {
         get
         {
             if (this.profilesCourses == null
             {
                 this.profilesCourses = ... // the SelectMany from above
                     .ToList();
             }
             return this.profilesCourses;
         }
    }
}

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

Это решение является неоптимальным, поскольку таблицу соединений необходимо пересоздавать каждый раз, когда вы будете использовать ее в новом репозитории.Кроме того, вы не можете добавлять профили или курсы, используя этот класс репозитория.Если будет довольно много работы по созданию функций для добавления / удаления профилей и курсов в ваш репозиторий.Возможно, будет проще воссоздать хранилище.

Вы получите ответ, спросив хранилище вместо вашего db

var repository = new Repository(db);
var profilesWithTheirCourses = repository.Profiles.GroupJoin(repository.ProfilesCourses,

    profile => profile.Id,                       // from every profile take the Id
    profileCourse => profileCourse.ProfileId,    // from every profileCourse take the ProfileId,

    (profile, profileCourses) => new     // take every profile with all its matching
    {                                    // profileCourses, to make a new obhect
        // Profile properties; add only those you plan to use:
        Id = profile.Id,
        Name = profile.Name,

        Courses = profileCourses.Join(repository.Courses, // join profileCourse with courses
           profileCourse => profileCourse.CourseId,       // from profileCourse take CourseId
           course => course.Id,                            // from course take Id
           (profileCourse, course) => new                  // when they match make a new
           {   // again: only the properties you actually plan to use
               Id = course.Id,
               Name = course.Name,
           })
           .ToList(),
    });

Решение только для этой проблемы

Есливы решили решить только эту проблему, вы можете объединить SelectMany и Join в один большой оператор LINQ.

Преимущество: быстрое решение;Недостаток: трудно читать, тестировать и поддерживать.Подобные проблемы в будущем будут иметь аналогичное неоптимальное решение,

var profilesWithTheirCourses = db.Profiles.SelectMany(
    profile => profile.CourseIds.Split(',", StringSplitOptions.RemoveEmptyEntries),
    (profile, splitCourseIds) => new
    {
        Id = profile.Id,
        Name = profile.Name,

        Courses = splitCourseIds.Join(db.Courses,
           courseId => courseId,
           course => course.Id,
           (courseId, course) => new
           {
               Id = course.Id,
               Name = course.Name,
           })
           .ToList(),
    });
0 голосов
/ 03 декабря 2018

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

var profileList = profiles.SelectMany(x=> x.CourseId.Split(',')
                        .Select(c=> 
                        new 
                        {
                            Name= x.Name, 
                            CourseId = int.Parse(c) 
                        }));
var result = profileList.Join(courses,
                p=>p.CourseId,
                c=>c.Id,
                (p,c)=>new {Name = p.Name, Courses= c.CourseName})
                .GroupBy(x=>x.Name)
                .Select(x=>new 
                {
                Name = x.Key, 
                Courses = string.Join(",",x.ToList().Select(k=>k.Courses))
                });

Где профили и курсы представляют собой набор представительных классов ваших структур данных.

public class Profile
{
public int Id{get;set;}
public string Name{get;set;}
public string CourseId{get;set;}
}


public class Coursename
{
public int Id{get;set;}
public string CourseName{get;set;}
}

Это будетожидаемый результат в виде

Имя: Курсы Zishan: Java, C ++

Имя: Курсы Ellen: C ++, Oracle, Dot Net

Ниже приведены примеры данных, которые я использовал длятестирование.

var profiles = new Profile[]
                {
                    new Profile{Id = 1, Name = "Zishan", CourseId = "1,2"},
                    new Profile{Id = 2, Name = "Ellen", CourseId = "2,3,4"}
                };
var courses = new Coursename[]
                {
                    new Coursename{Id=1, CourseName = "Java"},
                    new Coursename{Id=2, CourseName = "C++"},
                    new Coursename{Id=3, CourseName = "Oracle"},
                    new Coursename {Id=4, CourseName = "Dot Net"}
                };

Надеюсь, что поможет

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...