7

I'm currently evaluation whether AutoMapper can be of benefit to our project. I'm working on a RESTful Web API using ASP.NET Web API, and one of the things I must return is a resource that contains links. Consider this simplified example, using the following domain object:

public class Customer
{
    public string Name { get; set; }
}

I need to map this into a resource object, sort of like a DTO but with added properties to facilitate REST. This is what my resource object may look like:

public class CustomerResource
{
    public string Name { get; set; }
    public Dictionary<string, string> Links { get; set; }
}

The Links property will need to contain links to related resources. Right now, I could construct them using the following approach:

public IEnumerable<CustomerResource> Get()
{
    Func<Customer, CustomerResource> map = customer => 
        new CustomerResource
        {
            Name = customer.Name,
            Links = new Dictionary<string, string>()
            {
                {"self", Url.Link("DefaultApi", new { controller = "Customers", name = customer.Name })}
            }
        }

    var customers = Repository.GetAll();
    return customers.Select(map);
}

...but this is pretty tedious and I have a lot of nested resources and such. The problem that I see is that I can't use AutoMapper because it doesn't let me provide certain things needed during projection that are scoped to the point where the mapping operation is performed. In this case, the Url property of the ApiController provides the UrlHelper instance that I need to create the links for me, but there may be other cases.

How would you solve this conundrum?

P.S. I typed up this code specifically for this question, and it compiled in your head but may fail in your favorite IDE.

5
  • At the moment I'm inclined to create the map at the call site but I don't know if that's a good idea. Commented Apr 3, 2013 at 7:47
  • When are the links defined? During runtime? Commented Apr 5, 2013 at 20:51
  • To be more precise, are links defined 1) at compile time, 2) at start up time or 3) at mapping/resolve time? Commented Apr 5, 2013 at 21:31
  • The Url property refers to an instance of UrlHelper instantiated per request, so at mapping time. Commented Apr 6, 2013 at 11:47
  • I may avoid AutoMapper alltogether. It's just not a good fit for what I'm doing. Commented Apr 8, 2013 at 11:14

2 Answers 2

2
+50

This is not a pretty solution, but after reading through the docs it appears that there isn't one... We're currently throwing in contextual stuff by mapping Tuple<TDomainType, TContextStuff> to TDataTransfer. So in your case you'd Mapper.CreateMap<Tuple<Customer, Controller>, CustomerResource>.

Not pretty, but it works.

Sign up to request clarification or add additional context in comments.

1 Comment

It's not pretty, but it's creative. I'm pondering trying this, but I'm already thinking about the mess it would create when configuring nested mappings.
2

I would look in to using a Custom Type Converter. The type converter could have contextual information injected via an IOC container. Or, since the converter is instantiated at configuration time, it could have a reference to a factory which would return contextual information each time the type converter is run.

Simple Example

You could define an interface for getting your current "context" (what that means depends on what you're doing and how you implement things so for this example I'll just the current HttpContext which gets you access to Session, Server, Items, etc...):

public interface IContextFactory
{
    HttpContext GetContext();
}

And the implementation is simply:

public class WebContextFactory : IContextFactory
{
    public HttpContext GetContext()
    {
        return HttpContext.Current;
    }
}

Your custom type converter could take an instance of IContextFactory from your IOC container and each time the mapping is run, you can call GetContext() to get the context for the current request.

Accessing the Url Property

The UrlHelper comes from the Request object attached to the current controller's context. Unfortunately, that is not available in the HttpContext. However, you could override the Initialize method on your ApiController and store the controllerContext in the HttpContext.Items collection:

protected override void Initialize(System.Web.Http.Controllers.HttpControllerContext controllerContext)
{
    HttpContext.Current.Items["controllerContext"] = controllerContext;
    base.Initialize(controllerContext);
}

You can then access that from the current HttpContext:

var helper = ((HttpControllerContext) HttpContext.Current.Items["controllerContext"]).Request.GetUrlHelper();

I'm not sure it's the best solution, but it can get you the UrlHelper instance inside your custom type mapper.

7 Comments

How would you get the context with IoC? Say you're executing Mapper.Map in a MVC controller. How would you inject the instance that you're executing in into the resolver/converter?
@carlpett, I updated with a same of grabbing the current Http Context via a factory.
But HttpContext is not what I need when resolving.
What is the source for the "links to related resources" you mention in your original question?
The UrlHelper instance that is instantiated for the request and available through the ApiController's Url property.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.