Dotnet EF de Bază Linq șir conține într-o listă șir de caractere despărțite prin virgulă

0

Problema

Am un model ca acesta, în baza de date:

Post (PostId int, UserIds varchar(MAX)), exemplu Post (12, "1,2,3,7,9,20")

Vreau să interogare prin UserId, pentru acum, eu folosesc asta:

DBContext.Posts.Where(_ => _.UserIds.Contains(targetId)).ToList();

Dar problema este că, dacă obiectivul este de 1, a reveni, de asemenea, Post cu UserIds = "15,16" Încerc să folosesc Regex ca Regex.IsMatch(_.UserIds, $"\\b{targetId}\\b") dar SQL nu se poate traduce.

Este vreo modalitate de a rezolva acest caz?

2
1

Deci, baza de date are un tabel plin cu Posts. Fiecare Post pare a fi scris de zero sau mai multe (poate unul sau mai multe) de Utilizatori. Mi se pare, că aveți, de asemenea, un tabel de Users. Fiecare User a postat zero sau mai multe Posts.

Mi se pare că există o mulți-la-mai-mulți relația dintre Users și PostsFiecare Utilizator a postat zero sau mai multe Mesaje, fiecare Mesaj a fost postat de zero (unul?) sau mai mulți Utilizatori.

În mod normal, într-o bază de date ce va implementa o mulți-la-mai-mulți legătură cu o masa speciala: intersecția masă.

Nu utilizați tabelul de joncțiune. Baza ta de date nu este normalizat. Poate problema ta actuală pot fi rezolvate fără a modifica baza de date, dar am văzut atât de multe probleme, va trebui să rezolve, poate nu acum, dar în viitorul apropiat: ce imens de muncă ar trebuie să faceți dacă doriți să ștergeți un utilizator? Cum a face tu a lua toate "Posturile care utilizatorul [10] s-a postat" Și ce dacă Utilizatorul [10] nu vrea să fie menționate în lista de publicații Post [23]? Cum pentru a preveni Utilizatorul respectiv [10] este menționat de două ori în Post[23]:

UserIds = 10, 3, 5, 10, 7, 10

Normalizarea bazei de date

Ia în considerare pentru a actualiza baza de date cu un nod de masă și a scăpa de string coloană Post.UserIds. Acest lucru ar rezolva toate aceste probleme dintr-o dată.

class User
{
    public int Id {get; set;}
    public string Name {get; set;}
    ...

    // every user has posted zero or more Posts:
    public virtual ICollection<Post> Posts {get; set;}
}

class Post
{
    public int Id {get; set;}
    public string Title {get; set;}
    public Datetime PublicationDate {get; set;}
    ...

    // every Post has been posted by zero or more Users:
    public virtual ICollection<User> Users {get; set;}
}

Și intersecția tabel:

public UsersPost
{
    public int UserId {get; set;}
    public int PostId {get; set;}
}

Notă: [UserId, PostId] este unic. Utilizați această o cheie primară

În entity framework coloanele din tabele sunt reprezentate de non-virtual properties. Virtual proprietăți reflectă relațiile între tabele (unu-la-multe, multe-la-multe)

Nota: o cheie externă este o adevărată coloană într-un tabel, prin urmare, o cheie externă este non-virtuale.

Pentru a configura mai multe-la-mai-mulți, puteți utiliza API Fluent:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    // User - Post: many-to-many
    modelBuilder.Entity<User>()
            .HasMany<Post>(user => user.Posts)
            .WithMany(post => post.Users)
            .Map(userpost =>
                    {
                        userpost.MapLeftKey(nameof(UserPost.UserId));
                        userpost.MapRightKey(nameof(UserPost.PostId));
                        userpost.ToTable(nameof(UserPost));
                    });

    // primary key of UserPost is a composite key:
    modelBuilder.Entity<UserPost>()
        .HasKey(userpost => new {userpost.UserId, userpost.PostId});
}

Revenind la problema ta

Odată ce ați implementat intersecția tabelul dvs. de date cererea va fi ușor:

int userId = ...

// get this User with all his Posts:
var userWithPosts= dbContext.Users
    .Where(user => user.Id == userId)
    .Select(user => new
    {
         // Select only the user properties that you plan to use
         Name = user.Name,
         ...

         Posts = user.Posts.Select(post => new
         {
             // Select only the Post properties that you plan to use
             Id = post.Id
             PublicationDate = post.PublicationDate,
             ...
         })
         .ToList(),
    });

Sau, dacă nu doriți orice date de utilizator, începe cu Mesaje:

var postsOfUser = dbContext.Posts
    .Where(post => post.Users.Any(user => user.Id == userId))
    .Select(post => new {...});

Unii oameni nu le place să folosească virtual ICollections, sau folosesc o versiune de cadru entitate care nu acceptă acest lucru. În acest caz, veți avea de a face-vă:

int userId = ...
var postsOfThisUser = dbContext.UserPosts

    // keep only the UserPosts of this user:
    .Where(userPost => post.UserId == userId)

    // join the remaining UserPosts with Posts
    .Join(dbContext.Posts,

    userpost => userpost.PostId,    // from every UserPost get the foreign key to Post
    post => post.Id,                // from every Post, get the primary key

    // parameter resultSelector: from every UserPost with matching Post make one new
    (userPost, post) => new
    {
        Title = post.Title,
        PublicationDate = post.PublicationDate,
        ...
    }
}

Soluție fără a normalizat baza de date

Dacă nu poți convinge pe liderul de proiect pe care o bază de date corespunzătoare va preveni o mulțime de probleme în viitor, ia în considerare pentru a crea un SQL text care poate primi mesaje pentru tine.

Ta DbContext reprezintă implementarea curentă a bazei de date. Acesta a descris tabele și relații între tabele. Adăugarea unei metode pentru a prelua Posturile de un utilizator mi se pare o metodă legit pentru DbContext.

My SQL este un pic ruginit, vei ști mai bine decât mine cum să facă acest lucru în SQL. Cred că vei înțelege esențialul:

public IEnumerable<Post> GetPostsOfUser(int userId)
{
    const string sqlText = "Select Id, ... from Posts where ..."

    object[] parameters = new object[] {userId};
    return this.Database.SqlQuery(sqlText, parameters);
}
2021-11-23 11:09:01

Vă mulțumim pentru răspunsul dumneavoastră. Da, va fi mai ușor dacă am normalizarea bazei de date, dar e un sistem mai vechi, am facut doar un simplu exemplu pentru a descrie problema pe care o am în, în cadrul proiectului acest domeniu se referă la o mulțime de lucruri, au nevoie de mai mult timp pentru a refactor în timp ce eu doar vreau să remediere rapidă este acum. Oricum, multumesc pentru raspunsul tau.
Jihai
1

Iată o posibilă soluție dacă nu puteți normaliza:

var sql = "select PostId,UserIds from Post";
sql += $" outer apply string_split(UserIds,',') where value={targetId}";

DBContext.Posts.FromSqlRaw(sql).ToList();
2021-11-23 18:13:09

În alte limbi

Această pagină este în alte limbi

Русский
..................................................................................................................
Italiano
..................................................................................................................
Polski
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................