AutoMapper behaviour changes with a POST - c#

I have a complex object using inheritence that I map with automapper, it maps perfectly during a get request, but during a post request the exact same code doesn't map the inerited types correctly.
Let me explain. (See code below)
In the first case when I map the object during a simple get request, it maps perfectly fine and the Parent property of the class A below is of its specific type B or C.
But when the exact same mapping happens during a post, the Parent property of A is of type A!??
Now, the code is the same, the data model coming back from the DB is the same. (I use nhibernate - and the types are as I expect) the only difference is that it is a post request?!
Is there something I should know about AutoMapper in this case?
Class definitions (ViewModels follow the same structure):
public class A
{
public A Parent { get; set;}
}
public class B : A
{ }
public class C : A
{ }
And mapped like this:
CreateMap<A, AViewModel>()
.Include<B, BViewModel>()
.Include<C, CViewModel>();
CreateMap<B, BViewModel>();
CreateMap<C, CViewModel>();
Calling map:
var aModel = _aManager.Get("same parameter");
var aViewModel = Mapper.Map<AViewModel>(aModel);
Edit #1 - This depicts the logic in the post Action:
[Transaction] // Commits the nhibernate transaction on OnActionExecuted
[HttpPost]
public ActionResult UpdateA(OtherModelViewModel viewModel)
{
var a = _aManager.Get("same parameter");
var otherModel = Mapper.Map<OtherModel>(viewModel);
a.AddOtherModel(otherModel);
_otherModelRepository.New(otherModel);
// Eeek, writing this out I am seeing a problem here, I suspect this is where my problem would be, loading the model again from the db, after updating it in session without commiting it? I am going to change the logic and see if it fixes it.
var aModel = _aManager.Get("same parameter");
var aViewModel = Mapper.Map<AViewModel>(aModel);
// return result.
}

Sorry I was being silly and letting the complexity get the better of me.
I use a transaction attribute, to persist the information in OnActionExecuted.
So what I was doing was > loading the model > modifing it > then loading it again and trying to map it before it had even been persisted.
I know that nHibernate really doesn't like it when you try and do things like that, so I think the in memory object graph was in a state of flux (pending commit), which was affecting the mapping.
I have change my logic to rather do an ActionRedirect after the update, which has resolved the mapping issue.
Much happier all around.

Related

Handling partial updates (under-posting) with AutoMapper

I'm trying to find a method of handling partial updates; A ViewModel has 5 fields, but only one field is sent in the Request (i.e. only send fields that were modified instead of posing the entire view model), since null values were not explicitly sent for the other fields, I don't think that they should be set to null. Since AutoMapper does not have any kind of support for a Bind list, it's proving difficult to find an elegant solution...
public virtual IActionResult Edit(int id, ViewModel1 viewModel)
{
var model = GetModel(id);
mapper.Map(viewModel, model);
// any field that was not posted, but exists in the ViewModel1 is now null in the model
...
}
The only approach I can think of, is to use Request.Form.Keys and Reflection to build an ExpandoObject that only contains the posted properties, then set CreateMissingTypeMaps and ValidateInlineMaps to allow AutoMapper to map the dynamic types... It just feels like this is a dirty workaround to compensate for functionality that's missing in AutoMapper... Is there a standard way of handling this?

API Versioning - What do I do if model changes

