我正在使用Entity framework 5并使用存储库模式。假设我有这些实体客户,文件,图像,任务,发票,用户。
每个实体(除客户外)都有客户的外键。当用户登录时,我将customerid存储在会话中(aps.net mvc)。我想要的是所有实体上的CRUD仅限于用户登录的客户。例如,我不能删除属于客户1的任务,由来自客户2的用户删除。
为每个存储库方法添加customerid参数是实现此目的的最佳方法还是有更好/更聪明的方法?
答案 0 :(得分:1)
Tricky给出一个明确的答案,但你可以通过实现更高的顺序函数使它更具可扩展性,如下所示:
public interface IRepository<T>
{
public T GetBy(Expression<Func<T, bool>> query)
}
public class FileRepository : IRepository<File>
{
public File GetBy(Expression<Func<T, bool>> query)
{
using(var context = new FilesContext())
{
return context.Files.Where(query).FirstOrDefault();
}
}
}
public class SomeController
{
private IRepository<File> _repo;
public SomeController(IRepository<File> repo)
{
_repo = repo;
}
public ActionResult Index()
{
var model = _repo.GetBy(f => f.CustomerId == Session.Whatever.CustomerId);
return View(model);
}
}
这样,您可以在需要时更改搜索查询,而不是将自己绑定到使用硬编码的客户ID属性。例如,如果您想通过FileID获取File对象,而不是CustomerID,那么:
var model = _repo.GetBy(f => f.FileId == someId);
这是代码中唯一需要更改的部分。
关于C#中的高阶函数和函数式编程的一些非常好的信息:http://www.codeproject.com/Articles/375166/Functional-programming-in-Csharp
修改强>
您可以使用装饰器样式模式将“在使用DB时始终使用客户ID”隔离到其自己的存储库中,从而:(大量免责声明 - 我没有对此进行了测试,但这些方面的内容应该有效)
public class SpecialFileRepo : IRepository<File>
{
private readonly IRepository<File> _baseRepo;
public SpecialFileRepo(IRepository<File> baseRepo)
{
_baseRepo = baseRepo;
}
public SpecialFileRepo() : this(new FileRepository())
{
}
public File GetBy(Expression<Func<File, bool>> query)
{
var parameters = query.Parameters;
var newParam = Expression.Parameter(typeof (File), "f");
var additionalQuery = Expression.AndAlso(query.Body,
Expression.Equal(
Expression.PropertyOrField(newParam, "CustomerId"),
Expression.Constant(HttpContext.Current.Session["customerId"])));
var newQuery = query.Update(additionalQuery, parameters);
return _baseRepo.GetBy(newQuery);
}
}
然后,任何与存储库交谈的内容,就其而言,它只是一个基本存储库,但是这个类位于中间并始终将“customerid = sessionwhatever”表达式移植到最终传递给数据库的内容上。当然,任何只关心使用基础存储库的东西仍然可以这样做。