我目前正在使用Unity容器在现有的ASP.NET Web API项目中实现依赖项注入。
我已经设法通过配置依赖项解析器将我的服务类注入我的API控制器。
但是对于控制器功能,我必须使用数据传输对象(DTO) 在那个目标中,我无法找到如何使用我的模型合同。
以下是 Web API控制器方法:
[HttpPost]
[Route("api/application/save")]
public IHttpActionResult SaveApplication(ApplicationUpdateDTO applicationUpdate)
{
// Inner code calling service methods expecting IApplication and
// collections of ITag as parameters.
}
这是 DTO定义:
public class ApplicationUpdateDTO
{
public IApplication Application { get; set; }
public IEnumerable<int> DeletedTagIds { get; set; }
public IEnumerable<ITag> AddedTags { get; set; }
public IEnumerable<int> DeletedPlatformIds { get; set; }
public IEnumerable<ITag> AddedPlatforms { get; set; }
}
因此,DTO本身已初始化,但不是全部null
的属性。
我理解为什么不能设置属性:接口不能实例化,并且它没有任何线索可以使用哪些类。但是由于注册,我的Unity容器确实如此。
备注:
- 如果我在DTO中使用我的界面实现,那么显然可以正常工作。
- 控制器方法接收与我的DTO相同的JSON对象。
修改
我还参考this post尝试了ModelBinder
的实施
但对于关于ValueProviderResult
的行,我得到了null
值。
为方便起见,以下是托德在另一个问题中的答复:
public class CreateSomethingModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
string key = bindingContext.ModelName;
ValueProviderResult val = bindingContext.ValueProvider.GetValue(key);
if (val != null)
{
string s = val.AttemptedValue as string;
if (s != null)
{
return new CreateSomething(){Title = s; UserId = new Guid(ControllerContext.HttpContext.Request.Headers["userId"]);}
}
}
return null;
}
}
我从问题的回答中得到的微小差异是使用System.Web.Http.ModelBinding.IModelBinder
而不是MVC。
根据要求,以下是我的界面的简要说明 IApplication接口:
public interface IApplication
{
/// <summary>
/// Identifier of the application.
/// </summary>
int Id { get; set; }
/// <summary>
/// Name of the application.
/// </summary>
string Name { get; set; }
/// <summary>
/// Version of the application.
/// </summary>
string Version { get; set; }
/// <summary>
/// Tags associated to the application.
/// </summary>
ICollection<ITag> Tags { get; }
}
ITag界面:
public interface ITag
{
/// <summary>
/// Identifier of the tag.
/// </summary>
int Id { get; set; }
/// <summary>
/// Identifier of the application to which the tag is linked.
/// </summary>
int ApplicationId { get; set; }
/// <summary>
/// Value of the tag.
/// </summary>
string Value { get; set; }
}
JSON :
的示例{
"marketApplication": {
"Id": 20,
"Name": "MyApplication",
"Version": "2.0"
},
"deletedTagIds": [],
"addedTags": [
{
"Id": 0,
"Value": "NewTag"
}
],
"deletedProgramIds": [],
"addedPrograms": [
{
"Id": 0,
"Name": "x86"
}
]
}
答案 0 :(得分:2)
依赖注入是组合松耦合组件图形的实践。组件是系统中包含行为的类。
依赖注入并不意味着构建仅包含数据的对象。使用依赖注入我们构建组件图。在构建了该图之后(使用构造函数注入),我们使用方法调用将运行时数据传递给该图。
每次尝试使用依赖注入或DI容器(如Unity)时,都会遇到麻烦。因此,尽管您的问题表明您希望使用Unity执行此操作,但Unity应该被排除在等式之外(针对此特定情况)。
正如其他人已经说过的那样,通过请求构建的数据传输对象(DTO)是Web API的Model Binder的工作。默认的模型绑定器不能为您反序列化接口,这是非常明显的;他们应该反序列化什么实现?
虽然您可以替换默认的模型绑定器,但您应该退后一步,仔细查看您要实现的目标。你正在抽象数据。隐藏抽象背后的DTO通常没什么意义,因为接口意味着抽象行为。
因此,不是使用接口,而是使用具体类通常要好得多。
它可以节省来自&#34; sub-DTO&#34;手动具体的
更简单的方法是使用组合,而不是这样做。您可以使用较小的DTO组成DTO。这样可以避免完全复制。
使用我的Unity容器中注册的匹配类型。
这假设这些DTO应该在容器中注册,但同样,DI容器不应该保存任何运行时数据。这应该被禁止。或者如上所述here:
在构建过程中不要将运行时数据注入应用程序组件中;它会导致歧义,使组合根变得更加复杂,并且使得验证DI配置的正确性变得非常困难。我的建议是让运行时数据流过构造对象图的方法调用。
<强>更新强>
组合的想法很简单,您可以从较小的类构建类;而不是使用继承或复制对象结构。在您的情况下,这看起来如何显然取决于您的需求,但我想您希望将ITag
数据复制到另一个具有更多属性的类:
public class SomeObject
{
// Members:
public string Name { get; set; }
public string Description { get; set; }
// Members to copy from ITag
public int Id { get; set; }
public int ApplicationId { get; set; }
public string Value { get; set; }
// more members
}
相反,您可以从具体的SomeObject
DTO中撰写 Tag
:
public class SomeObject
{
// Members:
public string Name { get; set; }
public string Description { get; set; }
public Tag Tag { get; set; }
// more members
}
这样您就不必复制Tag
个成员;您只需要设置Tag
属性,并引用反序列化的Tag
DTO。