Dotnet EF Core Linq chaîne contient une liste de chaîne de caractères séparés par des virgules

0

La question

J'ai un modèle de ce genre dans la base de données:

La poste (PostId int, les Identifiants de type varchar(MAX)), exemple de message (12, "1,2,3,7,9,20")

Je veux l'interroger en Identifiant, pour l'instant, j'utilise ceci:

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

Mais le problème est que si la cible est de 1, il a également retour de courrier avec les Identifiants = "15,16" J'essaie d'utiliser les Regex comme Regex.IsMatch(_.UserIds, $"\\b{targetId}\\b") mais SQL ne peut pas le traduire.

Est de toute façon de résoudre cette affaire?

2
1

Si votre base de données est un tableau rempli avec Posts. Chaque Post semble être posté par zéro ou plus (peut-être, à un ou plusieurs Utilisateurs. Il me semble que vous aussi vous avez une table de Users. Chaque User a posté zéro ou plus Posts.

Il me semble qu'il y a un plusieurs-à-plusieurs relation entre Users et Posts: Chaque Utilisateur a posté zéro, un ou plusieurs Postes; chaque Poste a été posté par zéro (un?) ou plusieurs Utilisateurs.

Normalement dans une base de données, vous pouvez implémenter un plusieurs-à-plusieurs avec la table spéciale: la table de jonction.

Vous n'utilisez pas la table de jonction. Votre base de données n'est pas normalisée. Peut-être que votre problème peut être résolu sans changer la base de données, mais je vois beaucoup de problèmes, vous aurez à résoudre, peut-être pas maintenant, mais dans un avenir proche: quel immense travail devez-vous faire si vous souhaitez supprimer un utilisateur? Comment voulez-vous obtenir toutes "les Messages que l'utilisateur [10] a mis en ligne" Et que si l'Utilisateur [10] ne veut pas être mentionné dans la liste de publication de Poste [23]? Comment éviter que l'Utilisateur [10] est mentionné deux fois dans le Post[23]:

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

Normaliser la base de données

Envisager de mettre à jour la base de données avec une table de jonction et de se débarrasser de la colonne de chaîne Post.UserIds. Cela permettrait de résoudre tous ces problèmes à la fois.

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;}
}

Et la table de jonction:

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

Note: [UserId, PostId] est unique. Utilisez ce une clé primaire

Dans le cadre de l'entité les colonnes de tables sont représentées par des non-virtuel propriétés. Les propriétés virtuelles reflètent les relations entre les tables (un-à-plusieurs, plusieurs-à-plusieurs)

Remarque: une clé étrangère est un véritable colonne dans une table, d'où une clé étrangère est non-virtuel.

Pour configurer plusieurs-à-plusieurs, vous pouvez utiliser l'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});
}

De retour à votre problème

Une fois que vous avez mis en œuvre la table de jonction votre demande sera facile:

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(),
    });

Ou, si vous ne voulez pas les données de l'utilisateur, commencez par les Postes:

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

Certaines personnes n'aiment pas utiliser le virtuel ICollections, ou qu'ils utilisent une version de l'entity framework qui ne prend pas en charge cette. Dans ce cas, vous aurez à faire la Jointure vous-même:

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,
        ...
    }
}

Solution sans base de données normalisée

Si vraiment vous ne pouvez pas convaincre votre chef de projet et une bonne base de données permettra d'éviter beaucoup de problèmes dans l'avenir, envisager de créer un texte SQL que de recevoir correctement les messages pour vous.

Votre DbContext représente l'implémentation actuelle de votre base de données. Il décrit les tables et les relations entre les tables. L'ajout d'une méthode pour récupérer les Messages d'un utilisateur me semble une pièce de théâtre pour la méthode de DbContext.

Mon SQL est un peu rouillé, vous savez mieux que moi comment le faire en SQL. Je suppose que vous allez prendre connaissance de l'essentiel:

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

Merci pour votre réponse. Oui, il sera facile si je normaliser la base de données mais c'est un vieux système, j'ai juste fait un exemple simple de décrire le problème j'ai en, dans le projet de ce champ se rapporte à beaucoup de choses, besoin de plus de temps pour refactoriser le code alors que je veux juste correctif maintenant. De toute façon, merci pour votre réponse.
Jihai
1

Voici une solution possible si vous ne pouvez pas normaliser:

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

Dans d'autres langues

Cette page est dans d'autres langues

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