OK、ここにはいくつかのものがあります。モデルにいくつかのプロパティを追加することで、これを少し簡単にすることができます。それはオプションですか?その場合は、コレクションプロパティをエンティティに追加します。今、どのEF APIを使用しているのかわかりません:DbContext(コードファーストまたはedmx)またはObjectContext。私のサンプルでは、これらのクラスを生成するためにedmxモデルでDbContext APIを使用しました。
いくつかの注釈があれば、edmxファイルを省略することもできます。
public partial class BusinessUnit
{
public BusinessUnit()
{
this.ChlidBusinessUnits = new HashSet<BusinessUnit>();
this.UserPermissions = new HashSet<UserPermissions>();
}
public int BusinessUnitID { get; set; }
public string BusinessName { get; set; }
public int ParentBusinessUnitID { get; set; }
public virtual ICollection<BusinessUnit> ChlidBusinessUnits { get; set; }
public virtual BusinessUnit ParentBusinessUnit { get; set; }
public virtual ICollection<UserPermissions> UserPermissions { get; set; }
}
public partial class User
{
public User()
{
this.UserPermissions = new HashSet<UserPermissions>();
}
public int UserID { get; set; }
public string FirstName { get; set; }
public virtual ICollection<UserPermissions> UserPermissions { get; set; }
}
public partial class UserPermissions
{
public int UserPermissionsID { get; set; }
public int BusinessUnitID { get; set; }
public int UserID { get; set; }
public virtual BusinessUnit BusinessUnit { get; set; }
public virtual User User { get; set; }
}
public partial class BusinessModelContainer : DbContext
{
public BusinessModelContainer()
: base("name=BusinessModelContainer")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public DbSet<BusinessUnit> BusinessUnits { get; set; }
public DbSet<User> Users { get; set; }
public DbSet<UserPermissions> UserPermissions { get; set; }
}
@Chaseメダルは、再帰LINQ(またはEntity SQL)クエリを記述することはできません。
オプション1:遅延ロード
遅延ロードでは、あなたがこのような何かを行うことができ、有効になって...
private static IEnumerable<BusinessUnit> UnitsForUser(BusinessModelContainer container, User user)
{
var distinctTopLevelBusinessUnits = (from u in container.BusinessUnits
where u.UserPermissions.Any(p => p.UserID == user.UserID)
select u).Distinct().ToList();
List<BusinessUnit> allBusinessUnits = new List<BusinessUnit>();
foreach (BusinessUnit bu in distinctTopLevelBusinessUnits)
{
allBusinessUnits.Add(bu);
allBusinessUnits.AddRange(GetChildren(container, bu));
}
return (from bu in allBusinessUnits
group bu by bu.BusinessUnitID into d
select d.First()).ToList();
}
private static IEnumerable<BusinessUnit> GetChildren(BusinessModelContainer container, BusinessUnit unit)
{
var eligibleChildren = (from u in unit.ChlidBusinessUnits
select u).Distinct().ToList();
foreach (BusinessUnit child in eligibleChildren)
{
yield return child;
foreach (BusinessUnit grandchild in child.ChlidBusinessUnits)
{
yield return grandchild;
}
}
}
オプション2:プリロードエンティティ
はしかし、あなたがサーバーに繰り返しトリップを回避するために、これを最適化することができ、いくつかの方法があります。あなたがデータベースにreasaonably少数のビジネスユニットしか持っていない場合は、リスト全体をロードすることができます。そして、関係を自動的に修正するEFの能力のために、データベースからユーザーとその権限を読み込むだけで、私たちに必要なものすべてが与えられます。
明確にする:このメソッドは、すべてBusinessUnit
エンティティをロードすることを意味します。ユーザーに許可されていないものであっても。ただし、SQL Serverでは「チャタリング」が大幅に減少するため、上記のオプション1よりも優れたパフォーマンスを発揮する可能性があります。以下のオプション3とは異なり、これは特定のプロバイダに依存しない「純粋な」EFです。
上記の2つの例は改善される可能性があることにご注意ください。
オプション3:あなたはビジネスユニットの数が多い場合は別注SQLクエリは、共通テーブル式
を使用して、あなたが最も効率的な方法をしようとする場合があります。これは、階層的な共通表式を使用して1回のヒットで情報を戻すカスタムSQLを実行することです。これはもちろん、実装を1つのプロバイダ、おそらくSQL Serverに結びつけます。
WITH UserBusinessUnits
(BusinessUnitID,
BusinessName,
ParentBusinessUnitID)
AS
(SELECT Bu.BusinessUnitId,
Bu.BusinessName,
CAST(NULL AS integer)
FROM Users U
INNER JOIN UserPermissions P ON P.UserID = U.UserID
INNER JOIN BusinessUnits Bu ON Bu.BusinessUnitId = P.BusinessUnitId
WHERE U.UserId = ?
UNION ALL
SELECT Bu.BusinessUnitId,
Bu.BusinessName,
Bu.ParentBusinessUnitId
FROM UserBusinessUnits Uu
INNER JOIN BusinessUnits Bu ON Bu.ParentBusinessUnitID = Uu.BusinessUnitId)
SELECT DISTINCT
BusinessUnitID,
BusinessName,
ParentBusinessUnitID
FROM UserBusinessUnits
あなたは、ユーザーが権限を持っているBusinessUnitオブジェクトのコレクションをマテリアライズするには、以下のようなコードを使用します。
あなたのSQLはこのようなものになるだろう。
bm.BusinessUnits.SqlQuery(mySqlString, userId);
上記の行と@Jeffreyが提案した非常に類似したコードとの間には微妙な違いがあります。上記のものはDbSet.SqlQuery()を使用し、彼の使用方法はDatabase.SqlQueryです。後者は、コンテキストによって追跡されないエンティティを生成しますが、前者は(デフォルトでは)エンティティを追跡します。トラッキングされたエンティティは、変更を作成して保存する機能、およびナビゲーションプロパティの自動修正機能を提供します。これらの機能が必要ない場合は、変更トラッキングを無効にしてください(.AsNoTracking()またはDatabase.SqlQueryを使用)。
概要
何が最も効果的である方法を決定するために設定され、現実的なデータでテストに勝るものはありません。手作業で作成したSQLコード(オプション3)を使用すると、パフォーマンスは最高になりますが、移植性の低い複雑なコードを犠牲にしています(基本的なdbテクノロジに結びついているため)。
利用可能なオプションは、使用しているEFの "味"と、もちろん選択したデータベースプラットフォームによって異なります。これを説明する具体的なガイダンスをご希望の場合は、追加情報で質問を更新してください。
- どのデータベースをお使いですか?
- EDMXファイルを使用してプロジェクトを作成するか、最初にコードを作成しますか?
- EDMXを使用している場合は、デフォルト(
EntityObject
)のコード生成手法、つまりT4テンプレートを使用しますか?
参照.Single ();))、この仮定はOPには述べられていない。 –
@Andy: 'Single()'の呼び出しは、有効なプライマリキーを使用して単一のユーザを選択することです。パーミッションへの参照は '.Include()'メソッドにあります。その結果、ユーザのすべてのパーミッションがクエリに含まれます。 – Olly
あなたは..私の悪い! –