转换LINQ2SQL简单的查询到CompiledQueries,从而提高应用性能(Convertin

2019-09-16 12:29发布

我写的Windows Phone(SDK 7.1)应用Silverlight的,我在一个显示来自CompactSQL DB数据LongListSelector从Silverlight工具包控制的Windows Phone。

一旦列表达到约150个条目,该应用程序确实减慢加载数据,导航,并从网页和动画无法显示(我知道使用后台线程将与释放的动画UI线程帮助)。

我现在有一点我经常使用的三个查询 - 每次从LongListSelector数据被更新或页面NavigatedTo。 我已经转换MoviesByTitleCompiledQuery并已帮助了很多,所以我一直在寻找我的其他两个查询做同样的( groupedMoviesLongListSelector.ItemSource类型的List<Group<Movie>> ),但我似乎无法到找出正确的语法。

通过使用-在我会如何让这些查询更高效的任何建议CompiledQuery或以其他方式?

MoviesByTitle是在另一类称为Queries

public static Func<MovieDBContext, IOrderedQueryable<Movies>> MoviesByTitle = CompiledQuery.Compile((MovieDBContext db) => from m in db.Movies orderby m.Title,m.Year select m);

在田的MainPage

private static List<String> characters = new List<String> { "#", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z" };
public static List<Group<Movies>> emptyGroups = new List<Group<Movies>>();

在一个的MainPage LOADDB()方法中 - 当数据库被更新调用此方法中的OnNavigatedTo和其他几个地方。

//Populates the 'empty' Groups of Movies objects only once.
if (emptyGroups.Count < 1)
{
    characters.ForEach(x => emptyGroups.Add(new Group<Movies>(x, new List<Movies>())));
}

IEnumerable<Movies> query = Queries.MoviesByTitle(App.db);

//Groups the objects 
IEnumerable<Group<Movies>> groupedMovies = (from t in query
                                            group t by t.GroupHeader into grp
                                            orderby grp.Key
                                            select new Group<Movies>(grp.Key.ToString(), grp));

//Joins the 'empty' group and groupedMovies together for the LongListSelector control
moviesLongList.ItemsSource = (from t in groupedMovies.AsEnumerable().Union(emptyGroups)
                              orderby t.Title
                              select t).ToList();

GroupHeader是的属性Movies在DB和实体

[Column(CanBeNull=true, UpdateCheck = UpdateCheck.Never)]
public char GroupHeader
    {
        get
        {
            char l;
            //For Alphabetized Grouping, titles do NOT start with "The ..."
            if (this.Title.ToLowerInvariant().StartsWith("the "))
            {
                l = this.Title.ToLowerInvariant()[4];
            }
            else
            {
                l = this.Title.ToLower()[0];
            }
            if (l >= 'a' && l <= 'z')
            {
                return l;
            }
            return '#';
        }
        set { }
    }

Group类如下:

public class Group<T> : IEnumerable<T>
{
    public Group(string name, IEnumerable<T> items)
    {
        this.Title = name;
        this.Items = new List<T>(items);
    }

    public string Title
    {
        get;
        set;
    }

    public IList<T> Items
    {
        get;
        set;
    }
    ...
}

Answer 1:

我认为GroupHeader是存储在数据库中以1-N关系的实体Movie的实体。

首先,我没有看到3个DB查询这里。 LINQ表达式并不总是一个DB查询(例如有LINQ到对象)。 有时,确定真正是怎么回事是相当具有挑战性。 在这种情况下,最好的朋友是一个DB Profiler工具或智能跟踪 - 他们展示一下正在运行时对数据库执行查询。

据我理解的代码,你实际上有1+N查询:第一个是MoviesByTitle ,然后你有N在得到通过在头分组的电影表达的查询。 这是N ,而不是1 ,因为你施放queryIEnumerable<>使得它不再是一个查询,但一个简单的CLR对象 ,这简直是一个迭代foreach般的循环每次需要一次发送查询到DB GroupHeader实体(这是一个实体,是不是?)。

尝试2个查询合二为一。 你甚至可以不需要使用CompiledQuery 。 下面是一个近似的代码:

// I left this without changes
if (emptyGroups.Count < 1)           
{           
    characters.ForEach(x => emptyGroups.Add(new Group<Movies>(x, new List<Movies>())));           
} 

// I put var here, but it actually is an IQueryable<Movie>
var query = from m in App.db.Movies orderby m.Title, m.Year select m;

// Here var is IQueryable<anonymous type>, I just can't use anything else but var here
var groupedMoviesQuery = from t in query
                    group t by t.GroupHeader into grp 
                    orderby grp.Key 
                    select new
                    {
                       Movies = grp,
                       Header = grp.Key
                    }; 

// I use AsEnumerable to mark that what goes after AsEnumerable is to be executed on the client
IEnumerable<Group<Movie>> groupedMovies = groupedMoviesQuery.AsEnumerable()
                                            .Select(x => new Group<Movie>(x.Header, x.Movies))
                                            .ToList();

//No changes here
moviesLongList.ItemsSource = (from t in groupedMovies.AsEnumerable().Union(emptyGroups)            
                              orderby t.Title            
                              select t).ToList(); 

此代码应工作方式更好。 我实际上做的是,我把你的queryIEnumerable是一个迭代只CLR对象到IQueryable可以进一步包装到一个更复杂的查询。 现在有只有一个查询它得到通过头分组的所有电影。 它应该是快。

我会介绍更多的进步的代码,使之更快的工作:

  1. 您可以使用Union从DB和一些默认列表读取实体。 你算账订购。 您可以安全地从LINQ2SQL代码(“查询”和“groupedMoviesQuery”)删除所有其他排序
  2. 分组似乎不是最有效的位置相比,联接。 你为什么不只是查询GroupHeader小号包括其相关的Movie S' 这应该产生在DB查询这应该是比GROUP BY更有效的联接。
  3. 如果您仍然有问题,性能,你可以把查询到编译的查询。

我会告诉你用上面的列表中的第一项的优化原来的逻辑编译查询的例子:

class Result
{
  public GroupHeader Header {get;set;}
  public IEnumerable<Movie> Movies {get;set;}
}

public static Func<MovieDBContext, IQueryable<Result>> GetGroupHeadersWithMovies =
  CompiledQuery.Compile((MovieDBContext x) => 
      from m in x.Movies
      group m by m.GroupHeader into grp 
      select new Result
      {
        Movies = grp,
        Header = grp.Key
      });


文章来源: Converting Linq2SQL simple queries to CompiledQueries to increase app performance