Projection using contextual values in AutoMapper - c#

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.

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.

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.

Related

parsing object with Property List<Claim> and tell the JsonConvert.DeserializeObject to use specific constructor for claims?

I'm using .NET Core with Newtonsoft.Json. I have a UserModel class that has a List<Claim> property
public class UserModel
{
public string GUID { get; set; }
public bool isActive { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public List<Claim> Claims { get; set; }
}
and I'm trying to parse the JSON request into this object class like so:
public IActionResult Testpost([FromBody]JObject body)
{
if (body == null) return BadRequest();
UserModel user = JsonConvert.DeserializeObject<UserModel>(body.ToString());
return Ok(user);
}
but deserializing JSON into an object like Claim class which I don't have access to throws an exception
Newtonsoft.Json.JsonSerializationException: 'Unable to find a constructor to use for type System.Security.Claims.Claim. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute. Path 'Claims
because it is not able to decide on a constructor
According to online sources I can create a custom converter class that can manage the UserModel object creation but I would like to avoid this.
Is it possible to deserialize a JSON object into my UserModel class and tell the JsonConvert.DeserializeObject to use a specific Claim constructor like Claim(String, String) for parsing the Claims?
EDIT:
as mentioned by #PaulG i have already check the answer for How to programmatically choose a constructor during deserialization?
however the accepted solution used Creates a new class that implements the JsonConverter class then manually parses the body of the JObject request. Moreover, the answer shows how to deal with Claims but not with complex objects where claims are nested as properties
reading another solution in the thread it shows how to create a class that directly implements the constructor needed like so:
class MyClaim : Claim {
public MyClaim(string type, string value):
base(type, value){}
}
but this will require me to keep note on the difference between Claim and MyClaim when writing my code. the JSON converter may not be able to assume which constructor to use but i should be able to tell it which one. or is it by design and i have to suck it up and write extra code just for this?
because the alternative for me would be something like this:
public IActionResult CreatePublicUser([FromBody]JObject body)
{
string Username = body["Username"].ToString();
string Password = body["Password"].ToString();
var Claims = body["Claims"].Children();
List<Claim> UserClaims = new List<Claim>();
foreach (var c in Claims)
{
UserClaims.Add(
new Claim(
c["Type"].ToString(),
c["Value"].ToString()
)
);
}
UserModel NewUser = (new UserBuilder())
.WithUserName(Username)
.WithPassword(Password)
.WithClaims(UserClaims)
.Build();
return Ok(NewUser)
}
I suggest that you take a different approach altogether which is a common practice as well. Define a model only for the interaction with client, e.g.
public class UserModelWeb
{
public List<ClaimWeb> Claims { get; set; }
}
These DTO objects will be used only for the data conversion from JSON and never in the business logic layer. Then you can map your web models to the business logic that you will be using later. This will allow you to not be dependent on internal classes when you read external data. I.e. if you suddenly add a new field, it will be populated from external (and probably untrusted) source. This clear separation of concerns will not allow this since you will have to explicitly define a field in a web model.
Example for your case: let's say you will have later an internal field in the database that only you can edit: "InternalNote". If you add that field to the model, anyone can post the data and edit the field while your intention was only to allow yourself to edit it.
Additionally this will solve your problem since you won't need to cast to other classes.
P.S. You can use your class directly in action methods:
MyAction([FromBody]UserModelWeb user)
It should be deserialized from json right away.

Ninject bind property on view models to filter

I'm fairly new to ninject so you'll have to forgive the potentially stupid question. I have been able to successfully bind my own custom filter to controller actions, however my question is can I do the same to a property on a ViewModel? My scenario is thus:
I have a view model with properties that look like this
public class CreateViewModel
{
...
[PopulateWith(typeof(Country))]
public IEnumerable<SelectListItem> Countries { get; set; }
...
}
the attribute is is a simple class
public class PopulateWithAttribute : Attribute
{
public Type Type { get; }
public PopulateWithAttribute(Type t)
{
Type = t;
}
}
and all I want to be able to do is have a block of code that will run when a new instance of CreateViewModel is created, that will take the database context to hydrate the enumeration. I'm not sure if even an action filter is the correct route to go down like how you make custom authorization / logging functionality. It doesn't seem that the BindFilter<> has anything that points to being able to bind on properties...
A point in any direction or to any resources would be great.

Registering a Serializer for Nancy which uses the NancyContext

I want to use custom serialization for my Nancy Modules, which uses the requested URL as part of its inputs.
I've seen that you need to register dependencies that use the context in an override of the ConfigureRequestContainer (as answered in C# IOC and Request State in NancyFX)
public class NancyBootstrapper : DefaultNancyBootstrapper
{
protected override void ConfigureRequestContainer(
TinyIoCContainer container,
NancyContext context)
{
container.Register<JsonSerializer>(new CustomJsonSerializer(context));
}
}
And I've seen that you can specify a different serialiser for Nancy using the IRegistrations interface (as answered in Configuring JsonNetSerializer and JsonNetBodyDeserializer using Nancy TinyIoC)
public class JsonRegistration : IRegistrations
{
public IEnumerable<TypeRegistration> TypeRegistrations
{
get
{
yield return new TypeRegistration(typeof(JsonSerializer), typeof(CustomJsonSerializer));
}
}
public IEnumerable<CollectionTypeRegistration> CollectionTypeRegistrations { get; protected set; }
public IEnumerable<InstanceRegistration> InstanceRegistrations { get; protected set; }
}
If I register my serializer the first way, I can't get Nancy to use it for JSON serialization.
If I register it the second way, I can't inject a copy of the current NancyContext and get access to the request - it tries to create an instance of the serialiser before the ConfigureRequestContainer method is even called.
What am I missing here?
Assuming that you are interested in the "serialization" part - i.e. returning your model as json, did you consider to extend your model to carry the needed information trough the pipeline, and then the serializer to strip it?
The following code makes the assumption that the custom serializer is smart enough to not serialize null properties.
Get["/somedata"] = _ => new MyModelEx
{
WhateverRealProperty = "some data",
RequestUri = this.Context.Request.Uri
};
public class MyModelEx : MyModel, IModelWithRequestUri
{
public string RequestUri {get; set;}
}
And in your serializer you can test for this "extra" data element, grab whatever you need from it, strip it off (set it to null??) and serialize the rest.
Or variations of the above, where you can have some "container" generic model like
public class ExtendedModel<T>
{
public T Model {get; set;}
public string RequestUri {get;set;} // or even pass the whole context if u need to
}
Some variations of the above would be the simplest implementation.
I would prefer a different approach, where I would create some form of a per-request "bag", in which to hold the current context, and inject that bag into the serializer (of course, this will require the serializer to be registered per-request as well).
Check this answer for ideas.

Create map with DBContext lookup, using automapper and ninject

So what I'm trying to do is use my DBContext to get some information from the DB for a mapping.
So I create a custom TypeConverter:
public class RoundVMtoTrampetRound : ITypeConverter<RoundVM, TrampetRound>
{
public RoundVMtoTrampetRound(DBTariff context)
{
this.context = context;
}
private DBTariff context { get; set; }
public TrampetRound Convert(ResolutionContext context)
{
RoundVM source = (RoundVM)context.SourceValue;
Mapper.CreateMap<RoundVM, TrampetRound>();
var dest = Mapper.Map<TrampetRound>(source);
dest.Difficulty = this.context.DifficultyTrampet.Find(source.Id);
return dest;
}
}
And in my controller i create a mapper using:
Mapper.CreateMap<RoundVM, TrampetRound>().ConvertUsing<RoundVMtoTrampetRound>();
But when i do the mapping i get error message saying that there is no default constructor. But i want ninject to do this for me with the code:
kernel.Bind<DBTariff>().ToSelf().InRequestScope();
An answer is here but i still get the same problem
Automapper + EF4 + ASP.NET MVC - getting 'context disposed' error (I know why, but how to fix it?)
I have tried the solution given in the link but get the same error.
So how to i get Automapper to use my Ninject to fix the problem?
Edit
I also found this where the same thing is done with autofac
http://thoai-nguyen.blogspot.se/2011/10/autofac-automapper-custom-converter-di.html
so my guess is that i need to tell automapper to use my ninject resolver but how to i do that and where?
All information you need is given in the blog article you already linked.
To break it down into the absolute minimum information, you need to do:
Mapper.Initialize(x =>
{
x.ConstructServicesUsing(type => kernel.Get(type));
});
before you ever access any other Mapper. property / method, so you need to call Mapper.Initialize(..) before you do Mapper.CreateMap.

Placement of a Function in an ASP.NET MVC Application

For educational purposes, I am building a clone social bookmarking service (similar to reddit). Under each 'bookmark' in the list, I'd like to show the base domain of the source URL (as opposed to the full URL.
I've already found a few threads on SO on how to do this, so I've gone ahead and made a class to abstract the functionality, but I'm unsure of where I should actually be calling the method.
At the moment I have my BookmarkList controller method passing a list of Bookmark objects to the view, where I'm iterating over the list. Since the Bookmark object doesn't have a property for storing the base URL (as I'm computing it on the fly) I can't put it inside the Bookmark objects before passing them to the view (and it seems wrong anyway). So should I be calling the GetDomainFromUrl method I've made from the view itself? For some reason that doesn't feel appropriate either.
I am unsure of how to fit in this functionality without breaking MVC convention.
Thanks.
I would add it to the Bookmark class. Properties can be computed, here is an example from a tutorial on asp.net mvc from msdn (MSDN source):
public string LastName { get; set; }
public string FirstMidName { get; set; }
public string FullName
{
get
{
return LastName + ", " + FirstMidName;
}
}
Instead of simply concatinating strings, you would call your GetDomainFromUrl method here.
IE,
public string BaseUrl
{
get
{
return GetDomainFromUrl(this.Url);
}
}
Notice that there is not a set method defined, since you could/would be setting the Url property.
Also, is your Url stored as a string?
If you are using the URI class, you could just use this.FullUrl.Host, assuming your article's url is defined in a property called FullUrl.(also assuming that this would not defeat the point of this assignment, since you said this was for school.)
For very basic scenarios, or where you have full control over your models, MVC (Model, View, Controller) is a good pattern.
In my experience, you typically need additional information that is important to your views but not to your actual model. For example, a list of dropdown items to be displayed for a model property, or in your case, putting the base URL for a site for your users to see.
In this case, I like to adapt MVC to be VM-V-C (ViewModel, View, Controller).
Essentially, you would want to create a Bookmark ViewModel and use that when rendering your views:
BookmarkViewModel.cs:
public class BookmarkViewModel
{
public string BaseUrl {get;set;}
// + all existing bookmark properties
}
You can either add your base URL function right into your view model and have the view model perform the function itself, or you can do it in your controller when creating the view model.
There are a few different options on how you could do this. I recommend storing the GetDomainFromUrl() method in the Bookmark Class. Also I recommend creating a property for the BaseUrl as well.
You can then either pass the full Url to the Bookmark object in the constructor, perform your function, and set it to the BaseUrl property.
class Bookmark
{
public string BaseUrl { get; }
public Bookmark(string url)
{
BaseUrl = GetDomainFromUrl(url);
}
private string GetDomainFromUrl(string url)
{
//your logic to generate BaseUrl
}
}
Another alternative is to do something like the following:
class Bookmark
{
private string baseUrl;
public string BaseUrl
{
get
{
return baseUrl;
}
set
{
baseUrl = GetDomainFromUrl(value));
}
}
private string GetDomainFromUrl(string url)
{
//your logic to generate BaseUrl
}
}
and then set the BaseUrl property somplace in your code to the value of the full url, and when you do so it will perform your function and store it in your property.

Categories