以下は、LINQに変換しようとしているクエリの例です。
SELECT *
FROM Users
WHERE Users.lastname LIKE '%fra%'
AND Users.Id IN (
SELECT UserId
FROM CompanyRolesToUsers
WHERE CompanyRoleId in (2,3,4) )
CompanyRolesToUsers
とUsers
の間にはFK関係がありますが、多対多の関係であり、CompanyRolesToUsers
はジャンクションテーブルです。
すでにサイトの大部分が構築されており、PredicateExtensionsクラスを使用してExpressionsを構築することにより、ほとんどのフィルタリングが既に機能しています。
簡単なフィルターのコードは次のようになります。
if (!string.IsNullOrEmpty(TextBoxLastName.Text))
{
predicateAnd = predicateAnd.And(c => c.LastName.Contains(
TextBoxLastName.Text.Trim()));
}
e.Result = context.Users.Where(predicateAnd);
別のテーブルの副選択に述語を追加しようとしています。 (CompanyRolesToUsers
)
私が追加できるようにしたいのは、これを行うものです:
int[] selectedRoles = GetSelectedRoles();
if( selectedRoles.Length > 0 )
{
//somehow only select the userid from here ???:
var subquery = from u in CompanyRolesToUsers
where u.RoleID in selectedRoles
select u.UserId;
//somehow transform this into an Expression ???:
var subExpression = Expression.Invoke(subquery);
//and add it on to the existing expressions ???:
predicateAnd = predicateAnd.And(subExpression);
}
これを行う方法はありますか?ストアドプロシージャを簡単に記述できるのでイライラしますが、このLINQのことは初めてで、期限があります。一致する例を見つけることができませんでしたが、どこかにあると確信しています。
これがあなたのためのサブクエリです!
List<int> IdsToFind = new List<int>() {2, 3, 4};
db.Users
.Where(u => SqlMethods.Like(u.LastName, "%fra%"))
.Where(u =>
db.CompanyRolesToUsers
.Where(crtu => IdsToFind.Contains(crtu.CompanyRoleId))
.Select(crtu => crtu.UserId)
.Contains(u.Id)
)
質問のこの部分に関して:
predicateAnd = predicateAnd.And(c => c.LastName.Contains(
TextBoxLastName.Text.Trim()));
クエリを作成する前に、テキストボックスから文字列を抽出することを強くお勧めします。
string searchString = TextBoxLastName.Text.Trim();
predicateAnd = predicateAnd.And(c => c.LastName.Contains( searchString));
データベースに送信されるものを適切に制御する必要があります。元のコードでは、トリミングのためにトリミングされていない文字列がデータベースに送信されるという可能性があります。これは、データベースが実行するのに適した作業ではありません。
このステートメントにはサブクエリは必要ありません。
select u.*
from Users u, CompanyRolesToUsers c
where u.Id = c.UserId --join just specified here, perfectly fine
and u.lastname like '%fra%'
and c.CompanyRoleId in (2,3,4)
または
select u.*
from Users u inner join CompanyRolesToUsers c
on u.Id = c.UserId --explicit "join" statement, no diff from above, just preference
where u.lastname like '%fra%'
and c.CompanyRoleId in (2,3,4)
とはいえ、LINQでは次のようになります。
from u in Users
from c in CompanyRolesToUsers
where u.Id == c.UserId &&
u.LastName.Contains("fra") &&
selectedRoles.Contains(c.CompanyRoleId)
select u
または
from u in Users
join c in CompanyRolesToUsers
on u.Id equals c.UserId
where u.LastName.Contains("fra") &&
selectedRoles.Contains(c.CompanyRoleId)
select u
繰り返しますが、どちらもこれを表現するための立派な方法です。私はどちらの場合も明示的な「結合」構文を好みますが、それは...
これが私がLINQでサブクエリを行っている方法です。これはあなたが望むものを手に入れるべきだと思います。明示的なCompanyRoleId == 2 ...を、必要な異なるロールの別のサブクエリに置き換えるか、同様に結合できます。
from u in Users
join c in (
from crt in CompanyRolesToUsers
where CompanyRoleId == 2
|| CompanyRoleId == 3
|| CompanyRoleId == 4) on u.UserId equals c.UserId
where u.lastname.Contains("fra")
select u;
さて、正しいレコードを取得する基本的な結合クエリを次に示します。
int[] selectedRolesArr = GetSelectedRoles();
if( selectedRolesArr != null && selectedRolesArr.Length > 0 )
{
//this join version requires the use of distinct to prevent muliple records
//being returned for users with more than one company role.
IQueryable retVal = (from u in context.Users
join c in context.CompanyRolesToUsers
on u.Id equals c.UserId
where u.LastName.Contains( "fra" ) &&
selectedRolesArr.Contains( c.CompanyRoleId )
select u).Distinct();
}
しかし、これは、すでに用意されているアルゴリズムと最も簡単に統合できるコードです。
int[] selectedRolesArr = GetSelectedRoles();
if ( useAnd )
{
predicateAnd = predicateAnd.And( u => (from c in context.CompanyRolesToUsers
where selectedRolesArr.Contains(c.CompanyRoleId)
select c.UserId).Contains(u.Id));
}
else
{
predicateOr = predicateOr.Or( u => (from c in context.CompanyRolesToUsers
where selectedRolesArr.Contains(c.CompanyRoleId)
select c.UserId).Contains(u.Id) );
}
LINQtoSQLフォーラム のポスターのおかげです。
あなたの場合、このようなことをすることができます 構文は少しずれているかもしれません)。こちらもご覧ください---(link
subQuery = (from crtu in CompanyRolesToUsers where crtu.RoleId==2 || crtu.RoleId==3 select crtu.UserId).ToArrayList();
finalQuery = from u in Users where u.LastName.Contains('fra') && subQuery.Contains(u.Id) select u;
正しいレコードを返すSQLのバージョンは次のとおりです。
select distinct u.*
from Users u, CompanyRolesToUsers c
where u.Id = c.UserId --join just specified here, perfectly fine
and u.firstname like '%amy%'
and c.CompanyRoleId in (2,3,4)
また、(2,3,4)はWebアプリのユーザーがチェックボックスリストから選択したリストであり、簡単にするためにハードコーディングしただけであることを忘れていました。本当にそれはCompanyRoleId値の配列なので、(1)または(2,5)または(1,2,3,4,6,7,99)の可能性があります。
また、より明確に指定する必要があるもう1つのことは、Webアプリケーションユーザーが入力したフォームフィールドに応じて、PredicateExtensionsを使用してクエリのWhereに述語句を動的に追加することです。作業中のクエリを、式の動的リストに添付できるLINQ式に変換します。
サンプルLINQクエリの一部を試して、それらをコードに統合できるかどうかを確認し、結果を投稿します。ありがとう!
マルセル