我试图将NHibernate用于带有旧数据库的新应用程序。它进展得很顺利,但我已经陷入困境,无法找到问题的良好解决方案。
我们说我有这个模型:
所以我对此进行了映射,一切顺利。现在我可以检索一部电影,获取所有相关的内容,...... 我的应用程序是一家电影商店" generator"。每个"服务"实际上是一个不同的商店,当用户进入我的网站时,他被重定向到其中一个商店,显然,我必须向他展示他的商店可用的电影。这个想法是:用户来了,他的服务得到认可,我向他展示了与他的服务相关的内容。我需要能够为后台检索电影的所有内容。 我试图通过NHibernate找到最透明的方法来实现这一目标。我无法真正对数据库模型进行更改。
我想了几个解决方案:
将服务条件添加到我的所有查询中。会工作,但它有点麻烦。该模型非常复杂,有大量的表/查询..
使用nhibernate过滤器。看似理想并且工作得很好,我在所有映射中添加了serviceid上的过滤器,并在我的用户服务被识别后立即执行了EnableFilter但是... nhibernate过滤的集合不适用于第二个lvl缓存(在我的情况下redis)和第二个lvl缓存使用是必需的。
将计算属性添加到我的对象,如Movie.PublishedContents(Int32 serviceId)。可能会工作但需要编写大量代码和#34;污染"我的域名。
添加从我的nhibernate实体继承的新实体,如PublishedMovie:仅显示上下文数据的电影
这些都不能让我满意。有没有办法做到这一点?
谢谢!
答案 0 :(得分:1)
您正在询问同一数据库中所有租户的多租户问题。我使用Ninject依赖注入有效地处理了这种情况。在我的应用程序中,租户被称为“手动”,我将在示例代码中使用它。
路线需要包含承租人,例如
{manual}/{controller}/{action}/{id}
可以在租户上设置约束以限制允许的租户。
我使用Ninject在每个请求会话策略中配置和提供ISessionFactory作为单例和ISession。这是使用Ninject Provider类封装的。
我使用轻量级存储库类进行过滤,例如
public class ManualRepository
{
private readonly int _manualId;
private readonly ISession _session;
public ManualRepository(int manualId, ISession session)
{
_manualId = manualId;
_session = session;
}
public IQueryable<Manual> GetManual()
{
return _session.Query<Manual>().Where(m => m.ManualId == _manualId);
}
}
如果您需要漂亮的网址,则需要将租户路由参数转换为相应的数据库值。我在web.config中设置了这些,并在启动时将它们加载到字典中。 IRouteConstraint实现读取“手动”路由值,查找它,并设置“manualId”路由值。
Ninject可以处理将ISession注入存储库并将存储库注入控制器。控制器操作中的任何查询都必须基于存储库方法,以便应用过滤器。诀窍是从路由值注入manualId。在NinjectWebCommon中,我有两种方法可以实现这一目标:
private static int GetManualIdForRequest()
{
var httpContext = HttpContext.Current;
var routeValues = httpContext.Request.RequestContext.RouteData.Values;
if (routeValues.ContainsKey("manualId"))
{
return int.Parse(routeValues["manualId"].ToString());
}
const string msg = "Route does not contain 'manualId' required to construct object.";
throw new HttpException((int)HttpStatusCode.BadRequest, msg);
}
/// <summary>
/// Binding extension that injects the manualId from route data values to the ctor.
/// </summary>
private static void WithManualIdConstructor<T>(this IBindingWithSyntax<T> binding)
{
binding.WithConstructorArgument("manualId", context => GetManualIdForRequest());
}
声明存储库绑定以注入manualId。通过惯例可能有更好的方法来实现这一目标。
kernel.Bind<ManualRepository>().ToSelf().WithManualIdConstructor();
最终结果是查询遵循模式
var manual = _manualRepository
.GetManual()
.Where(m => m.EffectiveDate <= DateTime.Today)
.Select(m => new ManualView
{
ManualId = m.ManualId,
ManualName = m.Name
}).List();
我不需要担心在我的查询中过滤每个租户。
至于二级缓存,我不在这个应用程序中使用它,但我的理解是你可以设置缓存区域来隔离租户。这应该让你开始:http://ayende.com/blog/1708/nhibernate-caching-the-secong-level-cache-space-is-shared