I have 2 models, Products and Categories, these are from the model on the repository:
IEnumerable<Product>
IEnumerable<Category>
I create a WebAPI and introduce AutoMapper eventually having code as below to return the products and categories in their own separate classes
public class ProductsController : ApiController
{
....
public IHttpActionResult Get()
{
try
{
IEnumerable<Product> products = GetProducts();
var mappedResult = _mapper.Map<IEnumerable<ProductModel>>(products);
return Ok(mappedResult);
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}
As i say same code in a new class for Category. Notice the new model for this class so its separate from the Domain model (ProductModel).
I now would like to create a page in my MVC application (not .Net Core) which needs to display a list of categories in a dropdown and list the products with a foreach loop. I create a new class as below
Public Class ProductsAndCategoriesModel
{
public ProductModel Products { get; set; }
public CategoryModel Categories { get; set; }
}
The idea here is to have a new method to load the same products and categories data but in its own class. Im following the same convention as above but how do i map two different data sources to this one class using AutoMapper?
var mappedResultProducts = _mapper.Map<IEnumerable<ProductsAndCategoriesModel>>(products);
var mappedResultCategories = _mapper.Map<IEnumerable<ProductsAndCategoriesModel>>(cats);
//return Ok(mappedResult);
I need to map products to Products found in ProductsAndCategoriesModel and Categories found in ProductsAndCategoriesModel. I tried to pass in categories but couldnt as it threw a compiler exception. How could i achieve this?
Edit 1
public ProductCategoryProfile()
{
CreateMap<Product, ProductsAndCategoriesModel>();
CreateMap<Category, ProductsAndCategoriesModel>();
}
No need to make things more complicated than they should be. Why don't you simply do it like this:
Model class:
public Class ProductsAndCategoriesModel
{
public IEnumerable<ProductModel> Products { get; set; }
public IEnumerable<CategoryModel> Categories { get; set; }
}
Mapping profile:
public ProductCategoryProfile()
{
CreateMap<Product, ProductModel>();
CreateMap<Category, CategoryModel>();
}
Controller:
public IHttpActionResult Get()
{
try
{
IEnumerable<Product> products = GetProducts();
IEnumerable<Category> categories = GetCategories();
var result = new ProductsAndCategoriesModel
{
Products = _mapper.Map<IEnumerable<ProductModel>>(products),
Categories = _mapper.Map<IEnumerable<CategoryModel>>(categories)
}
return Ok(result);
}
catch (Exception ex)
{
return InternalServerError(ex);
}
}
Related
public class UserController : ApiController
{
UserSampleEntities entities = new UserSampleEntities();
// GET api/<controller>
[Route("api/User")]
public IEnumerable<user> Get()
{
{
return entities.users;
}
}
}
This returns the json with all the entries in the database with all its properties. How do I filter such that I can obtain a json for only specific properties?
Create a new class that represents a user using only the properties you want to expose from "api/User":
public class UserDto
{
public int Foo { get; set; }
public string Bar { get; set; }
// add the properties you need here
}
Rewrite your API action to this:
[Route("api/User")]
public IEnumerable<UserDto> Get()
{
return entities.users
.Select(u => new UserDto
{
Foo = u.Foo,
Bar = u.Bar,
// map the properties you need here
})
.ToArray();
}
I am getting a self Referencing issue with EF and I'm trying to over come it but still allow the Service to be able to perform a GET passing in {[FromODataUri] int key} a key and return an IQuerable Obj to get the Expanded tables if necessary. Below is a slimmed down version of the tables. Any suggestions on how to handle the situation.
public class People
{
public int PeopleId {get;set;}
public string PeopleName {get;set;}
public int? ProductId{get;set;}
public virtual Product Product{get;set;}
}
The ProductId is a PK in Product but its not required. As per the convention it doesn't have to be Decorated with the PK DataAnnotation overide.
public class Product
{
public Product()
{
PeopleCollection = HashSet<People>();
}
public int ProductId {get;set;}
public string ProductName {get;set;}
public virtual ICollection<People> Peoples{get;set;}
}
In this case I recommend using DTO's or using anonymous objects, for example:
public IHttpActionResult Get() {
var response = db.YourTable.Select(x=>new{
x.PeopleId,
x.PeopleName,
x.ProductId,
Product= new {
x.ProductId,
x.ProductName
}
}).toList();
return Ok(response);
}
Thats how I would do it with anonymous objects, If you want to use DTO's you just need to map them, hope this is what you are looking for.
For just a specific id:
public IHttpActionResult Get(int id) {
var response = db.YourDb.Select(x=>new{
x.PeopleId,
x.PeopleName,
x.ProductId,
Product= new {
x.ProductId,
x.ProductName
}
})Where(x=>x.PeopleId == id).toList();
return Ok(response);
}
Note, this method is using query string parameters
I figured this out after some time. The self referencing issue will come up if you are Inheriting from APIController but if you switch to inherit from ODataController everything works.
So
public class MyController : ApiController
{
..... Bunch of code here
}
To
public class MyController : ODataController
{
..... Bunch of code here
}
I am getting the hang of Automapper in an ASP.NET MVC 5 application for the purpose of mapping a domain model to a ViewModel. There is a case that I still don't know how to resolve: when the ViewModel (destination) has a property not in the domain model (source).
The two additional properties in the ViewModel are IEnumerables that I need to populate in the Controller.
As I explain in the comments in the Controller block (shown below), the domain model is the source and will be fed into the View table. The additional two IEnumerables in the ViewModel will fill the DropDownLists in the HTML.BeginForm() block.
The examples I have seen using .CreateMap<>().ForMember() deal with calculations or transformations of properties in the source model, and not this case, where I am defining something in the controller based on the Action parameters.
My question is how to map the remaining IEnumerables, as defined in the controller?
Mapping Config in App_Start
public static class MappingConfig
{
public static void RegisterMaps()
{
AutoMapper.Mapper.Initialize(config =>
{
config.CreateMap<StudentRoster, StudentRosterViewModel>();
});
}
}
Model and ViewModel:
[Table("StudentRoster")]
public partial class StudentRoster
{
public int ID { get; set; }
[Required]
[StringLength(3)]
public string Campus { get; set; }
[Required]
[StringLength(4)]
public string FiscalYear { get; set; }
[Required]
[StringLength(50)]
public string StudentName { get; set; }
public int StudentID { get; set; }
}
// ViewModel
public partial class StudentRosterViewModel
{
// Automapper successfully mappped the first five fields
// to the parent class
public int ID { get; set; }
public string Campus { get; set; }
public string FiscalYear { get; set; }
public string StudentName { get; set; }
public int StudentID { get; set; }
// These two fields are not in the parent class
public IEnumerable<SelectListItem> CampusListSelect { get; set; }
public IEnumerable<SelectListItem> FiscalYearSelect { get; set; }
}
Index Action in Controller:
// GET: StudentRosterViewModels
public ActionResult Index(string campus = "MRA", string fy="FY16")
{
IEnumerable<StudentRoster> query = db.StudentRosters.Where(m=>m.Campus==campus).ToList();
// This successfully maps the domain model to the viewmodel
// This IEnumerable will display in the "Table"
IEnumerable<StudentRosterViewModel> mappedQuery =
AutoMapper.Mapper.Map<IEnumerable<StudentRoster>, IEnumerable<StudentRosterViewModel>>(query);
// The two remaining IEnumerables need to be mapped to 'mappedQuery'
// CampusListSelect and FiscalYearSelect
// These two IEnumerables will populate the dropdownlists in Html.BeginForm()
IEnumerable<SelectListItem> CampusList = new SelectList(new List<string> { "CRA", "DRA", "MRA", "PRA" }, campus);
IEnumerable<SelectListItem> FiscalYearList = new SelectList(new List<string> { "FY12", "FY13", "FY14", "FY15", "FY16" }, fy);
return View(mappedQuery.ToList());
}
You can try to use DynamicMap to populate your items without creating additional classes for mapping.
In case if you're using the old version of AutoMapper (4.1 or below) the you can try something the following:
// GET: StudentRosterViewModels
public ActionResult Index(string campus = "MRA", string fy="FY16")
{
IEnumerable<StudentRoster> query = db.StudentRosters.Where(m=>m.Campus==campus).ToList();
// This successfully maps the domain model to the viewmodel
// This IEnumerable will display in the "Table"
IEnumerable<StudentRosterViewModel> mappedQuery =
AutoMapper.Mapper.Map<IEnumerable<StudentRoster>, IEnumerable<StudentRosterViewModel>>(query);
// The two remaining IEnumerables need to be mapped to 'mappedQuery'
// CampusListSelect and FiscalYearSelect
// These two IEnumerables will populate the dropdownlists in Html.BeginForm()
IEnumerable<SelectListItem> CampusList = new SelectList(new List<string> { "CRA", "DRA", "MRA", "PRA" }, campus);
IEnumerable<SelectListItem> FiscalYearList = new SelectList(new List<string> { "FY12", "FY13", "FY14", "FY15", "FY16" }, fy);
var objForDynamicMapping = new
{
CampusListSelect = CampusList,
FiscalYearListSelect = FiscalYearList
};
foreach(var mappedItem in mappedQuery)
{
// will create the mapping configuration dynamically
AutoMapper.Mapper.DynamicMap(objForDynamicMapping, mappedItem);
}
return View(mappedQuery.ToList());
}
In case if you're using the AutoMapper 4.2 or high.
Then you just need to put this row:
var config = new MapperConfiguration(cfg => cfg.CreateMissingTypeMaps = true);
in place where you create the mapper configuration and then just use method Map like:
mapper.Map(objForDynamicMapping, mappedItem);
instead of DynamicMap.
Hope it will help.
im following this tuturial..
http://www.pluralsight.com/courses/mvc4
sadly there is no demonstration on how to add new department.
I was following that tutorial and did some of my changes too like names etc.
but now stucked as what i want to do is not avaiaible.
Coming to Question
I have two entities in Project Gem.Domain
Category.cs
Product.cs
with interface datasource.
namespace Gem.Domain
{
public interface IStoreDataSource
{
IQueryable<Product> Products { get; }
IQueryable<Category> Categories { get; }
void safe();
}
}
Now this in other project to which this Gem.domain is being refereced.
has the context class name as StoreDb.
namespace Gem.Infrastructure
{
public class StoreDb : DbContext, IStoreDataSource
{
public StoreDb() : base("GemStoreConnection")
{
}
public DbSet<Product> Products { get; set; }
public DbSet<Category> Categories { get; set; }
void IStoreDataSource.safe()
{
SaveChanges();
}
IQueryable<Product> IStoreDataSource.Products
{
get
{
return Products;
}
}
IQueryable<Category> IStoreDataSource.Categories
{
get
{
return Categories;
}
}
}
}
im using structuremap.mvc same as explained in tutorial for dependency resolution.
but when i try to add new category from controller, i cant add.
Neither it understands the .Add() nor .Attach method..
My Controller Method to update the category Name.
[HttpPost]
public ActionResult UpdateCategoryName_DT(Category category)
{
if (!ModelState.IsValid) return Content("Not Valid");
var updatedCategory = new Category()
{
CategoryID = category.CategoryID,
Name = category.Name
};
//For test using .Add() but not working neither do .Attach
_db.Categories.Add();
return Json(updatedCategory);
}
Constructor in CategoriesController
public class CategoriesController : Controller
{
private IStoreDataSource _db;
public CategoriesController(IStoreDataSource db)
{
_db = db;
}
one way to solve would be i guess to remove dependency resolution and do make direct object of DbStore but that wouldnt seem right..
What to do??
If you are using Entity Framework, regular routine usually looks like that:
using (var context = new MyDbContext())
{
var product = new Product("Surface", "Pro");
context.Products.Add(product);
context.SaveChanges();
}
Sample database context:
public class MyDbContext : DbContext
{
DbSet<Product> Products { get; set; }
}
If you want to use interface for your context, you are looking for something like this:
public interface IMyDbContext
{
DbSet<Product> Products { get; set; }
void SaveChanges();
}
Finally it turns out I manually had to define Add, Delete Attach methods in DataSource interface file.
So now it looks like:
public interface IStoreDataSource
{
IQueryable<Product> Products { get;}
IQueryable<Category> Categories { get;}
void safe();
T Attach<T>(T entity) where T : class;
T Add<T>(T entity) where T : class;
T Delete<T>(T entity) where T : class;
}
and added these methods in StoreDB (normally referred as db context):
public class StoreDb : DbContext, IStoreDataSource
{
public StoreDb() : base("GemStoreConnection")
{
}
public DbSet<Product> Products { get; set; }
public DbSet<Category> Categories { get; set; }
void IStoreDataSource.safe()
{
SaveChanges();
}
IQueryable<Product> IStoreDataSource.Products
{
get
{
return Products;
}
}
IQueryable<Category> IStoreDataSource.Categories
{
get
{
return Categories;
}
}
T IStoreDataSource.Add<T>(T entity)
{
return Set<T>().Add(entity);
}
T IStoreDataSource.Delete<T>(T entity)
{
return Set<T>().Remove(entity);
}
T IStoreDataSource.Attach<T>(T entity)
{
var entry = Entry(entity);
entry.State = EntityState.Modified;
return entity;
}
}
Finally using the Update method in controller:
public ActionResult UpdateCategoryName_DT(Category category)
{
if (!ModelState.IsValid) return Content("Not Valid");
var updatedCategory = new Category()
{
CategoryID = category.CategoryID,
Name = category.Name
};
_db.Attach(category);
_db.safe();
return Json(updatedCategory);
}
I'm not sure it's the best way. I am new to .NET and learning MVC.
I have extended a entity framework class (SQL server table) and added some extra properties to the child class but when I want to insert into the table which I already extended I get this exception:
Mapping and metadata information could not be found for EntityType 'Student.Models.Add.SubjectToStageModel'.
My controller:
[HttpPost]
public ActionResult SubjectToStage(SubjectToStageModel model)
{
try
{
if (ModelState.IsValid)
{
using (StudentEntities studentEntities = new StudentEntities())
{
int intCount =
studentEntities.SubjectToStageTbls.Count(
x => x.StageId == model.StageId && x.SubjectId == model.SubjectId);
if (intCount == 0)
{
studentEntities.SubjectToStageTbls.Add(model);
studentEntities.SaveChanges();
}
}
}
}
catch (Exception exception)
{
Debug.WriteLine(exception.ToString());
TempData["error"] = "An error occured";
}
return RedirectToAction("SubjectToStage");
}
My base class:
public partial class SubjectToStageTbl
{
public SubjectToStageTbl()
{
this.StudentMarkTbls = new HashSet<StudentMarkTbl>();
}
public int SubjectToStageId { get; set; }
public int SubjectId { get; set; }
public int StageId { get; set; }
public int Point { get; set; }
public virtual StageTbl StageTbl { get; set; }
public virtual ICollection<StudentMarkTbl> StudentMarkTbls { get; set; }
public virtual SubjectTbl SubjectTbl { get; set; }
}
My subclass:
public class SubjectToStageModel : SubjectToStageTbl
{
public IEnumerable<SelectListItem> StageListItem
{
get
{
List<SelectListItem> listsSelectListItems = new List<SelectListItem>();
try
{
using (StudentEntities studentEntities = new StudentEntities())
{
IQueryable<StageTbl> queryableStage = studentEntities.StageTbls;
foreach (var stage in queryableStage)
{
SelectListItem selectListItem = new SelectListItem();
selectListItem.Value = stage.StageId.ToString();
selectListItem.Text = stage.StageName;
listsSelectListItems.Add(selectListItem);
}
}
}
catch (Exception exception)
{
Debug.WriteLine(exception.ToString());
}
return listsSelectListItems;
}
}
public IEnumerable<SelectListItem> SubjectListItem
{
get
{
List<SelectListItem> listsSelectListItems = new List<SelectListItem>();
try
{
using (StudentEntities studentEntities = new StudentEntities())
{
IQueryable<SubjectTbl> queryableSubject = studentEntities.SubjectTbls;
foreach (var stage in queryableSubject)
{
SelectListItem selectListItem = new SelectListItem();
selectListItem.Value = stage.SubjectId.ToString();
selectListItem.Text = stage.SubjectName;
listsSelectListItems.Add(selectListItem);
}
}
}
catch (Exception exception)
{
Debug.WriteLine(exception.ToString());
}
return listsSelectListItems;
}
}
}
You didn't explicitly map the SubjectToStageModel class. If you want Entity Framework to work with derived classes you should add them to the model as well. But I don't think you intended to do that in the first place.
In fact, SubjectToStageModel is a view model. It may look convenient to derive a view model from an entity class, but I think generally it's not a good idea. View models should be tailored to the view (or use case) they're used in. A couple of reasons:
It's very likely that the entity class contains more properties than you need in the view. In later maintenance it's always a pain to keep checking what you do and don't need.
While view evolves, it may require a differently structured model than the entity.
The view may require different validations.
The view may be allowed to return a state that absolutely shouldn't be stored (you may require some post processing of entered data), so it's good to ensure it can't possibly be stored.
It creates a dependency between the data layer model and the view.
Maybe these consideration don't apply in your case. Still I'd prefer to have an independent view model. However, if you're lazy (we developers prefer the word pragmatic), you may succeed by doing:
studentEntities.SubjectToStageTbls.Add((SubjectToStageTbl)model);
(I never tried though).