I'm fairly new with APIs and versioning but from what I understand, the developer (which is me) should retain the code if there will be a contract breaking change for the API. Correct me if I'm wrong but I consider Model changes as contract breaking change.
So my question is - Do you make a new model (ex. ModelName.V2) just for the sake of versioning? Is there a better way to do this? This would mean even a slight change in property in my model would mean I would iterate it to another version.
p.s. let me know if I need to edit my question as I'm also fairly new in StackOverflow.
Sample
public class Product
{
public int ID {get;set;}
public string Name {get;set;}
}
and accompanying Controller
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/[controller]/[action]")]
public class Products : Controller
{
Product[] prod = new Product[]{
new Product(){ID = 1, Name = "T-Shirt"},
new Product(){ID = 2, Name = "Jeans"}
};
[HttpGet]
[ActionName("gotcha")]
public IActionResult GetProduct()
{
return Ok(prod);
}
}
and for V2 Controller
[ApiVersion("2.0")]
[Route("api/v{version:apiVersion}/[controller]/[action]")]
public class V2.Products : Controller
{
[HttpGet]
[ActionName("gotcha")]
public IActionResult GetProduct()
{
var trash = "Hello World!";
return OK(trash);
}
}
The above codes are what I understand as contract breaking and needed versioning and below is my problem with Model contract breakage:
public class Product
{
public int ID {get;set;}
public string Name {get;set;}
public decimal Price {get;set;}
}
So the sample above shows I added a new property. This I consider as contract breakage and do I iterate a new controller version and then retain the old Model as well? In the future this will be messy if I keep legacy versions.
First, you only need to reversion if there's a breaking change. Not everything is necessarily a breaking change, and typically adding a new property is not in fact a breaking change. Outdated clients should simply ignore it, and if not, that's more on the client than you, as they'd be doing something weird/wrong to cause anything to break that way. You need to be more concerned about changes or deletions, which from a client perspective are kind of the same thing; with a change, it's as if the old property was removed and new one was added. Even then, though, it only matters if the name or the type changes. Any under the hood processing doesn't matter, and technically, even if you change the property name, you can utilize something like the JsonProperty attribute to make the serialization return the old name, if you want.
Assuming you do have a breaking change, then yes, you should create a new version of your model class, and probably a new action/controller to go with it, both named with the new version number, i.e. Product2 and GetProduct2 and/or Product2Controller, etc. Yes, this can lead to code duplication, but there's two things you can do to minimize that:
Use inheritance if possible. For example, Product2 can inherit from Product and simply override whatever property needs to change. If you just add a new GetProduct2 action, you can factor out the original code for GetProduct into a private generic method GetProduct2<TProduct>, and then reimplement the original (and new) method to simply return that, i.e. return GetProduct<Product>(); and returnGetProduct();`. These are just examples. There's many different ways to handle this, but the point is that it doesn't necessarily require major code duplication to do versioning.
If you notice your codebase is starting to feel cluttered, you can begin deprecating API versions. Issue a notice to your clients that one or more of your oldest versions are now deprecated. Then, after a reasonable amount of time (depending on the complexity of the changes required to get up to date), remove that old code in a new release. This will of course break any outdated clients, but they were forewarned and given time to change, so if they don't that's on them. You'll notice all the big boys do this from time to time. I know I've been receiving a ton of emails from Facebook warning of pending API version removals. That's essentially what they're doing behind the scenes: cleaning their codebase.
When I do a API versioning. I add a new model with the Version number just to be able to track down the changes in data data structure, example:
public class ProductV2
{
//New attributes
}
I would just inherit attributes from the previous version, if the new model is similar to the old one plus new attributes:
public class ProductV2 : Product
{
//New attributes
}
Hopefully I understood your question this time.

How is an EF navigation property transferred between different classes?

This question is about the navigation property in an EF generated entity.
I am trying to improve my ability to more loosely couple code and have run into a problem understanding the navigation property that is so useful when using EF entities. I am working with the code first methodology. The code that follows is my best attempt to simulate the issue to keep the code presented here tightly focused on my question. While we can sometimes omit something critical when we do this, because the problem I am struggling with is present below, I think the simulation is OK.
Using the following architecture with the class definitions that follow, there is a navigation property layer1 in the layer2 entity. In this architecture, layer1/layer2 interface directly with SQL.
Public Class layer1
Public Property ID As Integer
Public Property name As String
Public Overridable Property layer2 As New List(Of layer2)
End Class
Public Class layer2
Public Property ID As Integer
Public Property name As String
Public Property layer1ID As Integer
Public Overridable Property layer1 As layer1 <<<< navigation property
End Class
Public Class efTestingContext
Inherits DbContext
Public Sub New()
MyBase.New("name=efTestingContext")
Configuration.ProxyCreationEnabled = False
End Sub
Public Property layer1 As DbSet(Of layer1)
End Class
This image shows what the layer1 entity looks like after SQL data retrieval.
It can be seen that the layer2.layer1 property is in fact a layer1 type, which makes sense.
Now the following classes dlayer1/dlayer2 are added to simulate a domain layer and simulate the different architecture also shown.
Public Class dlayer1
Public Property ID As Integer
Public Property name As String
Public Overridable Property layer2 As New List(Of dlayer2)
End Class
Public Class dlayer2
Public Property ID As Integer
Public Property name As String
Public Property layer1ID As Integer
Public Overridable Property layer1 As dlayer1 <<<< navigation property
End Class
End Class
This requires what I assume is often called a DTO (?) to convert layer1 to dlayer1. This simulated conversion object is shown below.
Function Index() As ActionResult
Dim theLayers = db.layer1.Include("layer2").ToList()
Dim thedLayers As New List(Of dlayer1)
'This code simulates the storage to domain translation
For Each layer In theLayers
Dim aLayer As New dlayer1
aLayer.ID = layer.ID
aLayer.name = layer.name
For Each subLayer In layer.layer2
Dim aSubLayer As New dlayer2
aSubLayer.ID = subLayer.ID
aSubLayer.name = subLayer.name
aSubLayer.layer1ID = subLayer.layer1ID
aSubLayer.layer1 = subLayer.layer1 <<<< something else needs to happen. What?
aLayer.layer2.Add(aSubLayer)
Next
thedLayers.Add(aLayer)
Next
Return View(theLayers)
End Function
The code above returns, of course, the following:
How do I code this so that the navigation property will be properly created in the dlayer1 entity?
I am aware of libraries such as automapper, but that seems to be focused on the domain-to-presentation interaction. I am not aware of libraries that handle the data-to-domain interaction. But even if there is a library that would do this, I’d still like to know how I would discretely code this. I'm also assuming this question is equally applicable to the equivalent C# code.
Navigation properties often don't survive an implicit DTO mapping, because they are prone to recursion. Converting layer1 to dlayer1 also converts the underlying layer2 to dlayer2, each of which then convert their layer1 to dlayer1, and you have created infinite recursion.
There are some ways around this, but the easiest way is to simply ignore the nav props, and later recreate them based on the PK/FK properties.
As an aside, this issue in unrelated to EF. It's related to the inability of implicitly handling circular references. Circular references generally lead to recursion, and .NET doesn't really protect you from unwanted recursion. It only raises an issue once the stack actually overflows at runtime.
Either empty the nav props, or make sure that your mapping does not try to map the nav props.
Convert each object to its equivalent DTO class. There should be no problem, as they are no longer linked to any other object (or their links are being ignored by the mapping process)
Recreate their navigational properties by matching dlayer1.ID to the dlayer2.layer1ID property.
That is the easiest way to get around it, and the performance cost of doing so is negligible.
Update for the questions in your comment.
Regarding point #3 - that's simply storing the parent ID value and not, as point #1 seems to say, recreating a nav prop.
Maybe I explained it badly. Let me show a quick example. Note that the code is massively oversimplified for the sake of brevity.
1 - Either empty the nav props, or make sure that your mapping does not try to map the nav props.
foreach( var child in myChildren ) { child.Parent = null; }
myParent.Children = null;
2 - Convert each object to its equivalent DTO class. There should be no problem, as they are no longer linked to any other object (or their links are being ignored by the mapping process)
foreach( var child in myChildren )
{
myDtoChildren.Add(child.ConvertToDTO());
}
myDtoParent = myParent.ConvertToDTO();
3 - Recreate their navigational properties by matching dlayer1.ID to the dlayer2.layer1ID property.
foreach( var dtoChild in myDtoChildren )
{
dtoChild.Parent = myDtoParent;
}
myDtoParent.Children = myDtoChildren.Where(c => c.ParentId == myDtoParent.Id);
In other words; you temporarily deleted the nav prop (the link to the other object), but you retained the foreign key (the ParentId property).
After mapping, you then use the ParentId (FK) property to fill in the Parent (nav prop) property.
But you also said there are some ways around this. Can you elaborate?
The example above is essentially a workaround. You edit the data to avoid the problem. But there are other ways to avoid the problem.
For XML serialization, you can add a [XmlIgnore] attribute to the nav prop so it does not get mapped (therefore preventing recursion).
WCF data contracts actually don't run into recursion problems here, they are able to handle circular references.
If you use explicit mapping rules (as opposed to implicit ones), you can simply omit the nav prop from the mapping.

Returning a class type based on int value

Right now, I have an enum like this:
public enum ReferenceType
{
Language = 1,
Period = 2,
Genre = 3
}
Language, Period, and Genre are all entity classes that map back to tables in my database. I also have model classes that map almost 1 to 1 with the entity classes, which I then display in a view.
I also have a service method like this:
List<Model> Get<Model, Entity>()
where Model : BaseModel
where Entity : BaseEntity;
I can call it like Service.Get<LanguageModel, Language>() and it will return every row from table Language from my database and automatically convert them into LanguageModels, which I'll then display in my view.
I want to create a wrapper method around the Get() method where you simply need to pass in an integer and it will call my Get() method filling in the entity and model class types automatically. The only problem is I'm having a hard time wrapping my head around the actual implementation behind this.
The pseudo-code would be along the lines of:
Call WrapperGet((int)ReferenceType.Genre)
Wrapper method resolves the entity type (Genre) and model type (GenreModel)
Call the Get() method with the resolved entity and model types, so it'd be Get<GenreModel, Genre>()
Return the results
How would I actually go implementing this in C#?
I can't think for a full generic way, I think simplest way is as below:
List<Model> WrapperGet(int type)
{
switch (type)
{
case 1: return Get<LanguageModel, Language>();
....
}
return null;
}
If you want strongly-typed results, you'll have to forgo the idea of passing an int or enum in, and switch to calling Get directly or making methods like this:
List<LanguageModel> GetLanguages() { return Get<LanguageModel, Language>(); }
If weakly typed works for you, then I'd go with Saeed's solution, but the types need a little changing to work. Model was a generic type in your example, so presumably he meant List<BaseModel> as the return type. But a List<LanguageModel> isn't a List<BaseModel>! You can resolve this by either making it return IEnumerable<BaseModel> or changing Get to return a List<BaseModel>, or change it afterwards with return new List<BaseModel>(Get<...

How can I collect model validation error messages?

I am building an ASP.NET MVC application, and I am trying to find a way where I can collect data from the user in a custom view model, try and set these values to one or more of my entities, then based on validation logic on those entities, collect error messages if any and get them back to the view. I am new to MVC and web design in general, it is therefore quite possible that I am making major conceptual errors, but I have tried to research as far as I could.
I realize that this is more work than having the view be strongly typed to the entity, where it would then be easy to have the validation errors display, as in this tutorial. However, I don't want to do this for security and because there are some places where I want to have values collected from a single view model to be set in multiple different entities.
I also realize that I could set validation rules on the view model itself, rather then on the entity, but this seems like poor architecture, as I would have to define them redundantly in different view models, and I would then be less sure whether I had prevented bad values from being persisted to the database.
My plan is therefore to have the validation rules be set on the entity itself and to have the view model as a dumb container. Then, in a different location in the application, I would apply the values from the view model to my entity(ies) in accordance my business logic. At this point, I would like my validation logic to be called. If the data is invalid, I plan on setting the error string in the custom attribute on the view model to the error from the validation logic on the entity. I am thinking it would go something like this:
public class CustomViewModel()
{
[SomeCustomValidation()] //Has a place for an error string and a boolean IsValid
String Property { get; set; }
}
public class BusinessLogic()
{
CustomViewModel TestForValidity(CustomViewModel viewModel)
{
MyEntity.Property = viewModel.Property;
// if(MyEntity.IsValid)? catch SomeSortOfException?
// collect error message, put it in the attribute on the view model, set IsValid to false
}
}
public class MyEntity()
{
[MoreCustomValidation()]
public String Property { get; set; }
}
I therefore have three questions:
When I try to pass data that does not satisfy my validation rules, will some sort of error or exception be thrown? Is there some indication I can use or collect when I try to pass in invalid data?
If there is some error or exception thrown, how can I collect the error message so I can assign it to my view model?
Most importantly, am I going about this all wrong? Can attributes not be modified at runtime, for example to include a new error message or to change IsValid to false? I know that I can use reflection to access the attributes. If I can modify them, how would I do so?
Thank you in advance for your help. I apologize if I misunderstand something big.
It seems you might be over-complicating things a bit. I think what you want to do is prevent the model binder from binding to properties that it should not be able to, as well as retaining the ability to check ModelState.IsValid when properties on your model do not meet the requirements of their validation attributes.
IMO the best way to accomplish this is through the use of what I call "strongly-typed binding filters". First define an interface with only the properties that you want the model binder to be allowed to bind on your model.
public interface INewBlogPost
{
string Title { get; set; }
string Body { get; set; }
}
Then ensure your entity inherits from the binding filter interface.
public class BlogPost : INewBlogPost
{
...
}
Next, modify your action method to create a new entity and manually invoke the model binder whilst typing it to the interface you just defined.
public ActionMethod NewBlogPost()
{
BlogPost newBlogPost = new BlogPost();
TryUpdateModel<INewBlogPost>(newBlogPost);
if (ModelState.IsValid)
{
...
}
}
Because you passed in a type when invoking the model binder via TryUpdateModel you explicitly told the model binder what type to bind to. This means that the model binder will only have access to the properties listed in the interface. Now when you pass a model into the method to be bound it will have to be of type INewBlogPost. Because your entity inherits from your binding filter interface, an instance of it will satisfy this requirement. The model binder will happily bind to the properties on the interface completely oblivious of any other properties your model object may have.
See this blog post for more information.
Aside
It is sometimes easy to run into action method ambiguity when you have two action methods with the same name; one for POST and one for GET like this:
[HttpGet]
public ActionResult NewBlogPost()
{
return View();
}
[HttpPost]
public ActionResult NewBlogPost()
{
BlogPost newBlogPost = new BlogPost();
TryUpdateModel<INewBlogPost>(newBlogPost);
if (ModelState.IsValid) { ... }
}
An easy way to fix that is to modify your POST action method to look like this:
[HttpPost]
public ActionResult NewBlogPost(FormCollection formCollection)
{
BlogPost newBlogPost = new BlogPost();
TryUpdateModel<INewBlogPost>(newBlogPost, formCollection);
if (ModelState.IsValid) { ... }
}
The MVC model binder knows how to bind the request form collection to an argument of type FormCollection so it will populate this just fine. Because your POST action now accepts an argument, it is no longer ambiguous with your GET method. You can pass this formCollection into TryUpdateModel to be used as the binding source if you wish, but you don't have to as it will default to the request form collection anyway. But since you are passing it in you may as well use it :)

Categories