2013-11-26 42 views
0

作为一个简单的例子,我有用户,产品和客户。用户可以访问某些产品和某些客户。如何保持客户数据隔离

我正在使用edmx文件将我的SQL Server映射到我的代码并使用linq获取数据。一个典型的查询可能是这个样子:

from prod in ctx.Products 
join userProduct in ctx.UserProduct 
on prod.Id equals userProduct.ProductId 

join user in ctx.UserProfile 
on userProduct.UserId equals user.Id 

where user.UserName == username // <-- username is a method parameter 
select new Product 
{ 
    Id = prod.Id, 
    DisplayText = prod.UserFriendlyText 
} 

每次我需要从我必须加入对访问权限表的数据库中的数据来排除数据的用户没有访问。这意味着如果有人(最终会发生)忘记加入访问表,用户将看到太多。有没有办法来INCLUDE数据,所以如果我忘记访问表什么都没有显示?

我也一直在考虑将不同的客户分成不同的数据库,因为他们的数据永远不会彼此相关,如果我在客户之间泄漏数据,这将是一个小小的灾难。来自同一客户的用户之间泄漏的产品很差,但并不重要。

如果它很重要我在C#MVC4 CQRS体系结构中最终读写一致性。

我检查堆栈溢出类似的问题,但所有我能找到的是这个没有答案之一:

回答

1

如何使用Repository pattern,并迫使你开发的用它来打电话给数据库?这将促进代码重用并提高应用程序的可维护性。

因为将从存储库中调用一个方法,您可以控制与数据库交互的代码并强制一致性,这样可以确保始终使用访问表并按您的意愿使用。

+0

我想一个简单的存储库模式,如方法,接受用户名作为参数,并且只返回在'IQueryable'中有权访问的产品可以工作,但它仍然像我一样错误地可以查询产品而不加入访问权限表。 –

+1

然后让你的开发者不能直接查询产品。例如,仅为您的模块和数据库API提供Repository接口(然后在运行时使用依赖注入来提供实际的Repository服务)。 –

0

我在我的数据库中有类似的问题。我的实体中有90%是“依赖于组织”的。我的方法使用类似这样的方法的通用基础知识库:

public virtual T Find(int id) 
    { 
     T e = Context.Set<T>().Find(id); 

     var od = e as OrganisationDependent; 
     if (od != null && od.OrganisationID != CurrentOrganisationID) 
      return null; 

     if (e == null) 
      return null; 

     return e; 
    } 

“全部”方法是一个特殊问题。通过How to conditionally filter IQueryable

private static readonly PropertyInfo _OrganisationIDProperty = ReflectionAPI.GetProperty<OrganisationDependent, int>(o => o.OrganisationID); 

    private static Expression<Func<TOrg, bool>> FilterByOrganization<TOrg>(int organizationId) 
    { 
     //The FilterByOrganisation method uses the LINQ Expressions API to generate an expression that will filter on organisation id 
     //This avoids having to cast the set using .AsEnumerable().Cast<OrganisationDependent>().Where(x => x.OrganisationID == CurrentOrganisationID).AsQueryable().Cast<T>(); 
     //https://stackoverflow.com/questions/20052827/how-to-conditionally-filter-iqueryable-by-type-using-generic-repository-pattern 
     var item = Expression.Parameter(typeof(TOrg), "item"); 
     var propertyValue = Expression.Property(item, _OrganisationIDProperty); 
     var body = Expression.Equal(propertyValue, Expression.Constant(organizationId)); 
     return Expression.Lambda<Func<TOrg, bool>>(body, item); 
    } 

    public virtual IQueryable<T> All 
    { 
     get 
     { 
      if (typeof(T).IsSubclassOf(typeof(OrganisationDependent))) 
       return Context.Set<T>().Where(FilterByOrganization<T>(CurrentOrganisationID)); 

      return Context.Set<T>(); 
     } 
    } 

解决。这将关闭大部分用户可以访问别人的数据的地方。但它不会过滤导航属性。所以我必须将代码添加到非组织依赖实体上的所有导航属性才能做到这一点。

我不想将我的数据分成不同的数据库,但有一天我会发现创建按不同模式组织过滤的视图是否可行 - 与我的表具有相同的名称和结构,然后切换模式根据用户.....哦,我想自动创建它们为每个新的组织和autmatically利用二维码的第一太迁移它们....

而且你可以投票Allow filtering for Include extension method这里

0

如果您您可以使用CQRS风格的体系结构,您可以考虑让每个用户拥有一个或多个包含他们有权访问的产品/客户的视图模型。

如果你看到自己不得不在CQRS的查询方面实现逻辑,这强烈地表明你做错了什么。

+0

谢谢Niklas我也有这样的感觉,在查询方面的逻辑是错误的。你能否详细说明你将如何做这样的每个用户拆分?例如,在处理事件或将事件交给相关订户之前会发生这种情况吗? –

+0

查询端的实际实现当然会受到存储引擎的影响,如果是关系,基于文档等等。 –

+0

如果不知道在什么情况下使用视图模型,您真的很难给出一个您需要的视图模型的例子,但这里的主要问题是不应该可以查询它并接收显示用户信息但不访问的结果。如果您使用的是documentDb,则可以将userId作为键并包含他们有权访问的产品/ ID列表。 ProductsPerCustomerViewModel或类似产品。然后,该事件需要包含有关viewmodelupdater应该如何知道访问权限的信息,或者在创建事件之前让您的域执行该逻辑 –