SQLite-net TableQuery <t> .Select()性能不佳

时间:2016-12-15 19:23:27

标签: c# performance linq sqlite-net

考虑下面的完整代码块,特别是这部分 - 对象初始化(就是你所谓的那个?):

new LocalFileInfo() {
  IsFavorite = p.IsFavorite,
  ...
  WhenCrawled = p.WhenCrawled
}

有没有把这个代码抽象成一些我可以重用的方法,而不是将对象初始化代码复制粘贴到每个查询中?我强烈倾向于使用性能最佳的代码,而不是最容易维护的代码(但显然很容易维护)。

public static List<LocalFileInfo> RecentlyCrawledFiles(int take)
{
    if (take < 1) take = 1;

    List<LocalFileInfo> list = new List<LocalFileInfo>();

        using (SQLite.Net.SQLiteConnection conn = new SQLite.Net.SQLiteConnection(new SQLite.Net.Platform.WinRT.SQLitePlatformWinRT(), sqliteDb))
        {
            // works but is slower.
            // list = (from p in conn.Table<LocalFileInfo>() select p).OrderBy(f => f.WhenCrawled).Take(take).ToList();

            list = (from p in conn.Table<LocalFileInfo>() select new LocalFileInfo() {
                IsFavorite = p.IsFavorite,
                LastModified = p.LastModified,
                Name = p.Name,
                ParentFolder = p.ParentFolder,
                Path = p.Path,
                Size = p.Size,
                SourceId = p.SourceId,
                SourceName = p.SourceName,
                SourceType = p.SourceType,
                WhenCrawled = p.WhenCrawled
            })
            .OrderByDescending(f => f.WhenCrawled)
            .Take(take)
            .ToList();
        };

    return list;
}

4 个答案:

答案 0 :(得分:3)

我在SQLite-net

中查看了主分支的源代码

该图书馆似乎忽略了您的预测,所以我不认为为他们制作表达会有任何帮助

例如,参见SqlLite.cs TableQuery<T>

    public IEnumerator<T> GetEnumerator ()
    {
        if (!_deferred)
            return GenerateCommand("*").ExecuteQuery<T>().GetEnumerator();

        return GenerateCommand("*").ExecuteDeferredQuery<T>().GetEnumerator();
    }

每次执行GetEnumerator()ToList()时都会使用此foreach (var item in query)方法。

GenerateCommand()将构建SQL,理论上它是#34;支持字符串参数selectList,但库永远不会使用此参数

此外,当您执行Select(Expression)时,libary会存储_selector私有财产,但绝不会在GenerateCommand()或任何其他呼叫

的呼叫中使用它

<强> This issue has been reported

所以,我认为你最好的选择就是:

var list = conn.Table<LocalFileInfo>()
    .OrderByDescending(f => f.WhenCrawled)
    .Take(take)
    .ToList();

此后您可以执行Select<T>(),但该库已经加载了整个实体列表以及每个属性。但是,如果您保留这些实体(也就是说,ToList()之后的预测不会立即给您带来任何好处),那么选择事后可以帮助您使用GC。

或者使用连接对象中的Query<T>() ...这将引导您返回SQL域。

或者,等待 Entity Framework Core to support Xamarin

Roadmap中提到了

  

Xamarin在某些情况下可以使用,但尚未作为受支持的方案进行全面测试。

很抱歉,但这是我现在能想到的最好的:(

编辑:事实上,我相信如果你做了Select(p => p.IsFavorite),那么图书馆会失败,因为它无法映射你的投影(不是抨击图书馆,只是抬头)

答案 1 :(得分:2)

而不是

var file = image.src; //this is the image url that i'm sending

怎么样:

list = (from p in conn.Table<LocalFileInfo>() select new LocalFileInfo() {
            IsFavorite = p.IsFavorite,
            LastModified = p.LastModified,
            Name = p.Name,
            ParentFolder = p.ParentFolder,
            Path = p.Path,
            Size = p.Size,
            SourceId = p.SourceId,
            SourceName = p.SourceName,
            SourceType = p.SourceType,
            WhenCrawled = p.WhenCrawled
        })

只要您将所有属性投影到相同的对象类型中,它就会起作用。如果其中任何一个都不成立,那么你实际上可以像这样抽象出来:

list = conn.Table<LocalFileInfo>()

然后你可以这样称呼它:

public static class FromObjectExtensions
{
  public static IEnumerable<ToObject> ToToObject(this IEnumerable<FromObject> q)
  {
    return q.Select(t=>new ToObject
    {
       Property1=t.Property1,
       ...
    };
  }
}

只需将list = conn.Table<LocalFileInfo>().ToToObject(); ToObject替换为您的实际对象类型,然后填写要复制的属性。

答案 2 :(得分:1)

这样做会很好。

     using (SQLite.Net.SQLiteConnection conn = new SQLite.Net.SQLiteConnection(new SQLite.Net.Platform.WinRT.SQLitePlatformWinRT(), sqliteDb))
        {
  var list = conn.Table<LocalFileInfo>().OrderByDesending(f=>f.WhenCrawled).Take(take).ToList();
        };

虽然您的查询有效,但您重新选择了一个没有意义的选定项目,特别是:

 list = (from p in conn.Table<LocalFileInfo>() select new LocalFileInfo() {
                IsFavorite = p.IsFavorite,
                LastModified = p.LastModified,
                Name = p.Name,
                ParentFolder = p.ParentFolder,
                Path = p.Path,
                Size = p.Size,
                SourceId = p.SourceId,
                SourceName = p.SourceName,
                SourceType = p.SourceType,
                WhenCrawled = p.WhenCrawled
            })

由于这是Linq,您首先收集对象,并重新创建已声明对象的新实例。这毫无意义,并且效率极低。

答案 3 :(得分:1)

您需要为选择器定义Expression

public static Expression<Func<LocalFileInfo, LocalFileInfo>> MyLocalFileInfoSelector = 
    p => new LocalFileInfo() {
            IsFavorite = p.IsFavorite,
            LastModified = p.LastModified,
            Name = p.Name,
            ParentFolder = p.ParentFolder,
            Path = p.Path,
            Size = p.Size,
            SourceId = p.SourceId,
            SourceName = p.SourceName,
            SourceType = p.SourceType,
            WhenCrawled = p.WhenCrawled
        };

然后您可以在任何地方使用它:

return conn.Table<LocalFileInfo>()
    .Select(MyLocalFileInfoSelector)
    .OrderBy(f => f.WhenCrawled)
    .Take(take)
    .ToList();