Ignore properties dynamically at time of mapping using AutoMapper - c#

I'm using AutoMapper in my .net-core application to create DTOs from my Nhibernate entities to send to the client as a JSON. However, there are some requests that I need to omit certain properties on my entity. For example, assume I have the following classes:
public class Person
{
public ICollection<Friend> Friends { get; set; }
}
public class PersonDTO
{
public ICollection<FriendDTO> Friends { get; set; }
}
Now, there are cases where I need to send to the client a Person with all of their friends, however sometimes I just need the person. I have a automapper configuration that defines a simple mapping between Person and PersonDTO with no omitted properties because that's the most common case. However, rather than create two separate DTOS (one with the Friends property mapped and the other with it ignored), it would be nice to somehow specify this at the time of mapping. I've tried the following, but it doesn't seem to work:
var personDtoWithoutFriends = _mapper.map<Person, PersonDTO>(person,
opts => opts.ConfigureMap()
.ForMember(dest => dest.Friends, act => act.Ignore()))
I know I can solve this problem with multiple DTOs, however this is an extremely common pattern in my application and I would end up creating a large number of DTOs. Ignoring certain properties in the few cases I need to seems like a much more elegant solution. Am I doing something wrong or is this just not possible using AutoMapper?

Related

Entity Framework classes vs. POCO

I have a general difference of opinion on an architectural design and even though stackoverflow should not be used to ask for opinions I would like to ask for pros and cons of both approaches that I will describe below:
Details:
- C# application
- SQL Server database
- Using Entity Framework
- And we need to decide what objects we are going to use to store our information and use all throughout the application
Scenario 1:
We will use the Entity Framework entities to pass all around through our application, for example the object should be used to store all information, we pass it around to the BL and eventually our WepApi will take this entity and return the value. No DTOs nor POCOs.
If the database schema changes, we update the entity and modify in all classes where it is used.
Scenario 2:
We create an intermediate class - call it a DTO or call it a POCO - to hold all information that is required by the application. There is an intermediate step of taking the information stored in the entity and populated into the POCO but we keep all EF code within the data access and not across all layers.
What are the pros and cons of each one?
I would use intermediate classes, i.e. POCO instead of EF entities.
The only advantage I see to directly use EF entities is that it's less code to write...
Advantages to use POCO instead:
You only expose the data your application actually needs
Basically, say you have some GetUsers business method. If you just want the list of users to populate a grid (i.e. you need their ID, name, first name for example), you could just write something like that:
public IEnumerable<SimpleUser> GetUsers()
{
return this.DbContext
.Users
.Select(z => new SimpleUser
{
ID = z.ID,
Name = z.Name,
FirstName = z.FirstName
})
.ToList();
}
It is crystal clear what your method actually returns.
Now imagine instead, it returned a full User entity with all the navigation properties and internal stuff you do not want to expose (such as the Password field)...
It really simplify the job of the person that consumes your services
It's even more obvious for Create like business methods. You certainly don't want to use a User entity as parameter, it would be awfully complicated for the consumers of your service to know what properties are actually required...
Imagine the following entity:
public class User
{
public long ID { get; set; }
public string Name { get; set; }
public string FirstName { get; set; }
public string Password { get; set; }
public bool IsDeleted { get; set; }
public bool IsActive { get; set; }
public virtual ICollection<Profile> Profiles { get; set; }
public virtual ICollection<UserEvent> Events { get; set; }
}
Which properties are required for you to consume the void Create(User entity); method?
ID: dunno, maybe it's generated maybe it's not
Name/FirstName: well those should be set
Password: is that a plain-text password, an hashed version? what is it?
IsDeleted/IsActive: should I activate the user myself? Is is done by the business method?
Profiles: hum... how do I affect a profile to a user?
Events: the hell is that??
It forces you to not use lazy loading
Yes, I hate this feature for multiple reasons. Some of them are:
extremely hard to use efficiently. I've seen too much times code that produces thousands of SQL request because the developers didn't know how to properly use lazy loading
extremely hard to manage exceptions. By allowing SQL requests to be executed at any time (i.e. when you lazy load), you delegate the role of managing database exceptions to the upper layer, i.e. the business layer or even the application. A bad habit.
Using POCO forces you to eager-load your entities, much better IMO.
About AutoMapper
AutoMapper is a tool that allows you to automagically convert Entities to POCOs and vice et versa. I do not like it either. See https://stackoverflow.com/a/32459232/870604
I have a counter-question: Why not both?
Consider any arbitrary MVC application. In the model and controller layer you'll generally want to use the EF objects. If you defined them using Code First, you've essentially defined how they are used in your application first and then designed your persistence layer to accurately save the changes you need in your application.
Now consider serving these objects to the View layer. The views may or may not reflect your objects, or an aggregation of your working objects. This often leads to POCOS/DTO's that captures whatever is needed in the view. Another scenario is when you want to publish objects in a web service. Many frameworks provide easy serialization on poco classes in which case you typically either need to 1) annotate your EF classes or 2) make DTO's.
Also be aware that any lazy loading you may have on your EF classes is lost when you use POCOS or if you close your context.

