如何在 ASP.NET Web API 和 Unity 中使用 DTO 中的接口?
How to use interfaces in DTO with ASP.NET Web API and Unity?
我目前正在使用 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 容器可以。
- 是否可以使用此 "link" 以某种方式初始化 DTO 属性?
- 有更好的方法吗?
Notes:
- If I use implementations of my interfaces in the DTO, it obviously works fine.
- The controller method receives a JSON object that is identical to my DTO.
编辑
参考.
我也尝试了一个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"
}
]
}
依赖注入是组合松散耦合组件图的实践。组件是系统中包含 行为 .
的 classes
依赖注入并不意味着构建仅包含数据的对象。使用依赖注入,我们构建了一个组件图。在构建该图(使用构造函数注入)后,我们使用方法调用通过该图传递运行时数据。
每次您尝试将依赖注入或 DI 容器(如 Unity)用于其他任何事情时,您都会遇到麻烦。因此,尽管您的问题表明您想使用 Unity 执行此操作,但应将 Unity 排除在外(对于这种特殊情况)。
正如其他人已经指出的那样,构建通过请求传入的数据传输对象 (DTO) 是 Web API 的模型绑定器的工作。默认的模型绑定器不能为你反序列化接口,这是很明显的;他们应该反序列化到什么实现?
尽管您可以替换默认的模型绑定器,但您应该退后一步,仔细查看您要实现的目标。您正在抽象掉数据。将 DTO 隐藏在抽象背后通常意义不大,因为接口旨在抽象 行为 。
因此,与其使用接口,不如使用具体的 classes 更好。
it would save me the copy from a "sub-DTO" to a concrete one manually
与其这样做,更简单的方法是使用组合。您可以将较小的 DTO 组合成 DTO。这将使您不必完全复制。
by using the matching type registered in my Unity container.
这假设那些 DTO 应该在容器中注册,但同样,DI 容器不应该保存任何运行时数据。这应该被排除在外。或者如所述 here:
Don't inject runtime data into application components during construction; it causes ambiguity, complicates the composition root with an extra responsibility and makes it extraordinarily hard to verify the correctness of your DI configuration. My advice is to let runtime data flow through the method calls of constructed object graphs.
更新
组合 的想法很简单,您可以从较小的 classes 构建 classes;而不是使用继承或复制对象结构。在您的情况下,这看起来如何显然取决于您的需要,但我想您希望将 ITag
数据复制到另一个具有更多属性的 class:
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
}
相反,您可以从具体的 Tag
DTO:
compose SomeObject
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。
我目前正在使用 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 容器可以。
- 是否可以使用此 "link" 以某种方式初始化 DTO 属性?
- 有更好的方法吗?
Notes:
- If I use implementations of my interfaces in the DTO, it obviously works fine.
- The controller method receives a JSON object that is identical to my DTO.
编辑
参考
我也尝试了一个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"
}
]
}
依赖注入是组合松散耦合组件图的实践。组件是系统中包含 行为 .
的 classes依赖注入并不意味着构建仅包含数据的对象。使用依赖注入,我们构建了一个组件图。在构建该图(使用构造函数注入)后,我们使用方法调用通过该图传递运行时数据。
每次您尝试将依赖注入或 DI 容器(如 Unity)用于其他任何事情时,您都会遇到麻烦。因此,尽管您的问题表明您想使用 Unity 执行此操作,但应将 Unity 排除在外(对于这种特殊情况)。
正如其他人已经指出的那样,构建通过请求传入的数据传输对象 (DTO) 是 Web API 的模型绑定器的工作。默认的模型绑定器不能为你反序列化接口,这是很明显的;他们应该反序列化到什么实现?
尽管您可以替换默认的模型绑定器,但您应该退后一步,仔细查看您要实现的目标。您正在抽象掉数据。将 DTO 隐藏在抽象背后通常意义不大,因为接口旨在抽象 行为 。
因此,与其使用接口,不如使用具体的 classes 更好。
it would save me the copy from a "sub-DTO" to a concrete one manually
与其这样做,更简单的方法是使用组合。您可以将较小的 DTO 组合成 DTO。这将使您不必完全复制。
by using the matching type registered in my Unity container.
这假设那些 DTO 应该在容器中注册,但同样,DI 容器不应该保存任何运行时数据。这应该被排除在外。或者如所述 here:
Don't inject runtime data into application components during construction; it causes ambiguity, complicates the composition root with an extra responsibility and makes it extraordinarily hard to verify the correctness of your DI configuration. My advice is to let runtime data flow through the method calls of constructed object graphs.
更新
组合 的想法很简单,您可以从较小的 classes 构建 classes;而不是使用继承或复制对象结构。在您的情况下,这看起来如何显然取决于您的需要,但我想您希望将 ITag
数据复制到另一个具有更多属性的 class:
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
}
相反,您可以从具体的 Tag
DTO:
SomeObject
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。