【发布时间】:2016-12-09 18:07:28
【问题描述】:
对不起,另一个多租户帖子。我找不到一个好的网站解决方案,我已经阅读了大量关于 ASP MVC 多租户的精彩帖子,但我仍然需要一些好的建议。
我有一个 ASP MVC Entity Framework 6 Code First Web 应用程序。这个应用程序必须为许多不同的客户使用一个数据库。
我对所有客户端都有一个实体,每个客户端可以有不同的主机。
public class Client
{
public int ClientId { get; set; }
public string Name { get; set; }
...
public ICollection<ClientHost> Hosts { get; set; }
}
public class ClientHost
{
public int ClientId { get; set; }
public Client Client { get; set; }
public string Name { get; set; }
}
我在所有需要过滤的实体中添加了一列“ClientId”,这样我就可以将来自不同客户端的数据分开。
public class SomeEntity
{
public int Id { get; set; }
...
public int ClientId { get; set; }
}
我需要的第一件事是,基于主机,检索要使用的 ClientId。
private static int GetClientId()
{
var currentClient = Convert.ToInt32(HttpRuntime.Cache[CacheClient]);
if (currentClient != null) return currentClient;
lock (Synclock)
{
using (var dataContext = new MyDataContext())
{
var urlHost = HttpContext.Current.Request.Url.Host;
currentClient = dataContext.Clients
.FirstOrDefault(p => p.Hosts.Any(h => h.Name == urlHost));
if (currentClient == null) return null;
HttpRuntime.Cache.Insert(CacheClient, currentClient, null, Cache.NoAbsoluteExpiration, TimeSpan.FromSeconds(0), CacheItemPriority.Default, null);
return currentClient;
}
}
}
问题 1
如您所见,我从 DB 获取 clientId 并将其存储在缓存中,因此我不必每次需要时都调用 DB。
我不知道是否有更好的方法来获取客户端 ID,或者更好地存储它。
编辑
经过调查,我在 DbCONtext 中创建了一个变量,并在 Startup.cs 文件中对其进行了初始化。
public class MyDataContext : IdentityDbContext<ApplicationUser, CustomRole, int, CustomUserLogin, CustomUserRole, CustomUserClaim>
{
public static string ClientId { get; set; }
public MyDataContext() : base("MyDataBase") { }
public static MyDataContext Create()
{
return new myDataContext();
}
....
}
在 Startup.cs 中
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
MyDataContext.ClientId = ClientConfiguration.GetCurrentClientId();
ConfigureAuth(app);
}
}
问题 2
获得 ClientId 后,我需要为每个需要它的查询添加一个过滤器。手动执行此操作可能会使您犯很多错误或在某些地方忘记执行此操作。
我需要一种方法,应用程序可以自动将过滤器添加到所有查询(仅限那些需要它的实体),因此我不必担心客户端获取其他客户端的数据。我还需要将 ClientId 添加到所有插入和更新命令中。
我已阅读过有关过滤和/或使用 EF 拦截器的信息,但在阅读了一些有关该内容的帖子后,我不知道该怎么做。在这里需要一些帮助。
提前致谢。
编辑
为了解决问题 2,我关注了 Xabikos 的这篇精彩文章: http://xabikos.com/2014/11/17/Create-a-multitenant-application-with-Entity-Framework-Code-First-Part-1/
我对其进行了一些更改,因为我不使用用户来获取当前租户,而是使用主机。这是程序的一部分,我还不知道我将如何解决,但假设我已经有了 ClientId,我可以为所有查询添加过滤器,而不会意识到正在发生这种情况:
我已经替换了所有的用户逻辑:
private static void SetTenantParameterValue(DbCommand command)
{
if (MyDataContext.ClientId == 0) return;
foreach (DbParameter param in command.Parameters)
{
if (param.ParameterName != TenantAwareAttribute.TenantIdFilterParameterName)
continue;
param.Value = MyDataContext.ClientId;
}
}
所有地方都一样...
我只需要标记必须使用 TenantAware 过滤的实体,指示属性。在这种情况下,我在我的基类中做,然后将该基类应用于我需要的所有实体。
[TenantAware("ClientId")]
public abstract class ClientEntity : Entity, IClientEntity
{
public int ClientId { get; set; }
public Client Client { get; set; }
}
【问题讨论】:
标签: entity-framework filter multi-tenant interceptor