sqlite Linq Orderby没有2个参数

bejyjqdl  于 2023-08-06  发布在  SQLite
关注(0)|答案(2)|浏览(127)

我想使用Linq和SQLite OrderBy一个字段。表:
| OriginalWord |
| ------------ |
| Test1 |
| eat |
| test2 |
我想订购OriginalWord

await Database.Table<Word>()
      .Where(i => i.DictionaryId == id)
      .OrderBy(w => w.OriginalWord)
      .ToListAsync();

字符串
SQLite中的顺序区分大小写。我找到了一个solution

internal sealed class NameComparer : IComparer<string> {
    private static readonly NameComparer DefaultInstance = new NameComparer();

    static NameComparer() { }
    private NameComparer() { }

    public static NameComparer Default {
        get { return DefaultInstance; }
    }

    public int Compare(string x, string y) {
        int length = Math.Min(x.Length, y.Length);
        for (int i = 0; i < length; ++i) {
            if (x[i] == y[i]) continue;
            if (x[i] == '-') return 1;
            if (y[i] == '-') return -1;
            return x[i].CompareTo(y[i]);
        }

        return x.Length - y.Length;
    }
}

var sorted = names.OrderBy(name => name, NameComparer.Default).ToList();


这不起作用,因为OrderByhas只有一个参数:
x1c 0d1x的数据
我使用NET 7(MAUI)和SQLite。

wbrvyc0a

wbrvyc0a1#

问题是数据库可以使用索引来有效地进行排序。但是该索引可以区分大小写,并且可以遵循关于字符串排序的一些其他规则。这被称为Collation,您应该能够在数据库中为整个数据库或特定列指定它。
因此,如果希望查询在数据库中高效运行,可能需要更改排序规则。
一个解决方法是将未排序的列表加载到内存中,然后在内存中对其进行排序。这将有一些性能损失,因为没有索引可以使用。但是排序是相当有效的,所以我希望它足够快:

(await Database.Table<Word>()
      .Where(i => i.DictionaryId == id)      
      .ToListAsync())
      .OrderBy(w => w.OriginalWord, StringComparer.CurrentCultureIgnoreCase);

字符串

vawmfj5a

vawmfj5a2#

你不能在OrderBy内部调用C#函数,因为SQLite的OrderBy没有提供比较器参数。
相反,您需要在列上定义排序规则
您有两种选择:

  • 使用预定义排序规则之一:
public class Word
{
// whatever
    [Collation("NOCASE")]
    public string OriginalWord

字符串

  • 创建您自己的排序规则并使用一些PInvoke将其连接起来。(注意此代码未经测试,不确定它是否在MAUI中工作)。
[DllImport("sqlite3", CallingConvention = CallingConvention.Cdecl)]
SQLite3.Result sqlite3_create_collation16(
  IntPtr handle,
  [MarshalAs(UnmanagedType.LPWStr)]
  string zName,
  int eTextRep,
  IntPtr pArg,
  CompareCallback xCompare
);

const int SQLITE_UTF16_ALIGNED = 8;

delegate int CompareCallback(IntPtr pArg, int length1, IntPtr pointer1, int length2, IntPtr pointer2);

internal sealed class NameComparer : IComparer<string>
{
    public readonly CompareCallback = ComparePinvoke;
    // MUST keep delegate alive while the connection is open

    private int ComparePinvoke(IntPtr pArg, int length1, IntPtr pointer1, int length2, IntPtr pointer2)
    {
        var str1 = PtrToStringUni(pointer1, length1);
        var str2 = PtrToStringUni(pointer2, length2);
        return Compare(str1, str2);
    }

    // etc
}


然后可以像这样将归类添加到连接中

using (var connection = new SqliteConnection)
{
    var r = sqlite3_create_collation16(connection.Handle, "YourCollationNameHere", SQLITE_UTF16_ALIGNED, IntPtr.Zero, NameComparer.CompareCallback);
    if (r != SQLite3.Result.OK)
        throw SQLiteException.New (r, SQLite3.GetErrmsg(connection.Handle));

    // whatever
}


请注意,在连接打开时,您必须保持回调委托活动。在您的例子中,您似乎有一个静态Singleton比较器,因此使用它来保存回调委托,如图所示。否则,使用字段或使用GC.KeepAlive保持比较器活动。
最后,

public class Word
{
// whatever
    [Collation("YourCollationNameHere")]
    public string OriginalWord

相关问题