Issue with many-to-many relationship + TPH inhertitance in Entity Framework 6

I am running into an issue with EF6, though I'm fairly sure that this applies to previous versions that support this type of mapping. I fear I know the answer to the question at hand, but I hope that I am doing something wrong, or there is a better workaround than what I present here. All classes are gutted for clarity.
So I have
public abstract class SoftwareFirmware
{
public long Id { get; private set; }
public ICollection<DeviceType> DeviceTypes { get; private set; }
public SoftwareFirmware()
{
DeviceTypes=new HashSet<DeviceType>();
}
}
and
public class DeviceType
{
public long Id { get; set; }
public virtual ICollection<Firmware> AvailableFirmwareVerions { get; private set; }
public virtual ICollection<Software> AvailableSoftwareVerions { get; private set; }
public DeviceType()
{
AvailableFirmwareVerions = new HashSet<Firmware>();
AvailableSoftwareVerions = new HashSet<Software>();
}
}
which as you can see have a many to many relationship defined. I've defined two classes which derive from SoftwareFirmware, the aptly named
public class Firmware : SoftwareFirmware {}
and
public class Software : SoftwareFirmware {}
I'm using Table Per Hierarchy inheritance, so Software and Firmware are stored in the same table with a discriminator column. Finally, I've mapped the relationships in the derived DbContext's OnModelCreating method with
modelBuilder.Entity<DeviceType>().HasMany(d => d.AvailableFirmwareVerions).WithMany(firmware=>firmware.DeviceTypes);
modelBuilder.Entity<DeviceType>().HasMany(d => d.AvailableSoftwareVerions).WithMany(sofware=>sofware.DeviceTypes);
The problem at hand is that Entity Framework does not seem to support inheritance with this mapping, as I receive the following when EF tries to generate the database:
DeviceTypes: FromRole: NavigationProperty 'DeviceTypes' is not valid.
Type 'Software' of FromRole
'DeviceType_AvailableSoftwareVerions_Target' in AssociationType
'DeviceType_AvailableSoftwareVerions' must exactly match with the type
'SoftwareFirmware' on which this NavigationProperty is declared on.
From this I gather that a type that inherits from SoftwareFirmware is not good enough for the NavigationProperty, it must be a SoftwareFirmware type. If I tear the DeviceType collection out of the SoftwareFirmware base class and duplicate it in each of the derived classes, things work, but that's certainly less than ideal.
So finally, my question is- is there another way to configure this so that I can keep my navigation property in my base class? If not, is there a cleaner workaround than what I've described?
UPDATE: so it would seem that SQL Server Management Studio did me wrong, as I had diagrammed out the database previously without the overloaded version of WithMany that takes an expression and it did not include the junction tables. It seems like SSMS doesn't play nice with schema changes in terms adding new diagramming even when the database has been dropped and recreated- it must be restarted. Major pain, but I digress...
As a last ditch effort, I reverted to the parameterless version of WithMany for the mappings, deleted and recreated the database by restarting the application, restarted SSMS, and lo! The junction tables were created. All I needed to do is add an Ignore for the base SoftwareFirmware class's DeviceTypes property and everything generated cleanly. So my FluentAPI mapping code looks like this:
modelBuilder.Entity<DeviceType>().HasMany(d => d.AvailableFirmwareVerions).WithMany();
modelBuilder.Entity<DeviceType>().HasMany(d => d.AvailableSoftwareVerions).WithMany();
modelBuilder.Entity<SoftwareFirmware>().Ignore(s => s.DeviceTypes);
which generates this schema- pretty much exactly the schema I wanted (ignore the extra properties):
However, since the parameterless call to WithMany only hooks up a navigation property on one side, updates to Software.DeviceTypes and Firmware.DeviceTypes aren't tracked by EF so I'm back where I started.
The issue is that you have a single SoftwareFirmware.DeviceTypes property but you are then trying to use it as part of two separate relationships. SoftwareFirmware.DeviceTypes can't be the inverse of both DeviceType.AvailableFirmwareVerions and DeviceType.AvailableSoftwareVerions.
What you're trying to model is a bit strange because you're kind of treating them as distinct relationships, but also not. There are two options here...
Option 1: It's two separate relationships
Remove SoftwareFirmware.DeviceTypes and add a DeviceTypes property on Firmware and Software.
This is actually what you are doing when you put Ignore on the SoftwareFirmware.DeviceTypes property and use the empty overload of WithMany - which is why it works. You're telling EF that there are two relationships (one Software -> DeviceType and the other Firmware -> DeviceType) and that there is no navigation property that points back the other way. Since you ignored SoftwareFirmware.DeviceTypes it's just not part of your model.
Option 2: It's one relationship
Remove the two navigation properties on DeviceType and replace them with a single navigation to the SoftwareFirmware base class. You can always add some façade properties that filter the contents to Software and Firmware (as shown below)
public class DeviceType
{
public long Id { get; set; }
public virtual ICollection<SoftwareFirmware> AvailableVerions { get; private set; }
public virtual IEnumerable<Firmware> AvailableFirmwareVerions
{
get
{
return this.AvailableVerions.OfType<Firmware>();
}
}
public virtual IEnumerable<Software> AvailableSoftwareVerions
{
get
{
return this.AvailableVerions.OfType<Software>();
}
}
public DeviceType()
{
AvailableVerions = new HashSet<SoftwareFirmware>();
}
}
This problem sounds familiar. (checking my email ...yep it was over a year ago!) I had someone send me a sample where a fluent api relationship was failing. They did not have a many to many but I think it's the same problem. I spent a long time looking at it and asked Rowan Miller (on the team) and he said that the fluent api can't comprehend the property coming from the base type.
i.e. the fluent API can't see the DEVICETYPE property when it's looking at AvailableSoftwareVerions or at AvailableFirmwareVersions. (I can't tell you WHY this is. You'd think it could find it via reflection but maybe it just wasn't designed with this scenario in mind.)
This still didn't make sense to me so he explained further (and I'll update his explanation with your types which was a little confusing since you have extra levels of inheritance and things are named a bit inconsistently ...but I
Conceptually the classes don’t really make sense, because a DeviceType
can have many Software(s) or Firmware(s)… but the inverse navigation property is
defined on SoftwareFirmware. So what happens when something that isn’t
a Firmware or Software has a DeviceType? It’s inverse is configured as > DeviceType.AvailableSoftwareVersions
but that can’t work. Even taking EF out of the picture the correct way
to model that is to have the Project property be on Report.
That was with EF5. If my memory is correct and it's the same problem, then maybe it hasn't changed for EF6. Perhaps we should look to see if there's an issue in there for solving this problem. However, his further explanation suggests that it's not a bug but a protection.
(I'm going to ping him to verify that I'm inferring the previous problem to this one correctly).
In that email, Rowan also suggested using getter logic instead of a navigation properties as a workaround.

Where should I validate adding to my entity's collection property?

I'm just about to start a new pet project and I've been wondering about how I should go about validation when adding an entity to a parent's one-to-many collection. I'll use two example classes to summarize what I'm going on about a Student and a Teacher. Constraint here is that at any given time a Student can only be taught by one (and only one) Teacher who in-turn could be teaching one or more Students).
public class Student
{
public bool IsEnrolled { get; set; }
public virtual Teacher IsCurrentlyBeingTaughtBy { get; set; }
}
public class Teacher
{
public virtual ICollection<Student> IsCurrentlyTeaching { get; set; }
}
When students arrive at a class I need to assign them to the Teacher's IsCurrentlyTeaching collection, but I first need to make sure they're enrolled. My question is where best to validate this basic rule? The options going around my head currently are:
1. Use a repository pattern
As I'm going to be applying unit tests I'm leaning in favor of this method as I can wrap my data access logic up into a mockable object and there is a single responsibility here so I only have to validate this in my repository once. BUT - is validation the responsibility of the repository, or should I be only dealing with the CRUD of entities in a repository?
2. Validate this in the controller action
I should mention here that I propose this to be an MVC3 project, so keeping specifically to that should I be performing this validation in the controller's action before adding the Student to the repository (and subsequently the Teacher's list of students they're currently teaching). BUT - am I heading down a fat controller path that I really shouldn't be?
3. Perform this validation on the Teacher entity
Cutting out the middle-man (i.e. repository) should I be adding the Student via a method on the Teacher POCO such as AddStudent(Student student) and throwing a custom exception when trying to add a student who hasn't been enrolled?
There are probably more options available, but these are the three I'm trying to choose between at this present moment and I've got a little tunnel vision from thinking about this. Obviously all of the above can be suitably unit tested but thinking long-term (and accommodating growth) which path should I be heading down?
You may be able to create your own custom validator for this. That would let you piggyback on the validation the MVC is already providing. I've never tried this, but I would imagine something like this would work:
public class EnsureEnrollment : ValidationAttribute
{
public EnsureEnrollment () { }
public override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var studentList = value as IEnumerable<Student>;
if (studentList == null)
{
return ValidationResult.Success;
}
foreach(Student s in studentList )
{
if(!s.IsEnrolled)
{
//Insert whatever error message you want here.
return new ValidationResult("Student \"" + s.Name + "\" is not enrolled.");
}
}
return ValidationResult.Success;
}
}
Then on your property just add your annotation:
[EnsureEnrollment()]
public virtual ICollection<Student> IsCurrentlyTeaching { get; set; }
Personally, I like having my validation as part of static CRUDL methods on my entities. True, you have to pass the context in to every one of them, but it keeps the controllers a lot cleaner and makes all of that functionality readily available for any other projects that may use your entities in the future.
Previously I created a base class that all of my entities derived from which had a must override for Validate. The Validate method was called by almost all of the CRUDL methods and other working methods to ensure that the entity was proper before acting on it. Most of these validation rules were a bit more complex that could be easily expressed using the DataAnnotations attributes.
Or you can integrate specific validation points into a method with a more specific purpose. Take for instance:
public static bool AddToTeacher(SchoolContext db, Student student, Teacher teacher)
{
if (student.IsEnrolled)
{
teacher.IsCurrentlyTeaching(student);
return db.SaveChanges() > 0;
}
return false;
}
The AddToTeacher method only ensures that a specific requirement is met. If I wanted to ensure that the student was properly formed and was of eligible course track and what not, I would likely write a short method (or several all called by a "container" method) to validate those particular points.
In short, I do my best to keep every bit of entity specific code on the entity so that the controller is mostly ignorant of how the entities work.
As for on which entity to put it, it depends on how you think. Student.AddToTeacher is just as viable in my opinion as Teacher.AddStudent. I personally would use the former just because that is what most of my entities currently look like with "child" entities adding themselves to "parents" rather than the other way around.

