如何隔离业务逻辑以避免覆盖控制器?

时间:2012-12-06 01:35:41

标签: asp.net-mvc-3 autofac multi-tenant

来自http://code.google.com/p/autofac/wiki/MultitenantIntegration

的文档
  

由于相对复杂,因此可能更好   将业务逻辑隔离到传入的外部依赖项中   您的控制器,以便租户可以提供覆盖依赖项   而不是覆盖控制器。

假设我有一个包含许多操作的默认控制器,Index操作会获得产品,库存和商店的完整数据列表。 我的TenantB希望使用相同的控制器进行许多操作,但对于Index操作,它还希望获得销售列表。

在我看来,我可能不需要覆盖整个控制器进行这么小的改动。这是上面引用的句子所指的内容吗?我可以将一些外部方法注入到索引中,对于默认租户而言,这将是一种返回产品,库存和商店的方法,而TenantB会覆盖该方法并返回销售额吗?

如果我的假设是正确的,有人知道某些示例代码可以说明这种类型的实现吗?

1 个答案:

答案 0 :(得分:1)

这句话背后的想法是你将Index动作中的逻辑隔离成一个单独的依赖。

也就是说,现在你可能有一个像这样的控制器:

public class MyController : Controller
{
  protected IDataService _dataService;
  public MyController(IDataService dataService)
  {
    this._dataService = dataService;
  }
  public virtual ActionResult Index()
  {
    var data = this._dataService.GetData();
    var model = new IndexModel()
    {
      Data = data
    };
    return this.View(model);
  }
  public virtual ActionResult OtherAction()
  {
    // other stuff...
  }
}

在该索引操作中,您将获取数据,并在将数据传递给视图之前对数据执行一些业务逻辑。

使用这样的设计,您的租户特定需要获得一些额外的数据意味着您必须覆盖控制器。这实际上并不太难,如果您决定为其他服务依赖项执行属性注入,它应该可以工作。

public class TenantSpecificController : MyController
{
  // If you set up PropertiesAutowired on the tenant controller
  // registration, you'll get this property populated.
  public IOtherService OtherService { get; set; }
  public TenantSpecificController(IDataService dataService)
    : base(dataService)
  {
  }
  public override ActionResult Index()
  {
    var data = this._dataService.GetData();
    var otherData = this.OtherService.GetData();
    var model = new IndexModel()
    {
      Data = data
    };
    // You can't really change the model without changing the
    // view, so extended data goes in the ViewData or ViewBag
    this.ViewData["other"] = otherData;
    return this.View(model);
  }
}

但是文档中的那个句子暗示的是,如果你知道你将在该控制器中有一些花哨的租户覆盖逻辑(不仅仅是一次性,因为控制器覆盖很容易在一个然后将逻辑拉出控制器,如下所示:

public class MyController : Controller
{
  protected IIndexModelGeneratorService _modelService;
  public MyController(IIndexModelGeneratorService modelService)
  {
    this._modelService = modelService;
  }
  public virtual ActionResult Index()
  {
    var model = this._modelService.BuildModel();
    return this.View(model);
  }
  public virtual ActionResult OtherAction()
  {
    // other stuff...
  }
}

然后,您不会在控制器/操作级别执行租户覆盖,而是在特定服务上进行租户覆盖。您将业务逻辑从控制器中拉出来进入其他依赖项。

显然,这可能意味着更改模型以允许更多可扩展的数据......

public class IndexModel
{
  public DataObject Data { get; set; }
  public Dictionary<string, object> ExtendedData { get; set; }
}

...或者您的数据服务实现可能需要填充字典:

public interface IIndexModelGenerator
{
  IndexModel BuildModel(ViewDataDictionary dict);
}

...所以当你调用它时,你的控制器会传入ViewData字典来捕获额外的数据:

var model = this._modelService.BuildModel(this.ViewData);

这个想法仍然存在 - 如果你有很多这样的想法,那么将业务逻辑划分为多租户组件可能比将每个租户拥有不同控制器更容易。