I am having trouble using AutoMapper when doing CRUD operations in MVC. I have scoured the web looking for something related to this but cant find anything related to what I am trying to do. Maybe there is something but I am having trouble understanding the more advanced functions of AutoMapper.
Basically what im trying to do is use AutoMapper on the CREATE method in an MVC application. Now I can map just the basic information perfectly fine but when I need to include another Model I get completely lost in figuring it out. Here is my example below.
This is the model in my AppDomainClasses
public class City
{
public City()
{
this.Name = string.Empty;
this.Cities = new List<City>();
}
public City(string cityName)
{
this.Name = cityName;
this.Cities = new List<City>();
}
[Key]
public int Id { get; set; }
[Required]
public string Name { get; set; }
public List<City> Cities { get; set; }
}
This is my viewmodel in which im trying to automap
public class CityForList
{
[Key]
public int Id { get; set; }
[Required]
public string Name { get; set; }
}
public class CityFull : CityForList
{
public CityFull()
{
Cities = new List<CityForList>();
}
public List<CityForList> Cities { get; set; }
}
So basically when I created this Map
Mapper.CreateMap<CityFull, City>();
Then I pass a CityFull object into the mapper
Mapper.Map<CityFull, City>(cf);
So what I am doing is that I have the Create method passing in the CityFull object and a FormCollection of items which contains the ids of the cities. So all I need to have done is map the Id, the Name and the cities that were selected and have them mapped. Now I can map the Id and Name without problem but I do not know how to then add the selected cities ( comes from the form collection of a listbox) into AutoMapper to include them in the mapping.
I thank you ahead of time for any help you can offer!
The reason you don't get the selected cities is because the "Selected" attribute is not part of the "City" Class.
I would advise you to make another map with AutoMapper, that maps the controller's to a class(Lets call it CityController) that you will create , and have that CityController related to CityForList.
Hope this helps.
Related
I have asp.net web api application. I have the table Companies in the databse which have two fields: id and description. Recently I've updated the database and added a new column called CustomerID. After that when I am trying to call getCompanies
private readonly BackendContext _context;
public CompaniesController(BackendContext context)
{
_context = context;
}
// GET: api/Companies
[HttpGet]
public IEnumerable<Company> GetCompanies()
{
return _context.Companies;
}
I get
I think the controller tries to return the old companies model but can't achieve it because it doesnt exist now but I don't know how to fix this though the controller should return the updated model. Maybe I should somehow rebuild the app to make it use the updated version?
Additional code:
Context
public class BackendContext : Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityDbContext<IdentityUser>//DbContext
{
public BackendContext(DbContextOptions<BackendContext> options) : base(options) { }
public DbSet<Company> Companies { get; set; }
public DbSet<CompanyToProduct> CompanyToProducts { get; set; }
public DbSet<Product> Products { get; set; }
public DbSet<Customer> Customers { get; set; }
public DbSet<Vendor> Vendors { get; set; }
public DbSet<VendorToProduct> VendorToProducts { get; set; }
public DbSet<Invoice> Invoices { get; set; }
public DbSet<InvoiceItem> InvoiceItems { get; set; }
}
Model
public class Company
{
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int CustomerID { get; set; }
public virtual Customer Customer { get; set; }
public virtual ICollection<CompanyToProduct> CompaniesToProducts { get; set; }
public virtual ICollection<Invoice> Invoices { get; set; }
}
UPDATE
I've added some values to the table and I got the response of the first company:
[{"id":1,"name":"Google","description":"free food","customerID":6,"customer":null,"companiesToProducts":null,"invoices":null}
BUT I also got the fields which is not specified in the table: customer, companiesToProducts,invoices. Invoices and companiesToProducts are tables in my database and I don't know what is customer referred to. I should also mention that these tables are connected by foreign key.
UPDATE
Error:
Based on the comments on the question above, it sounds like the related tables are all trying to serialize and the overall process is failing likely due to circular references in the object graph. This comment above in particular hints at a solution:
I want to return only the data about companies but the controller also returns another fields like customer, companiesToProducts,invoices
While it's convenient to just return directly from the data context, this has the added side-effect of coupling the API with the database (and with the data access framework, which appears to be the issue here). In API design in general it's always a good idea to explicitly define the "shape" of that API. The fields to return, etc.
Project your result into an explicitly defined shape and return only what you want to return:
var result = _context.Companies
.Select(c => new
{
c.ID,
c.Name,
c.Description,
c.CustomerID
})
.ToList();
This defines specifically what you want to return, fetches only that information from the backing data, materializes it into an in-memory list, and finally then returns it through the API.
There is a potential downside to this, however. Because now we also need to change the return type of your API method. There are a couple options there, such as returning a generic response object or creating a view model which closely approximates your already existing model and starts to feel like duplication.
As with just about anything, it's a balance. Too far in any one direction and that direction starts to become a problem. Personally I often go the route of defining a view model to return:
public class CompanyViewModel
{
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int CustomerID { get; set; }
}
and returning that:
return _context.Companies
.Select(c => new CompanyViewModel
{
ID = c.ID,
Name = c.Name,
Description = c.Description,
CustomID = c.CustomerID
})
.ToList();
But the reason I normally do this is because I normally work in an environment where the web application is just one application attached to a common shared business domain, so the view models don't feel like code duplication. They're in a separate project, often take a different shape than the backing data objects, etc. But if your domain models are already in your web project and that's the only project you have, there's a strong desire to want to return those.
Another option when that's the case could be to universally set your JSON serialization to ignore circular references:
services.AddMvc()
.AddJsonOptions(
options => options.SerializerSettings.ReferenceLoopHandling
= Newtonsoft.Json.ReferenceLoopHandling.Ignore );
But do keep in mind that this still couples your API to your DB models. Maybe that's okay in this project, but if you ever add a column to your DB that you don't want users to see then it becomes an issue. As with anything, you have options.
I have a Employee class which is an Entity Framework code first class representing an Employee. I would like to create a view model based on the original Employee class and then populate that class from a linq query to my EF context.
public class EmployeeVM : Employee
{
public List<DepartmentSelect> Departments { get; set; }
}
EmployeeVM employee = context.Employees.Find(id);
I get the error "cannot implicitly convert type Employee to EmployeeVM."
Is there a simple way to do this rather then creating a new object and foreaching every parameter into the equivalent in the new class?
be nice if coding had an easy button, but it is what it is.. You could write the code one time and reuse it if you want. You could use AutoMapper and deal with some of the headaches that come with that. Your best bet would just be to write it yourself and maybe catch some errors if your context changes..
Without a mapper you could just add a static func to your viewmodel that will take an Employee object and create an EmployeeVM and use this in your context queries.
public class EmployeeVM
{
public EmployeeVM()
{
Departments = new List<DepartmentSelect>();
}
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime? Dob { get; set; }
public List<DepartmentSelect> Departments { get; set; }
public static Func<Employee, EmployeeVM> FromEntity = item => new EmployeeVM() {
Id = item.Id,
FirstName = item.FirstName,
LastName = item.LastName,
Dob = item.Dob
};
}
// get single EmployeeVM
var eVm = EmployeeVM.FromEntity(context.Employees.Find(id));
// get List<EmployeeVM
var eVmList = context.Employees.Select(EmployeeVM.FromEntity).ToList();
This isnt recommended but if Employee is a partial class you could always just extend it by adding another partial class in the same namespace.
public partial class Employee
{
//Add Extra Properties
public List<DepartmentSelect> Departments { get; set; }
}
you maybe want to use Automapper. http://automapper.org/
Tools like AutoMapper are designed to ease the burden of having a bunch of property-mapping code. You can also just serialize the first object and deserialize it into the second one.
I should probably mention, though, that this is probably a misuse of inheritance. Is there a reason you can't just put your Employee entity directly on your EmployeeVm as a property? Beyond that, Arash is right in pointing out that ViewModels should generally not be tightly coupled to your data model.
I have 2 simple models:
public class Country
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Region> Region { get; set; }
}
public partial class Region
{
public int ID { get; set; }
public string Name { get; set; }
public int CountryID { get; set; }
public virtual Country Country { get; set; }
}
Is it possible to have a single page to handle the creation of a country whereby the user inputs the country with multiple regions and then only posts to the server?
I've seen an implementation here where you create a custom ViewModel with numbered properties (Region1, Region2, Region3, etc) but it's limiting, any suggestions?
(I know AngularJS can be used to do this however I have no experience in this space as of yet.)
Thanks
Yes its very possible it just depends on how you plan to implement this.
My favourite style of implementing One to Many pages is initially creating the "one" (country) then redirecting to a page with a grid element where users can add the many (regions) to the one. It works well and its a very easy way for both the programmer to create and the user to understand.
As for creating a country with multiple regions in a single post, it could be done but you must think of how the implementation will work.
Sure, this is easy to do. You have defined your data model. Either you use that also as your View Model, or you can create a new model that is a complex object. The methods in your type:
public virtual Country Country { get; set; }
public virtual ICollection<Region> Region { get; set; }
These method being present normally indicates you're using Entity Framework and that these are "related entities" that you can traverse via this "navigation property" at run-time. You can create a Country and populate the Region collection on the fly when you try to use it.
Here is a good example of using a View Model:
What is ViewModel in MVC?
///Example of a Controller method creating a view model to display
[HttpGet]
public ActionResult Index()
{
var user = _userService.Get(User.Identity.Name);
var customerId = GlobalDataManager.GetCustomerId();
if (_error != null)
{
ModelState.AddModelError("", _error);
_error = null;
}
var model = new InboundListModel();
model.Initialize(customerId, user.CompanyId);
foreach (var campaign in model.Campaigns)
{
model.InitializeCallProggress(campaign.Id, _callInfoService.GetCallsCount(campaign.Id));
}
return View(model);
}
This View Model can be anything you want but it does need to be one type. So if you want 2 put 2 types in the ViewModel you just need a new container object:
public class ComplexViewModel
{
public Country Country { get; set; }
public ICollection<Region> Regions { get; set; }
}
Then you just need a way to populate the data like the example above where I call Initialize. This goes out to EF via a DAL project and retrieves the data for the model.
I am attempting to use several tables that all follow the same model, this is so that eventually if my application scales farther than needing a single database, the framework has already been laid.
Multiple object sets per type are not supported. The object sets 'Entity' and 'Reddit' can both contain instances of type 'Project.Models.Entity'.
I know this error comes from this section of my dbcontect model:
public DbSet<Entity> Entity {get; set;} //local users
public DbSet<Entity> Reddit { get; set; } //users who registered through reddit
public DbSet<Entity> Twitter { get; set; } //user who registered through twitter
I cant seem to find a way to make this work. I know existing topics cover why this is a problem, but the only solution I seem to find is "well make another database" which while seems simple, still doesn't really explain the issue or how to actually fix it through code.
(using entity framework 6)
This was extracted from the question.
I found the solution and It's so simple I'm rather ashamed that I even had to ask.
just make a new DBcontext for each table
which looks like
RedditDB.cs
public RedditDB() : base("name=Database")
{
}
public DbSet<Entity> Reddit { get; set; }
TwitterDB.cs
public RedditDB() : base("name=Database")
{
}
public DbSet<Entity> Twitter{ get; set; }
and in the controller just put
RedditDB _RDB = new RedditDB();
TwitterDB _TDB = new TwitterDB();
Use inheritance to create a new class with the same properties as the existing Entity.
public class Twitter
{
public int Id { get; set; }
public string Title { get; set; }
public ICollection<EntityC> Lines { get; set; }
}
public class Reddit : Twitter
{
// leave this empty
}
This is better than using separate DB contexts because it is easier to query a single context; you can join tables etc.
I am developing a new MVC4 project using Entity Framework for the first time. I really like being able to use code first models and update the database with migrations. I would like to be able to just have one place to change my model (the entity class) and for changes to this such as new properties to be reflected not only in the DB after a migration, but also in my view models.
So, what I would like to do, is to be able to generate a dynamic view model class using my entity class. The view model should copy all the properties and values from my entity class with some special logic defined in my entity class attributes.
For example, for a simple enity framework model like this:
public class UsersContext : DbContext
{
public UsersContext()
: base("DefaultConnection")
{
}
public DbSet<UserProfile> UserProfiles { get; set; }
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
}
I would like to generate a dynamic class that looke like this:
public class UserProfileView
{
[ScaffoldColumn(false)]
public int UserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
The pseudo code could look something like this but I don't know how I can achieve it:
function dynamic GeneraveViewModel(object entity)
{
Type objectType = entity.GetType();
dynamic viewModel = new System.Dynamic.ExpandoObject();
//loop through the entity properties
foreach (PropertyInfo propertyInfo in objectType.GetProperties())
{
//somehow assign the dynamic properties and values of the viewModel using the property info.
//DO some additional stuff based on the attributes (e.g. if the entity property was [Key] make it [ScaffoldColumn(false)] in the viewModel.
}
return viewModel;
}
Can anyone offer any advice?
I created Dynamic MVC to do this.
http://dynamicmvc.com
Install-package dynamicmvc
The main benefit to this approach is it allows you to focus on your business logic and have the UI logic built automatically for simple scenarios.