1

Silverlight for Windows Phone(SDK 7.1)アプリを作成しており、CompactSQLDBのデータをSilverlightToolkit forWindowsPhoneのLongListSelectorコントロールに表示しています。

リストの長さが約150アイテムになると、アプリはデータの読み込みを実際に遅くし、ページへの移動やページからの移動、アニメーションの表示に失敗します(バックグラウンドスレッドを使用すると、アニメーション用にUIスレッドを解放するのに役立ちます)。

現在、常に使用している3つのクエリがあります。LongListSelectorからのデータが更新されるか、ページがナビゲートされるたびです。私はに変換MoviesByTitleしましたがCompiledQuery、それは非常に役立ちました。そのため、他の2つのクエリ(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()メソッド内-このメソッドは、DBが更新されるときに、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();

GroupHeaderDBのプロパティMoviesとエンティティです

[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;
    }
    ...
}
4

1 に答える 1

4

GroupHeader私はそれがエンティティと1-nの関係でDBに保存されているエンティティであると仮定しMovieます。

まず、ここには3つのDBクエリが表示されません。LINQ式は常にDBクエリであるとは限りません(たとえば、オブジェクトへのLINQがあります)。実際に何が起こっているのかを判断するのは非常に難しい場合があります。このような場合の親友は、DBプロファイラーツールまたはIntelliTraceです。これらは、実行時にDBで実行されているクエリを表示します。

私がコードを理解している限り、実際には1+Nクエリがあります。最初はMoviesByTitle、で、次にN、映画をヘッダーでグループ化する式にクエリがあります。キャストするのではNなく、クエリではなく単純なCLRオブジェクトをキャストします。これは、エンティティ(エンティティ)が必要になるたびにDBにクエリを送信するようなループで繰り返されます。 )。1queryIEnumerable<>foreachGroupHeader

2つのクエリを1つにまとめてみてください。を使用する必要がない場合もあります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(); 

このコードはもっとうまくいくはずです。私が実際に行ったことは、反復のみのCLRオブジェクトから、queryさらに複雑なクエリにラップできるオブジェクトに変換したことです。これで、すべての映画をヘッダーでグループ化するクエリは1つだけになりました。それは速いはずです。IEnumerableIQueryable

コードをさらに高速化するために、コードにさらに多くの改善を導入します。

  1. UnionDBから読み取ったエンティティといくつかのデフォルトリストを使用します。後で注文します。Linq2Sqlコードから他のすべての順序を安全に削除できます(「query」および「groupedMoviesQuery」)
  2. ここでは、結合と比較して、グループ化は最も効率的ではないようです。GroupHeader関連するものを含めて、クエリを実行してみませんMovieか?これにより、dbクエリでJOINが生成され、GROUPBYよりも効率的になります。
  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
      });
于 2012-04-15T12:26:58.793 に答える