简体   繁体   中英

How to use interfaces in DTO with ASP.NET Web API and Unity?

I'm currently implementing dependency injection in an existing ASP.NET Web API project using the Unity container.

I already manage to inject my service classes into my API controller by configuring a dependency resolver.

But for a controller function, I have to use a Data Transfer Object (DTO).
In that object, I can't find how to use my model contracts.

Here is the Web API controller method :

[HttpPost]
[Route("api/application/save")]
public IHttpActionResult SaveApplication(ApplicationUpdateDTO applicationUpdate)
{
    // Inner code calling service methods expecting IApplication and
    // collections of ITag as parameters.
}

And here is the DTO definition :

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; }
}

As a result, the DTO itself is initialized, but not the properties that are all null .

I understand why the properties cannot be set : the interfaces cannot be instanciated and it doesn't have any clue of which classes to use for that. But my Unity container does, thanks to the registration.

  • Is it possible to use this "link" somehow to initialize the DTO properties?
  • Is there a better way do this?

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.

edit

I also tried the implementation of a ModelBinder by referring to this post .
But for the line about the ValueProviderResult , I got a null value.

For convenience, here is the response from Todd in the other question:

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;
    }
}

The small difference I got from the response of the question, is the usage of the System.Web.Http.ModelBinding.IModelBinder instead of the MVC one.

As requested, here are exerpts of my interfaces.
The IApplication interface :

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; }
}

The ITag interface :

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; }
}

An example of JSON :

{
    "marketApplication": {
      "Id": 20,
      "Name": "MyApplication",
      "Version": "2.0"
    },
    "deletedTagIds": [],
    "addedTags": [
      {
        "Id": 0,
        "Value": "NewTag"
      }
    ],
    "deletedProgramIds": [],
    "addedPrograms": [
      {
        "Id": 0,
        "Name": "x86"
      }
    ]
}

Dependency Injection is the practice of composing graphs of loosly coupled components. Components are the classes in your system that contain behaviour .

Dependency Injection is not meant to build up objects that merely contain data. Using Dependency Injection we build an graph of components. After that graph has been build (using constructor injection), we pass runtime data through this graph using method calls.

Every time you try to use Dependency Injection or an DI container (like Unity) for anything else, you will get into trouble. So although your question indicates that you want to do this with Unity, Unity should be left out of the equation (for this particular case).

As others already stated, the building of Data Transfer Objects (DTOs) that come in through the request is the job of Web API's Model Binder. The default Model Binders can't deserialize interfaces for you, which is quite obvious; to what implementation should they deserialize?

Although you can replace the default model binder, you should take a step back and look closely at what it is you are trying to achieve. You are abstracting away data. Hiding a DTO behind an abstraction makes usually little sense, since interfaces are meant to abstract behavior .

So instead of using interfaces, it is usually much better to use concrete classes instead.

it would save me the copy from a "sub-DTO" to a concrete one manually

Instead of doing that, a simpler approach would be to use composition . You can compose DTOs out of smaller DTOs. That would save you from having to do the copying completely.

by using the matching type registered in my Unity container.

This assumes that those DTOs should be registered in the container, but again, an DI container should not hold any runtime data. This should be kept out. Or as stated 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.

Update

The idea of composition is simple, you build classes from smaller classes; rather than using inheritance or duplicating object structures. How this would look like in your case obviously depends on your needs, but I imagine that you wish to copy that ITag data to another class that has more properties:

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
}

Instead, you can compose SomeObject from a concrete Tag DTO:

public class SomeObject
{
    // Members:
    public string Name { get; set; }
    public string Description { get; set; }

    public Tag Tag { get; set; }

    // more members
}

This way you don't have to copy Tag 's members; you only have to set the Tag property with a reference to the deserialized Tag DTO.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM