LoginSignup
1
0

More than 1 year has passed since last update.

EntityFrameworkでPostgreSQLのデータを取得するとバグる

Last updated at Posted at 2021-12-12

以下のteratailで質問されている内容が私の環境でも発生した。

不具合は質問内容と同様、ある特定のテーブルのデータを取得したときレコードが重複されて取得されてしまうことがあるというもの。

teratailではOS再起動で直ったとあるが私の環境では直らなかった。

さっさと結論だけ読みたい場合はこちら

バージョン情報

.NET 5.0
Microsoft.EntityFrameworkCore v5.0.12
Npgsql.EntityFrameworkCore.PostgreSQL v5.0.10

現象

以下のPersonTableを取得する場合を例にする。

PersonTable.cs
using System;
using SystemComponentModel.DataAnnotations.Schemes;

namespace SampleProject
{
    [Table("persontbl")]
    [Serializable]
    public class PersonTable
    {
        [Column("id")]
        public string ID{ get; set; }
        [Column("name")]
        public string Name{ get; set; }
        [Column("age")]
        public decimal Age{ get; set; }
    }

DBには以下の値が入っているものとする。

id name age
{0001 taro 21
0002 shigeru 12
0003 satoshi 8
0004 hikaru 14
0005 shigezo 87
0006 takeshi 10

そして、以下のコードでDBのレコードを取得する。

DBAccess_BEFORE.cs
using System;
using System.Collections.Generic;

namespace SampleProject
{
    public class DBAccess
    {
        public DatabaseAccess(DatabaseContext context)
        {
            this.context = context;
        }

        public List<PersonTable> GetShota()
        {
            int[] targetAges = new[]{ 6, 7, 8, 9, 10, 11, 12 };
            var results = context.PersonTable
                .Where(x => (targetAges.Contains(x.Age) == true))
                .ToList();

            return results;
        }
    }
}

GetShotaメソッドの取得結果として期待されるのはこのようになる。

id name age
0002 shigeru 12
0003 satoshi 8
0006 takeshi 10

しかし、不具合発生時にはこのようになってしまう。

id name age
0002 shigeru 12
0003 satoshi 8
0003 satoshi 8

あるいはこう。

id name age
0002 shigeru 12
0002 shigeru 12
0002 shigeru 12

原因

不明。
別のテーブルだと起きない。

解決策

EntityFramewok + LINQ で取得する方法としての解決策は

ありません。

(情報お待ちしております。)

結論

どうにもならないのでSQLを直打ちすることにした。

DBAccess_AFTER.cs
using System;
using System.Collections.Generic;

namespace SampleProject
{
    public class DBAccess
    {
        public DatabaseAccess(DatabaseContext context)
        {
            this.context = context;
        }

        public List<PersonTable> GetShota()
        {
            int[] targetAges = new[]{ 6, 7, 8, 9, 10, 11, 12 };
            List<PersonTable> results = new();
            using (var command = context.Database.GetDbConnection().CreateCommand())
            {
                string inCondition = "";
                for (int i = 0; i < targetAges.Count; i++)
                {
                    inCondition += $"{targetAges[i]}";
                    if (i != targetAges.Count - 1)
                    {
                        inCondition += ",";
                    }
                }

                if (command.Connection.State != ConnectionState.Open)
                {
                    command.Connection.Open();
                }

                command.CommandText = $"SELECT * FROM persontbl WHERE age IN ({inCondition});";
                using (var dbReader = command.ExecuteReader())
                {
                    bool read = dbReader.Read();
                    while (read)
                    {
                        string id = dbReader["id"] as string;
                        string name = dbReader["name"] as string;
                        decimal age = (decimal)dbReader["age"];

                        results.Add(new(){ ID = id, Age = (int)age, Name = name });

                        read = dbReader.Read();
                    }
                }

                command.Connection.Close();
            }

            return results;
        }
    }
}

不本意ながらまあ一番確実だよね・・・。

留意事項

私はショタコンではない。

DBAccess_AFTER.csはベタで処理を書いているけど、もちろんラッパーとか作ってちゃんと構造化してね?

同コードは「とりあえず動いた!」という段階で投稿したものなので、部分的に間違っている可能性がある。

あと、エラー処理もしていない。

勝手に質疑応答

Q:なんで x.Age >= 6 || x.Age <= 12 の条件でLINQ作ってないの?

A:元のソースだとその部分が string型だったんよ。
そのまま掲載できないから改変してたらdecimalになってた。すまん。

Q:ほかに試したことは?

A:ContainsメソッドでSQLのIN句に相当する条件を作っているのがいけないのかと思って、Whereの条件取っ払って全件取得にしても駄目だった。
どうやっても重複するレコードが発生してしまった。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0