Autoproject several sequences into the main ViewModel via Automapper

I'm trying to auto project from SQL server with Automapper some data into my view models.
The view model I have is:
public sealed class WorkstationViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public string OccupiedByName { get; set; }
}
And the code I'm trying to use is:
Mapper.CreateMap<Workstation, WorkstationViewModel>()
.ForMember(T => T.OccupiedByName, T => T.MapFrom(W =>
W.Sessions.AsQueryable().Select(E => E.StaffMember.Name).SingleOrDefault()));
Two properties Id and Name are auto-projected as they have equal names in Workstation class.
The exception I get on some codelines like this
var model = WorkstationsRepository.GetAll().Project()
.To<WorkstationViewModel>().SingleOrDefault();
is some weird object reference is null exception and on the top of the stack trace there are some automapper's CreateExpression<> methods which gives me a conclusion that the automapper cannot generate a good one expression to translate it to SQL code.
When I use simple maps, like .Name to .Category.Name or other one-item retrievals from the SQL table, it works perfectly. All I need is to get access to multiple items while projecting the sequence via Automapper.
The newer Project().To() API takes a totally different route than the "classic" Mapper.Map() API. I think that the latter would run in your case, but of course you won't have the benefit of the projection trickling thought to the SQL.
During Project().To() AutoMapper tries to get MemberInfos (reflection) from the involved types, which it uses to create lambda expressions. This means that the source properties in a mapping must be members of the source type. Evidently, W.Sessions.AsQueryable().Select(... is not a member of Workstation. So somewhere along the line AutoMapper bumps into a null memberinfo.
So, Project().To() is a bit restricted. In this particular case a remedy may be to map a Session with its parents WorkStation and StaffMember to the model. Reference navigation properties will map OK with Project().To().

MVC ViewModels and Entity Framework queries

I am new to both MVC and Entity Framework and I have a question about the right/preferred way to do this.
I have sort of been following the Nerd Dinner MVC application for how I am writing this application. I have a page that has data from a few different places. It shows details that come from a few different tables and also has a dropdown list from a lookup table.
I created a ViewModel class that contains all of this information:
class DetailsViewModel {
public List<Foo> DropdownListData { get; set; }
// comes from table 1
public string Property1 { get; set; }
public string Property2 { get; set; }
public Bar SomeBarObject { get; set; } // comes from table 2
}
In the Nerd Dinner code, their examples is a little too simplistic. The DinnerFormViewModel takes in a single entity: Dinner. Based on the Dinner it creates a SelectList for the countries based on the dinners location.
Because of the simplicity, their data access code is also pretty simple. He has a simple DinnerRepository with a method called GetDinner(). In his action methods he can do simple things like:
Dinner dinner = new Dinner();
// return the view model
return View(new DinnerFormViewModel(dinner));
OR
Dinner dinner = repository.GetDinner(id);
return View(new DinnerFormViewModel(dinner));
My query is a lot more complex than this, pulling from multiple tables...creating an anonymous type:
var query = from a in ctx.Table1
where a.Id == id
select new { a.Property1, a.Property2, a.Foo, a.Bar };
My question is as follows:
What should my repository class look like? Should the repository class return the ViewModel itself? That doesn't seem like the right way to do things, since the ViewModel sort of implies it is being used in a view. Since my query is returning an anonymous object, how do I return that from my repository so I can construct the ViewModel in my controller actions?
While most of the answers are good, I think they are missing an in-between lines part of your question.
First of all, there is no 100% right way to go about it, and I wouldn't get too hung up on the details of the exact pattern to use yet. As your application gets more and more developped you will start seeing what's working and what's not, and figure out how to best change it to work for you and your application. I just got done completely changing the pattern of my Asp.Net MVC backend, mostly because a lot of advice I found wasn't working for what I was trying to do.
That being said, look at your layers by what they are supposed to do. The repository layer is solely meant for adding/removing/and editing data from your data source. It doesn't know how that data is going to be used, and frankly it doesn't care. Therefore, repositories should just return your EF entities.
The part of your question that other seem to be missing is that you need an additional layer in between your controllers and the repositories, usually called the service layer or business layer. This layer contains various classes (however you want to organize them) that get called by controllers. Each of these classes will call the repository to retrieve the desired data, and then convert them into the view models that your controllers will end up using.
This service/business layer is where your business logic goes (and if you think about it, converting an entity into a view model is business logic, as it's defining how your application is actually going to use that data). This means that you don't have to call specific conversion methods or anything. The idea is you tell your service/business layer what you want to do, and it gives you business entities (view models) back, with your controllers having no knowledge of the actual database structure or how the data was retrieved.
The service layer should be the only layer that calls repository classes as well.
You are correct a repository should not return a view model. As changes to your view will cause you to change your data layer.
Your repository should be an aggregate root. If your property1, property2, Foo, Bar are related in some way I would extract a new class to handle this.
public class FooBarDetails
{
public string Property1 {get;set;}
public string Property2 {get;set;}
public Foo Foo {get;set;}
public Bar Bar {get;set;}
}
var details = _repo.GetDetails(detailId);
If Foo and Bar are not related at all it might be an option to introduce a service to compose your FooBarDetails.
FooBarDetails details = _service.GetFooBar(id);
where GetFooBar(int) would look something like this:
_fooRepo.Get(id);
_barRepo.Get(id);
return new FooBarDetails{Foo = foo, Bar = bar, Property1 = "something", Property2 = "something else"};
This all is conjecture since the design of the repository really depends on your domain. Using generic terms makes it hard to develop potential relationships between your objects.
Updated
From the comment if we are dealing with an aggregate root of an Order. An order would have the OrderItem and also the customer that placed the order.
public class Order
{
public List<OrderItem> Items{get; private set;}
public Customer OrderedBy {get; private set;}
//Other stuff
}
public class Customer
{
public List<Orders> Orders{get;set;}
}
Your repo should return a fully hydrated order object.
var order = _rep.Get(orderId);
Since your order has all the information needed I would pass the order directly to the view model.
public class OrderDetailsViewModel
{
public Order Order {get;set;}
public OrderDetailsViewModel(Order order)
{
Order = order;
}
}
Now having a viewmodel with only one item might seem overkill (and it most likely will be at first). If you need to display more items on your view it starts to help.
public class OrderDetailsViewModel
{
public Order Order {get;set;}
public List<Order> SimilarOrders {get;set;}
public OrderDetailsViewModel(Order order, List<Order> similarOrders)
{
Order = order;
SimilarOrders = similarOrders;
}
}
Repository should work only with models not anonymous types and it should only implement CRUD operations. If you need some filtering you can add a service layer for that.
For mapping between ViewModels and Models you can use any of mapping libraries, such as Automapper.
The current answers are very good. I would just point out that you are abusing anonymous types; they should only be used for intermediate transport steps, and never passed to other places in your code (e.g. view model constructors).
My approach would be to inject the view model with all the relevant model classes. E.g. an action method might look like:
var dinner = dinnerRepository.Get(dinnerId);
var bar = barRepository.Get(barId);
var viewModel = new DinnerAndBarFormViewModel(dinner, bar);
return View(viewModel);
I have the same doubt of the poster and I am still not convinced. I personally do not like very much the given advice of limiting the repository to just executing basic CRUD operations. IMHO, performances should always be kept into account when developing a real application, and substituting a SQL outer join with two different queries for master-detail relationships doesn't sound too good to me.
Also, this way the principle that only needed fields should be queried is completely lost: using this approach, we are forced to always retrieve all the fields of all the tables involved, which is simply crazy in non-toy applications!

Categories