2014-10-04 147 views
0

我正在构建一个ASP Web API应用程序,这次我想我将使用MVC模式。我和大部分东西相处得很好,但有一件事我不确定。首先我所有的项目包括以下内容:C#(Web API)多层(IOC)API控制器返回类型

  • 数据层
  • 业务层
  • 模型层(只是模型的属性)
  • 服务应用(在这里是我的控制器)

    每个人都在一个单独的项目中

可以说我有以下控制器

public class TestController : ApiController 
{ 
    ISomeService _someBusiness; 

    public TestController(ISomeService someBusiness) 
    { 
     _someBusiness = someBusiness; 
    } 

    public **SomeModelObject** GetModelObject(ind id) 
    { 
     return _someBusiness .GetSomeModelObject(id); 
    } 

} 

现在我的问题是返回值为GetModelObject(int id)。这里写着SomeModelObject。这意味着我的Service应用程序(或我的控制器)必须知道正在使用的模型的所有内容(所以我没有看到在单独的.dll中定义它的要点)。一种方法是将模型(准确地说是get/set方法)定义为接口,但我认为每个模型类都有一个接口太多了(主要是因为,正如我所说的,只是属性被存储在模型中),尽管如此,我只是觉得不适合为只存储数据的类创建接口。那么,在这种情况下是否有任何通用响应类型(甚至是一些完全不同的方法),还是我必须使用我的模型类(或者我可能总是使用字符串,并且它正在被转换为适当的格式客户端) ?

+0

我不明白为什么你有一个问题引用模型层的DLL从服务。这是很正常的事情。 – Fordio 2014-10-04 10:46:59

回答

0

使用接口来隐藏模型对象的复杂性有很好的理由。它保存数据,当然。但是它包含了仅对数据层有意义的不必要数据。以此EF型号:

public class Employee 
{ 
    public int Id { get; set; } 
    public string EmployeeNumber { get; set; } 
    public string Name { get; set; } 
    public virtual Collection<TimeCard> TimeCards { get; set; } 

    public int DepartmentId { get; set; } 
    public virtual Department Department { get; set; } 
} 

这是一个仙女常见的EF模型。它包含一个代理键Id和一个外键DepartmentId。这些值除了数据库和扩展后的实体框架之外毫无意义。 EmployeeNumber是唯一标识用户域中实体的自然键。

数据库访问之外,你应该只处理自然数据值。您可以通过在Business层中声明另一个携带数据的类并执行映射来做到这一点,或者更好的办法是使用一个接口来隐藏所有无用的成员。

public interface IEmployee 
{ 
    string EmployeeNumber { get; } 
    string Name { get; set; } 

    ICollection<ITimeCard> TimeCards { get; } 
    IDepartment Department { get; set; } 
} 

注意接口中缺少一些setter。您永远不会想要更改EmployeeNumber,因为这是实体的自然键。同样,您永远不会将收集对象分配给TimeCards属性。你只能迭代,添加或删除它们。

现在你的Employee类成为

public class Employee : IEmployee 
{ 
    public int Id { get; set; } 
    public string EmployeeNumber { get; set; } 
    public string Name { get; set; } 
    public virtual Collection<TimeCard> TimeCards { get; set; } 
    ICollection<ITimeCard> IEmployee.TimeCards { get { return TimeCards; } } 

    public int DepartmentId { get; set; } 
    public virtual Department Department { get; set; } 
    IDepartment IEmployee.Department { get { return Department; } set { Department = value; } } 
} 

在业务层及以上的,你只用IEmployee,IDepartment和ITimeCard的变量。因此,您将更紧密的API暴露给更高层,这是一件好事。

+0

感谢您的回答,我会像这样实施它,看看它是否符合我的需求。还有一个问题。我已将所有接口放入单独的项目(Interfaces.dll)中。这是正确的方法,或者我应该让他们与实际的层(模型层的接口应该在modellayer.dll等内)?这两种方法都有它们的优缺点,但是我选择了一个单独项目中的所有项目。 – user3466562 2014-10-04 12:52:42

+0

最好将它们放在单独的项目中,但并不重要。如果一切都在一个解决方案中,稍后很容易将它们转移到新项目中。 – 2014-10-04 22:28:14

+0

当我使用ISomeObject作为返回类型时,我在浏览器中出现以下异常:使用数据协定名称'SomeObject:schemas.datacontract.org/2004/07/ModelLayer'输入'ModelLayer.SomeObject';没有预料到。考虑使用DataContractResolver或将任何不知道的类型静态添加到已知类型的列表中 - 例如,使用KnownTypeAttribute属性或将它们添加到传递给DataContractSerializer的已知类型的列表中。 System.Runtime.Serialization.SerializationException我需要在界面内定义一些序列化吗? – user3466562 2014-10-05 11:24:31

0

你可以尝试使用在控制器级别的通用方法:

public class BusinessController<T> : ApiController 
{ 
    ISomeService _someBusiness; 

    public TestController(ISomeService someBusiness) 
    { 
     _someBusiness = someBusiness; 
    } 

    public T GetModelObject(ind id) 
    { 
     return _someBusiness.GetSomeModelObject(id); 
    } 

} 

最后你controlers从BusinessController继承,而不是ApiController:

public class TestController : BusinessController<SomeModelObject> 
{ 
} 

你也可以把模板的提前注入通过使用IoC容器和引导程序来实现正确的“ISomeService”。

+0

这实际上是我从来没有见过的东西。这整个依赖注入(特别是如果你有很多类)进展表现如何?现在我有大约10个类,我是doint依赖注入(与Unity),我想知道这是如何影响性能(或者它只是影响初始启动的应用程序)? – user3466562 2014-10-04 13:00:26

+0

依赖注入是一种技术来提高mainaitability和架构。这是SOLID原则中的“D”。关于表现,如果你做得很好,没有可衡量的惩罚。开销在“解决”Ioc容器方法上,取代了常规的“新”。如果您不打算创建数千个对象,并且对象实例化时间并不是非常重要,那么您可以依赖IoC而不需要任何外观。 – 2014-10-04 13:02:59