我正在为Windows Phone(SDK 7.1)应用编写Silverlight,我正在使用Silverlight Toolkit for Windows Phone的LongListSelector
控件中的CompactSQL DB显示数据。
一旦列表变成大约150个项目,该应用程序真的减慢了加载数据,导航到页面和从页面导航和动画无法显示(我知道使用后台线程将有助于释放动画的UI线程)。 / p>
我目前有三个不断使用的查询 - 每次更新LongListSelector的数据或页面为NavigatedTo。我已将MoviesByTitle
转换为CompiledQuery
并且已经帮助了很多,所以我希望对我的其他两个查询(groupedMovies
和LongListSelector.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;
}
...
}
答案 0 :(得分:4)
我假设GroupHeader
是存储在数据库中的实体,与Movie
实体的关系为1-n。
首先,我在这里看不到3个DB查询。 LINQ表达式并不总是DB查询(例如,LINQ to Objects)。有时确定真正发生的事情是非常具有挑战性的。在这种情况下,最好的朋友是DB分析器工具或IntelliTrace - 它们显示在运行时在DB上执行的查询。
据我了解代码,您实际上有1+N
个查询:第一个是MoviesByTitle
,然后您在表达式中有N
个查询,这些查询按照头。它是N
而非1
,因为您将query
投射到IEnumerable<>
,使其不再是查询而是简单的CLR对象,这只是迭代在一个foreach
类似的循环中,每次需要一个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();
此代码应该更好地工作。我实际上做的是将query
从IEnumerable
转变为IQueryable
,这是一个仅迭代的CLR对象,可以进一步包装成更复杂的查询。现在只有一个查询可以按标题分组所有电影。它应该很快。
我会对代码进行更多改进,以使其更快地运行:
Union
个实体和一些默认列表。你事后订购了。您可以安全地从Linq2Sql代码中删除所有其他排序('query'和'groupedMoviesQuery')GroupHeader
s包括相关的Movie
?这应该在db查询中产生一个JOIN,它应该比GROUP BY更有效。我将展示一个针对原始逻辑的编译查询示例,并优化上面列表的第一项:
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
});