1

次のエンティティを持つシステムがあるとします。

public class Doctor
{
    public int ID { get; set; }
    public int DepartmentID { get; set; }
    public string Name { get; set; }
    public ICollection<Recipe> Recipes { get; set; }
}

public class Patient
{
    public int ID { get; set; }
    public string Name { get; set; }
    public ICollection<Recipe> Recipes { get; set; }
}

public class Recipe
{
    public int ID { get; set; }
    public int DoctorID { get; set; }
    public int PatientID { get; set; }
    public Doctor Doctor { get; set; }
    public Patient Patient { get; set; }
    public ICollection<RecipeDetails> Details { get; set; }
}

public class RecipeDetails
{
    public int ID { get; set; }
    public Guid SomeGuid { get; set; }
    public double SomeValue { get; set; }
}

また、要件があります。

  • 医師は自分のレシピを編集できるはずです
  • 医師は、自分の部門内の医師からのレシピのみを表示できるようにする必要があります
  • 医師は、利用可能なレシピで検索を実行できる必要があります
  • 医師は、利用可能なレシピの詳細によってレポートを生成できる必要があります

今のところ、次のセキュリティ チェックを実装しました。

public void ValidateAccess(Doctor doctor, Recipe aRecipe, EntityAction action)
{
    if (action == EntityAction.Modify && doctor.ID == aRecipe.Doctor.ID)
        return;
    if (action == EntityAction.Read && doctor.DepartmentID == aRecipe.Doctor.DepartmentID)
        return
    throw new SecurityException();
}

これは、受信エンティティがある場合の単純なメソッドに最適です。ロジック メソッドの先頭でこのメソッドを呼び出すことで、アクセスを簡単に検証できます。

しかし、今私は問題を抱えています.正確なエンティティを持っていないが、いくつかの統計がある場合、このソリューションは検索とレポートでは機能しません。

コンポーネント「someGuid」を持つレシートを持つ名前「aName」の患者のレポートを生成したいと想像してみましょう。2 つの基準を持つクエリがあります。

var res = RecipeRepository.Query(r => aName.Contains(r.Patient.Name)).SelectMany(r => r.Details).Where(d => d.SomeGuid == someGuid).Sum(d => d.SomeValue);

このクエリは正しくありません。非表示にするレシピを含むすべてのレシピの統計が表示されます。これを修正するには、クエリにアクセス条件を追加する必要があります。

currentDoctor.DepartmentID == r.Doctor.DepartmentID

だから今私はクエリを持っています:

var res = RecipeRepository.Query(r => aName.Contains(r.Patient.Name) && currentDoctor.DepartmentID == r.Doctor.DepartmentID).SelectMany(r => r.Details).Where(d => d.SomeGuid == someGuid).Sum(d => d.SomeValue);

問題は、領収書の計算を行うシステム内の各クエリにこの部分を追加する必要があることです。

更新 (2012 年 11 月 12 日):

最初の例は非常に単純で、StuartLC が投稿で述べたように解決できます。しかし、私たちのシステムにはもっと複雑なレポートがあります。たとえば、レシピにコンポーネント someGuid を持つすべての患者を表示します。ここで、クエリは別のリポジトリから開始されるため、RecipeRepository のプライベート メソッドまたは保護されたメソッドを適用できません。サンプルクエリは次のとおりです。

var res = PatientRepository.Query(p => p.Name.Contains(aName) && p.Recipes.Any(r => r.Details.Any(d => d.SomeGuid == someGuid)));

この場合、フィルターをクエリに直接追加する必要があります。

var res = PatientRepository.Query(p => p.Name.Contains(aName) && p.Recipes.Any(r => currentDoctor.DepartmentID == r.Doctor.DepartmentID && r.Details.Any(d => d.SomeGuid == someGuid)));

更新を終了します。

このソリューションをより簡単にし、各クエリへの式のコピーと貼り付けを防ぐために適用できるパターンまたはプラクティスは何ですか? 回答とアドバイスをいただければ幸いです。

4

1 に答える 1

1

リポジトリ パターンQuery()メソッドが実体化されていない を返す場合IQueryable<T>、データ アクセス制限の問題を、「制限可能な」エンティティごとに 1 つずつヘルパー メソッドにリファクタリングできます。次に例を示します。

private IQueryable<Recipe> ApplyAccessFilters(IQueryable<Recipe> query, User user)
{
    IQueryable<Recipe> filteredQuery = query;

    // Custom method to determine if user is restricted to just his / her recipes
    if (!CheckUserPermission(currentUser, Access.MaySeeAllRecipies) ))
    {
        filteredQuery = filteredQuery
                              .Where(r => r.DepartmentId = currentUser.DepartmentId)
    } // Else no restriction, e.g. Admin Users can access all recipes

    // Other access related filters here

    return filteredQuery;
}

アクセス制限を必要とする各 MVC コントローラー アクションは、このメソッドを使用して、次のような結果のフィルター式を構築できます。

var recipes =  RecipeRepository.Query(r => r.SomeFields == someFilters); // NB, do NOT materialize the lambda
var recipesForDoctor =  ApplyAccessFilters(recipes, currentUser) // Access Filter
...
return View(recipesForDoctor); // [AsEnumerable()] - Consider materializing here

ページネーションなどの他の問題も同じ方法で処理できます。

さらに良いことに、このアクセス フィルターをfluentにすることができます。その場合、フィルターは目に優しくなります。

return View(RecipeRepository
            .Query(r => r.SomeFields == someFilters)
            .ApplyAccessFilters(currentUser)
            .Paginate(pagingInfo)
            .AsEnumerable());
于 2012-11-08T09:38:03.230 に答える