Dotnet EF Core Linq reťazec obsahuje zoznam string split čiarkou

0

Otázka

Mám model, ako je to v databáze:

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

Chcem dotaz tým, UserId, teraz používam tento:

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

Ale problém je, že ak cieľ 1, to tiež vrátiť Príspevok s UserIds = "15,16" Snažím sa používať ako regulárny výraz Regex.IsMatch(_.UserIds, $"\\b{targetId}\\b") ale SQL nemožno preložiť.

Je nejaký spôsob, ako vyriešiť tento prípad?

2
1

Takže vaša databáza obsahuje tabuľky plnené Posts. Každý Post zdá sa pridal nula alebo viac (možno jeden alebo viac) Používateľov. Zdá sa mi, že máte tabuľku Users. Každý User bol vyslaný nula alebo viac Posts.

Zdá sa mi, že tam je veľa-to-many vzťah medzi Users a Posts: Každý Užívateľ má publikované nula alebo viac Miest; každý Príspevok bol zverejnený na nulu (jeden?) alebo viac Užívateľov.

Za normálnych okolností v databáze by ste realizovať many-to-many vzťah s osobitnej tabuľky: spojovacej tabuľky.

Nemáte pomocou spojovacej tabuľky. Databáza nie je normovaná. Možno, že váš súčasný problém sa dá vyriešiť bez zmeny databázy, ale vidím, toľko problémov budete musieť vyriešiť, možno nie teraz, ale v blízkej budúcnosti: aké obrovské úsilie by musíte urobiť, ak chcete zmazať používateľa? Ako dosiahnuť, aby sa všetky Príspevky používateľa, [10] bol vyslaný" A čo ak si Užívateľ [10] nechce byť uvedené už v publikácii zoznam Post [23]? Ako zabrániť tomu, aby Používateľ [10] je uvedená dvakrát v Príspevku[23]:

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

Normalizovať databázy

Zvážiť možnosť aktualizácie databázy pomocou spojovacej tabuľky a zbaviť string stĺpec Post.UserIds. To by vyriešiť všetky tieto problémy naraz.

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

A spojovacej tabuľke:

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

Upozornenie: [UserId, PostId] je jedinečný. Použite tento primárny kľúč

V účtovnej jednotky rámec stĺpcov tabuliek sú zastúpené non-virtuálne vlastnosti. Virtuálne vlastnosti odrážajú vzťahy medzi tabuľkami (one-to-many, many-to-many)

Poznámka: cudzí kľúč je skutočný stĺpec v tabuľke, teda cudzí kľúč je non-virtuálne.

Ak chcete nakonfigurovať many-to-many, môžete použiť Plynule API:

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

Späť na váš problém

Akonáhle ste implementovali spojovacej tabuľke vaše údaje žiadosť bude jednoduché:

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

Alebo, ak nechcete, aby sa údaje používateľa, začať s Príspevky:

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

Niektorí ľudia nemajú radi, ak chcete použiť virtuálne ICollections, alebo ich použite verziu entity framework, ktorý nepodporuje tento. V tomto prípade, budete musieť urobiť, je Pripojiť sa sami seba:

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

Riešenie bez normovaná databázy

Ak sa naozaj nemôžem presvedčiť vášho projektu leader že riadne databázy bude zabrániť mnohým problémom v budúcnosti, zvážte vytvorenie SQL text, ktorý si riadne príspevky pre vás.

Vaše DbContext predstavuje súčasná implementácia databázy. To popísané tabuľky a vzťahy medzi tabuľkami. Pridaním metóda na zisťovanie Príspevky používateľa, zdá sa mi, dôveryhodne metóda pre DbContext.

My SQL je trochu hrdzavý, budete vedieť lepšie ako ja, ako sa to v SQL. Myslím, že dostanete podstata:

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

Ďakujem za odpoveď. Áno, bude to jednoduché, ak som normalizovať databáze, ale to je starý systém, len som urobil jednoduchý príklad popísať problém, ktorý som dostal do, v projekte tohto poľa sa vzťahuje na veľa vecí, potrebujú viac času na refactor to, keď chcem len rýchla oprava je teraz. Každopádne, vďaka za odpoveď.
Jihai
1

Tu je možné riešenie, ak nemôžete normalizovať to:

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

V iných jazykoch

Táto stránka je v iných jazykoch

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