我想强制执行一个要求,即在任何时候与数据库交互。用户,您需要调用特定的扩展方法。以下是一些更具体的信息。
当通过实体框架与我的数据库交互时,我有以下查询(db是DbContext实例):
var user = db.Users
.FromOrganisation(someId)
.FirstOrDefault(u => u.Username == someUsername);
在整个应用程序中,这个查询会一直变化(WHERE子句通常会不同,等等)。因此,我想要求您必须始终调用FromOrganisation()扩展方法,以便所有返回的数据首先由Organisation过滤。
这是为了防止任何人看到属于不同组织的数据,但我一直纠结于如何实现它
有没有一个单元测试我可以写来提醒开发人员,用户数据库集是在没有组织过滤的情况下使用的?如果没有,我有没有其他途径可以达到同样的保护水平。
如果它很重要,扩展方法本身看起来是这样的:
public static IQueryable<User> FromOrganisation(this IQueryable<User> u, int organisationId)
{
return u.Where(x => x.OrganisationId == organisationId);
}
我的最终解决方案
我更改了我的上下文如下:
public DbSet<User> Users { get; set; }
名称:
[Obsolete("MUST use service!")]
public DbSet<User> UsersUnfiltered { get; set; }
public IQueryable<User> Users(int id)
{
#pragma warning disable 618
return UsersUnfiltered.Where(x => x.OrganisationId == id);
#pragma warning restore 618
}
这样做的目的是鼓励您使用Users方法返回按组织筛选的用户列表。这可以像往常一样进行进一步的过滤、连接和查询等。
如果需要,您也可以访问UsersUnfiltered DbSet,但如果使用它,则会生成编译器警告。您可以通过在#pragma warning-disable618指令中访问它来抑制此警告。
有了这个,你就有了代码,可以防止你在没有按组织过滤的情况下使用用户数据,除非你真的想这样做。
感谢@mark_h帮助我找到这个解决方案。
您提出的建议的一个问题是,如果您可以强制使用它,那么每次您想要子查询一组用户时,即使在您从上下文(即)进行了初始查询之后,也必须强制使用它
var allUsersOfOrganisation1 = db.Users.FromOrganisation(1)
// some code...
var someUsersOfOrganisation1 = allUsersOfOrganisation1.FromOrganisation(1).Where(...)
如果强制使用扩展方法,如何区分对包含所有用户的集合的查询和筛选的子集?
您可以通过包装实体模型并仅通过强制执行唯一组织id的方法公开用户来实现您想要的结果;
public class WrappedEntities
{
private MyEntityFramework Entities = new MyEntityFramework();
//regular public entities
public DbSet<Organisation> Organisations { get { return Entities.Organisation; } set { Entities.Organisation = value as DbSet<Organisation>; } }
//Special case
public IQueryable<User> Users(int id)
{
return Entities.Users.Where(x => x.OrganisationId == organisationId);
}
//other wrapped methods...
public int SaveChanges()
{
return Entities.SaveChanges();
}
}
如果你给这个类一个接口,你也可以为了单元测试的目的模拟它。