私はしばらくの間プロジェクトに取り組んできました。最初は、一握りの本やオンライン記事でたくさんのパターンについて読んだ後、プログラムのアーキテクチャの計画に多くの時間を費やしました。EF5.0>リポジトリ/作業単位>MVVM>WPFを組み合わせることにしました。
私の最初のテストでは、それが完全に機能するように見えたので、私は決めたパターンにコミットし、それを実装するのに1か月を費やしました。トラブルが発生しているのは、もうすぐ終わりです。
解決できない問題の1つは、複雑なクエリを作成できないように見えることです。何をしようとしても、エラーが発生します。
このプログラムは、Entity Framework 5.0を使用して、当社のMSSql2005サーバー上のデータベースに接続します。そこから、エンティティごとにリポジトリクラスが存在します。各リポジトリは、そのコンストラクタでコンテキストを受け取り、標準のインターフェイスを実装します。
interface IRepository<T> where T : class
{
IEnumerable<T> GetAll();
IEnumerable<T> Query(Expression<Func<T, bool>> filter);
void Add(T entity);
void Remove(T entity);
}
基本クラスは次のようになります。
public abstract class Repository<T> : IRepository<T> where T : class
{
protected IObjectSet<T> _objectSet;
public Repository(ObjectContext context)
{
_objectSet = context.CreateObjectSet<T>();
}
public IEnumerable<T> GetAll()
{
return _objectSet;
}
public List<T> ToList()
{
return _objectSet.ToList<T>();
}
public IEnumerable<T> Query(Expression<Func<T, bool>> filter)
{
return _objectSet.Where(filter);
}
public void Add(T entity)
{
_objectSet.AddObject(entity);
}
public void Remove(T entity)
{
_objectSet.DeleteObject(entity);
}
public void Update(T entity)
{
_objectSet.UpdateModel(entity);
}
public abstract void Upsert(T entity);
}
具体的なリポジトリは次のようになります。
public class UserAccountRepository : Repository<UserAccount>
{
public UserAccountRepository(ObjectContext context)
: base(context)
{
}
public UserAccount GetById(int id)
{
return _objectSet.SingleOrDefault(user => user.ID == id);
}
public override void Upsert(UserAccount user)
{
if (user == null)
throw new ArgumentNullException();
if (user.ID == 0 || GetById(user.ID) == null)
Add(user);
else
Update(user);
}
}
次はUnitOfWorkクラスです。このクラスは、各リポジトリの遅延ロードされたインスタンスを保持し、UnitOfWorkの存続期間中存続する独自のObjectContextをインスタンス化します。このコンテキストは、コンストラクターのリポジトリーに渡されます。
UnitOfWorkクラスはインスタンス化され、変更はパブリックにアクセス可能なリポジトリを介して行われ、その後、UnitOfWorkクラスのパブリックメソッドを介して保存または破棄されます。
UnitOfWorkクラス:
(私はUserAccount、ComponentPermissions、Componentのみを含めました。UserAccountは、中間テーブルComponentPermissionsで維持されている、さまざまなレベルのアクセス許可を持つ複数のコンポーネントにアクセスできます)
public class UnitOfWork : IDisposable
{
#region Fields
private readonly ObjectContext _context;
private ComponentPermissionsRepository _componentPermissions;
private ComponentRepository _components;
private UserAccountRepository _userAccounts;
#endregion //Fields
#region Constructor
public UnitOfWork()
{
_context = (new DataAccess.Entities() as IObjectContextAdapter).ObjectContext;
}
#endregion //Constructor
#region Public Interface
public ComponentPermissionsRepository ComponentPermissions
{
get
{
if (_componentPermissions == null)
{
_componentPermissions = new ComponentPermissionsRepository(_context);
}
return _componentPermissions;
}
}
public ComponentRepository Components
{
get
{
if (_components == null)
{
_components = new ComponentRepository(_context);
}
return _components;
}
}
public UserAccountRepository UserAccounts
{
get
{
if (_userAccounts == null)
{
_userAccounts = new UserAccountRepository(_context);
}
return _userAccounts;
}
}
public void Commit()
{
_context.SaveChanges();
}
public void Dispose()
{
_userAccount = null;
_component = null;
_componentPermissions = null;
_context.Dispose();
}
#endregion //Public Interface
}
EFでのクエリに関する記事をたくさん読んだのですが、上記のアーキテクチャに適用した場合、読んだものは何も機能しないようです...そして、自分の足を撃ったようなひどい気持ちがありますが、私はどこがわからない。
単一のUserAccountを指定して、アクセス可能なコンポーネントと対応するComponentPermissionsのリストを取得したいと思います。この種のクエリを実行するにはどうすればよいですか?EFナビゲーションプロパティは、直接の隣人に対してのみ機能するようです...私は明らかな何かを見逃していますか?
編集
私は自分の問題をもっと具体的にすべきだと指摘されています。私が克服できない最初のハードルは、すべての例が次のように見えることです。
context.Component.Include( c => c.ComponentPermissions.Select(cps => cps.UserAccount)).ToList();
見た目は良さそうですが、コンテキストを公開していません。公開しても、Componentのようなエンティティタイプはありません。しかし、GetAll()をIQueryableに変更したので、次のことをまとめることができました。
List<Component> comps = uow.Components.GetAll().Include(c => c.UserPermissions.Select(cps => cps.UserAccount)).ToList();
ComponentPermissions compPer = comps[0].UserPermissions.Select(up => up).Where(up => up.UserAccount.UserName == userAccount).FirstOrDefault();
これにより、特定のuserAccountに関連する最初のComponentPermissionsオブジェクトが返されます...しかし、これは、たとえそれがある程度機能したとしても、非常に間違っているように見えます(つまり、すべてではなく、1つのComponentPermissionsオブジェクトしか取得しなかったためです。それら...しかし、私はそれを行う方法を理解することができませんでした)
それで、それを見て、誰かがそれをする正しい方法を説明できますか?