2

次のクエリからカウントを取得しようとすると問題が発生します。

var usersView = PopulateUsersView(); //usersView is an IQueryable object
var foo = usersView.Where(fields => fields.ConferenceRole.ToLower().Contains("role"));

ここで、UsersViewは、usersというEFエンティティから入力されるクラスです(上記のコードの最初の行を参照してください)。

これは、UsersViewクラスのクラス定義です。

public class UsersView
{
    public int UserId { get; set; }
    public string Title { get; set; }
    public string Name { get; set; }
    public string Surname { get; set; }
    public string Street1 { get; set; }
    public string Street2 { get; set; }
    public string City { get; set; }
    public string PostCode { get; set; }
    public string CountryName { get; set; }
    public string WorkPlaceName { get; set; }
    public string Gender { get; set; }
    public string EMail { get; set; }
    public string Company { get; set; }
    public string RoleName { get; set; }
    public string ConferenceRole { get; set; }
}

行を実行しようとすると、foo.Count()はNull例外を返します。これは、ConferenceRole列がデータベースでNullを許可していることが原因である可能性があります。

今私が理解できないのは、ObjectQueryで同じクエリを直接呼び出すと、レコードの数(つまり、foo2.Count()を呼び出す)が例外なく返されるということです。

var foo2 = entities.users.Where(fields => fields.ConferenceRole.ToLower().Contains("role"));

上記と同じクエリを実行できますが、代わりにIQueryable usersViewオブジェクトを使用できますか?

(entities.usersエンティティに直接クエリを実行するのではなく、usersViewオブジェクトを使用することが重要です)

編集

以下は、PopulateUsersViewメソッドのコードです

private IQueryable<UsersView> PopulateUsersView()
    {
        using (EBCPRegEntities entities = new EBCPRegEntities())
        {
            var users = entities.users.ToList();
            List<UsersView> userViews = new List<UsersView>();
            foreach (user u in users)
            {
                userViews.Add(new UsersView()
                {
                    UserId = u.UserId,
                    Title = u.Title,
                    Name = u.Name,
                    Surname = u.Surname,
                    Street1 = u.Street1,
                    Street2 = u.Street2,
                    City = u.City,
                    PostCode = u.Post_Code,
                    CountryName = u.country.Name,
                    WorkPlaceName = u.workplace.Name,
                    Gender = u.Gender,
                    EMail = u.E_Mail,
                    Company = u.Company,
                    RoleName = u.roles.FirstOrDefault().Name,
                    ConferenceRole = u.ConferenceRole
                });
            }
            return userViews.AsQueryable();
        }
    }

ありがとう

アップデート...

みんなありがとう、私はついにIQueryableオブジェクトとObjectQueryオブジェクトの違いに対する良い答えを見つけました。

解決策として、ConferenceRoleがnullであるかどうかを確認してから、多くの人が言っているように、containsメソッドで確認しています。

4

2 に答える 2

2

私の推測では、PopulateUsersView()メソッドは実際にクエリを実行し、IQueryable Linq-to-Objectsオブジェクトを返しますが、このfoo2行はSQLレイヤーでのみクエリを実行します。これが事実である場合、明らかPopulateUsersView()に実行するための非常に非効率的な方法になるでしょうCount

これをデバッグするには:

  • からコードを投稿できますPopulateUsersView()か?
  • EFトレースプロバイダーを介して両方のコードセットを実行して、SQLで何が実行されるかを確認できますか?(http://code.msdn.microsoft.com/EFProviderWrappersを参照してください)

アップデート

@Ryan-コードをに投稿してくれてありがとうPopulateUsersView

私の推測は正しかったようです-あなたはテーブル全体を-に戻すクエリを実行していますList-そしてこのリストをLinq2Objectsを使用してさらにクエリします。

@ntziolisは、問題を解決する1つの解決策を提供しました。これは、を実行する前にnullをテストすることによって行われますToLower()Countただし、空でないアイテムのリスト が唯一の要件である場合は、PopulateUsersView方法を変更するか、全体的なデザインを変更することを検討することをお勧めします。必要なのがtheだけの場合はCount、データベースがC#コードではなく、この作業を実行することを確認する方がはるかに効率的です。これは特に、テーブルに多数の行がある場合に当てはまります。たとえば、データベースから数千の行をメモリにプルバックしたくない場合などです。


アップデート2

!= null単純な修正だけでなく、これを最適化することを検討してください。

コードを見ると、複数のSQL呼び出しを引き起こすいくつかの行があります。

  • CountryName = u.country.Name
  • WorkPlaceName = u.workplace.Name
  • RoleName = u.roles.FirstOrDefault().Name

これらはforeachループで呼び出されるため、最大500ユーザーの数を計算するには、おそらく約1501のSQL呼び出しを行い(一部の役割と国はキャッシュされることが望ましいですが)、おそらく合計で1メガバイトのデータを返しますか?これはすべて、単一の整数を計算するためだけCountですか?

于 2011-07-14T08:22:44.813 に答える
2

ConferenceRoleメソッドを呼び出す前に、がnullかどうかを確認してください。

var foo = usersView.Where(fields => fields.ConferenceRole != null 
    && fields.ConferenceRole.ToLower().Contains("role"));

これにより、ユーザービューでcountメソッドを呼び出すことができます。

それで、なぜそれはに対して機能するのObjectQueryですか?

ObjectQueryに対してクエリを実行すると、LinqToSqlはクエリをnull値に問題のない適切なSQLに変換します。これは次のようなものです(サンプルのマークアップSQLは実際のクエリのみが大きく異なり、チェックするのではなく「=」が使用されます)含む):

SELECT COUNT(*) from USERS U WHERE TOLOWER(U.CONFERENCEROLE) = 'role'

:NETコードとの違いは次のとおりです。オブジェクトのメソッドを呼び出さず、メソッドを呼び出して値を渡すだけなので、この場合はNullReferenceは発生しません。

これを確認するために、whereメソッドを呼び出す前に.NETランタイムにSQLを強制的に実行させて、ToList()前にaを追加するだけです。.Where()

var foo2 = entities.users.ToList()
    .Where(fields => fields.ConferenceRole.ToLower().Contains("role"));

これにより、で見たのとまったく同じエラーが発生するはずUserViewです。

はい、これは最初にユーザーテーブル全体を返すので、ライブコードでは使用しないでください;)

更新
最初に間違ったクエリをc&pしたので、答えを更新する必要がありましたが、上記の点はまだ残っています。

于 2011-07-14T08:38:31.660 に答える