Dotnet EF ลึ Linq ข้อความบรรจุอยู่ในรายการข้อความแบ่งโดยจุลภาค

0

คำถาม

ฉันมีนางแบบเหมือนนี้ในฐานข้อมูล:

โพสต์(PostId น int ได้,UserIds varchar(แม็กซ์)),ตัวอย่างเช่นโพสต์(12,"1,2,3,7,9,20")

ฉันต้องการที่จะสืบค้นมันได้ด้วย UserId สำหรับตอนนี้ฉันใช้:

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

แต่ปัญหาก็คือถ้าเป้าหมายคือ 1,มันก็ยังกลับโพสกับ UserIds="15,16" ฉันพยายามจะใช้ Regex เหมือน Regex.IsMatch(_.UserIds, $"\\b{targetId}\\b") แต่ภาษา sql ไม่สามารถแปลมันออก

มีทางใดที่จะคลี่คลายคดีนี้?

2
1

ดังนั้นฐานข้อมูลของคุณมีโต๊ะเต็มไปด้วย Posts. ทุกๆ Post ดูเหมือนว่าจะส่งโดยเป็นศูนย์หรือมากกว่า(บางทีหนึ่งหรือมากกว่าเดิม)ผู้ใช้. สำหรับฉันมันดูเหมือนว่าคุณยังต้องเป็นโต๊ะของ Users. ทุกๆ User มีโพสต์เป็นศูนย์หรือมากกว่า Posts.

มันดูเหมือนว่ามีหลายจะมีความสัมพันธ์ระหว่าง Users แล้ว Postsทุกใช้โพสต์เป็นศูนย์หรือมากกว่ากัปตัน..เสาทางนี้;ทุกโพสถูกส่งโดยศูนย์(หนึ่ง?) หรือมากกว่าผู้ใช้.

ปกติในฐานข้อมูลคุณคงเตรียมกำหนดหลายจะมีความเกี่ยวข้องกับเป็นพิเศษโต๊ะ:ซักโต๊ะ

คุณไม่ใช้ห้องซักโต๊ะ ฐานข้อมูลของคุณไม่ใช่ normalized. บางทีของคุณปัจจุบันจะแก้ไขปัญหาได้โดยไม่เปลี่ยนแปลงฐานข้อมูลแต่ฉันเห็นมากมายปัญหาของคุณจะต้องแก้ปัญหาอาจไม่ใช่ตอนนี้แต่ในที่ใกล้กับอนาคตอะไรมากมาทำงานคุณจะต้องทำถ้าคุณต้องการจะลบผู้ใช้งาน? คุณทำยังไงทุกอย่าง"กัปตัน..เสาทางนี้นั่นของผู้ใช้[10 งโพสต์"และถ้าผู้ใช้[10]ไม่ต้องการที่จะพูดถึงอีกแล้วในบริษัทแล้รายการของโพสต์[23]? วิธีที่จะป้องกันไม่ให้มันของผู้ใช้[10]คือพูดถึงสองครั้งในโพสต์[23]:

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

Normalize ฐานข้อมูล

พิจารณาเพื่อปรับปรุงฐานข้อมูลกันซักโต๊ะและกำจัดของข้อคอลัมน์ Post.UserIds. มันจะแก้ปัญหาทั้งหมดนี้ปัญหาที่ครั้งหนึ่ง

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

และซักโต๊ะ:

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

ข้อควรจำ:[UserId,PostId นเป็เปลี่ยนไปราตรีสวัสดิ์นะ ใช้นี่เป็นตัวหลักของกุญแจ

อยู่ในรายการส่วนเฟรมเวิร์กในคอลัมน์ของตารางข้อมูลเรียบร้อยแล้วนี่เป็นตัวแทนโดยที่ไม่เสมือนคุณสมบัติของ. คนเสมือนคุณสมบัติของสะท้อนที่ความสัมพันธ์ระหว่างตารางข้อมูลเรียบร้อยแล้ว(หนึ่งเพื่อหลายต่อหลายไปหลาย)

ข้อควรจำ:ต่างประเทญแจสำคัญคือจริงของคอลัมน์ในโต๊ะจึต่างบ้านต่างสำคัญคือคนที่ไม่ใช่เสมือน.

ปรับแต่งหลาย-เพื่อนมากคุณสามารถใช้อย่างดีเชี่ยวชาญรูปแบบ 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});
}

กลับไปที่ปัญหาของคุณ

ครั้งหนึ่งที่คุณการจัดเตรียมไว้ซักโต๊ะของคุณข้อมูลการร้องของจะเป็นเรื่องง่าย:

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

หรือถ้าคุณไม่ต้องการอะไรของผู้ใช้ข้อมูลเริ่มต้นกับกัปตัน..เสาทางนี้:

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

คนบางคนไม่ชอบที่จะใช้เสมือน ICollections หรือพวกเขาใช้เป็นเวอร์ชั่นของรายการส่วนเฟรมเวิร์กนั่นก็ไม่ได้สนับสนุนเรื่องนี้ ในกรณีนี้เธอจะต้องทำร่วมกับตัวเอง:

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

แก้ปัญหาโดยไม่มี normalized ฐานข้อมูล

ถ้าคุณไม่สามารถโน้มน้าวของคุณเป็นหัวหน้าโครงการนั้นเป็นที่เหมาะสมฐานข้อมูลจะป้องกันปัญหามากมายในอนาคตพิจารณาเพื่อสร้างเป็นภาษา sql ข้อความนั่นได้อย่างเหมาะสมกัปตัน..เสาทางนี้สำหรับคุณ

ของคุณ DbContext แสดงถึงปัจจุบัน implementation ของฐานข้อมูลของคุณ. มันอธิบายว่าโต๊ะมันทำความสะอาดและความสัมพันธ์ระหว่างตารางข้อมูลเรียบร้อยแล้ว. เพิ่มวิธีการจะเรียนที่กัปตัน..เสาทางนี้ของผู้ใช้ดูเหมือนฉันถูกวิธีการสำหรับ DbContext.

ของฉันภาษา sql วนิดหน่อยรัสตี้คุณจะรู้ว่าวิธีที่ดีกว่าที่ฉันยังต้องทำอย่างนี้อยู่ในภาษา sql. ฉันคิดว่าเธอจะได้ gist:

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

ขอบคุณสำหรับคำตอบของคุณ ใช่มันจะเป็นเรื่องง่ายถ้าฉัน normalize ฐานข้อมูลแต่มันเก่าของระบบ,ผมแค่ทำง่ายๆตัวอย่างเช่นที่พออธิบายปัญหาฉันเข้าไปในหรับการออกแบบโครงการช่องข้อมูลนี้เกี่ยวข้องกับเรื่องต่างๆมากมายยอดต้องการเวลาเพื่ refactor มันในขณะที่ฉันแค่อยากจะ hotfix มันตอนนี้ ยังไงก็ตาม,ขอบคุณสำหรับคำตอบของคุณ
Jihai
1

นี่คือเป็นไปได้ทางออกถ้าคุณไม่สามารถ normalize มัน:

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

ในภาษาอื่นๆ

หน้านี้อยู่ในภาษาอื่นๆ

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

ดังอยู่ในนี้หมวดหมู่

ดังคำถามอยู่ในนี้หมวดหมู่