Help understanding the basics of AutoMapper - asp.net

So I have this two classes:
public class PhysicalTest
{
public int ID { get; set; }
public DateTime CreationDate { get; set; }
public int Weight { get; set; }
public int Height { get; set; }
public int Systolic { get; set; }
public int Diastolic { get; set; }
public int Pulse { get; set; }
}
public class PhysicalTestFormViewModel
{
public int ID { get; set; }
public DateTime CreationDate { get; set; }
[Required]
public int Weight { get; set; }
[Required]
public int Height { get; set; }
public int Systolic { get; set; }
public int Diastolic { get; set; }
public int Pulse { get; set; }
}
This is my AutoMapper configuration
Mapper.CreateMap<PhysicalTestFormViewModel, PhysicalTest>();
When I do this it works just fine
[HttpPost]
public ActionResult Create(int ehrId, PhysicalTestFormViewModel physicaltestvm)
{
EHR ehr = ehrRepository.Find(ehrId);
if (ehr.UserName != User.Identity.Name)
return View("Invalid Owner");
if (ModelState.IsValid)
{
PhysicalTest physicalTest= new PhysicalTest();
Mapper.Map(physicaltestvm, physicalTest);
physicalTest.PerformedBy = "Yo";
physicalTest.CreationDate = DateTime.Now;
ehr.PhysicalTests.Add(physicalTest);
ehrRepository.Save();
return RedirectToAction("Index");
}
else
{
return View(physicaltestvm);
}
}
But when I do this I get an error
Trying to map Summumnet.PhysicalTest
to
Summumnet.ViewModels.PhysicalTestFormViewModel.
Missing type map configuration or
unsupported mapping. Exception of type
'AutoMapper.AutoMapperMappingException'
was thrown.
public ActionResult Edit(int ehrId, int id)
{
EHR ehr = ehrRepository.Find(ehrId);
if (ehr.UserName != User.Identity.Name)
return View("Invalid Owner");
var physicalTest = ehr.PhysicalTests.Where(test => test.ID == id).Single();
PhysicalTestFormViewModel physicaltestvm = new PhysicalTestFormViewModel();
Mapper.Map(physicalTest, physicaltestvm);
return View(physicaltestvm);
}
In the scenario where the error is thrown I simply want to construct an ViewModel to display an Edit form.... what is the standard way of doing this?

You have only defined a mapping from PhysicalTestFormViewModel to PhysicalTest:
Mapper.CreateMap<PhysicalTestFormViewModel, PhysicalTest>();
You also need the opposite one:
Mapper.CreateMap<PhysicalTest, PhysicalTestFormViewModel>();
See this related SO question and answers.

you may do dynamic mapping where you dont have to create any maps
public ActionResult (PhysicalTestFormViewModel ptvm)
{
//other to wrote codes
EHR ehr = ehrRepository.Find(ehrId);
AutoMapper.Mapper.DynamicMap<PhysicalTestFormViewModel, PhysicalTest>(ptvm, ehr);
db.SaveChanges();
}

Related

Receiving AutoMapperMappingException

Currently I'm creating a new feature. It looks simple, but I am stuck at a problem with automapping dto to another one.
I have to create a wishlist [adding /deleting items of wishlist].
All works fine, except one thing: while adding an item to the wishlist, I'm get a message like this:
"type": "AutoMapperMappingException",
"message": "Error mapping types..."
However, I can see it got inserted into the database. Also, can delete it too. I understand the problem is linked to Automapper, but I could not figure out how to map correctly.
[HttpPost]
public async Task<IActionResult> Add(WishListItemCreationDto wishListItemDto)
{
var itemAdd = _mapper.Map<WishlistItemDto>(wishListItemDto);
var itemCreated = await _wishListItemService.AddAsync(itemAdd);
return CreatedAtAction(nameof(GetId), new { id = itemCreated.Id }, wishListItemDto);
}
//service
public async Task<WishlistItemDto> AddAsync(WishlistItemDto item)
{
var entity = _mapper.Map<WishlistItem>(item);
var entityDetails = await _productDetailsRepository.GetById(item.ProductDetailId);
entity.ProductDetails = entityDetails;
await _wishListItemRepository.AddAsync(entity);
return _mapper.Map<WishlistItemDto>(entity);
}
DTOs:
public class WishListItemCreationDto
{
[Required]
public string CustomerId { get; set; }
[Required]
public int ProductDetailId { get; set; }
[Min(1)]
[Required]
public int Quantity { get; set; }
}
public class WishlistItemDto
{
public int Id { get; set; }
public string CustomerId { get; set; }
public int ProductDetailId { get; set; }
public ProductDetailsDtoWithPrimaryImage ProductDetails { get; set; }
public int Quantity { get; set; }
}
public class WishlistItem
{
public int Id { get; set; }
public string CustomerId { get; set; }
public Customer Customer { get; set; }
public int ProductDetailsId { get; set; }
public ProductDetails ProductDetails { get; set; }
public int Quantity { get; set; }
}
ProductDetails DTO:
public class ProductDetails
{
public int Id { get; set; }
public int ProductId { get; set; }
public Product Product { get; set; }
public IList<ProductAttributeValue> ProductAttributes { get; set; } = new List<ProductAttributeValue>();
public int Quantity { get; set; }
public string Sku => $"BRD{Id}";
public byte[] RowVersion { get; set; } = new byte[0];
}
public class ProductDetailsDtoWithPrimaryImage
{
public int Id { get; set; }
public int Quantity { get; set; }
public int ProductId { get; set; }
public ProductDisplayEntity Product { get; set; }
public IEnumerable<ProductAttributeWithValueDto> ProductAttributes { get; set; }
public byte[] RowVersion { get; set; }
public string Sku => $"BRD{Id}";
public int? PrimaryImageId { get; set; }
}
AutoMapper:
public WishlistItemProfile()
{
CreateMap<WishlistItem, WishListItemCreationDto>().ReverseMap();
CreateMap<WishlistItemDto, WishListItemCreationDto>().ReverseMap();
CreateMap<WishlistItem, WishlistItemDto>()
.ForMember(wi => wi.ProductDetailId, opt => opt.MapFrom(f => f.ProductDetailsId))
.ForMember(wi => wi.ProductDetails, opt => opt.MapFrom(f => f.ProductDetails))
.ReverseMap();
}
everything is okay, but you missed inner mapping of your classes.
What the error says:
Mapping types:
ProductDetailsDtoWithPrimaryImage -> ProductDetails
SimpleWebApi.Controllers.ProductDetailsDtoWithPrimaryImage -> SimpleWebApi.Controllers.ProductDetails
Add additional mapping in your constructor WishlistItemProfile
CreateMap<ProductDetails, ProductDetailsDtoWithPrimaryImage>().ReverseMap();
And it starts works perfect

generate a list of products of a category

I am developing a shop application and I need to show products of each category. The problem is each product is created from a product template which is stored in a table and each template is related to a category. here is the product model:
namespace fardashahr.Models
{
[Table("Product")]
public class ProductModel
{
public ProductModel()
{
if (Specs == null)
{
Specs = new Dictionary<string, SpecItemsModel>();
}
}
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
public int ProductTemplateId { get; set; }
[Required]
public bool IsPublished { get; set; }
[Required]
public bool InStock { get; set; }
[Range(95, 110)]
public float SyncRate { get; set; }
public DateTime? ProductionDate { get; set; }
[Required]
public DateTime RegisterationDate { get; set; }
public string ImageName { get; set; }
public IEnumerable<string> GalleryImages { get; set; }
[NotMapped]
public Dictionary<string, SpecItemsModel> Specs { get; set; }
[ForeignKey("ProductTemplateId")]
public virtual ProductTemplateModel ProductTemplate { get; set; }
[ForeignKey("ManufacturerId")]
public virtual CodingItemModel Manufacturer { get; set; }
[ForeignKey("BrandId")]
public virtual CodingItemModel Brand { get; set; }
[ForeignKey("ModelId")]
public virtual CodingItemModel Model { get; set; }
[ForeignKey("SeriesId")]
public virtual CodingItemModel Series { get; set; }
}
}
and here is the the ProductTemplate:
namespace fardashahr.Models
{
[Table("ProductTemplate")]
public class ProductTemplateModel
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[StringLength(500)]
public string Name { get; set; }
[Required]
public int CategoryId { get; set; }
[StringLength(500)]
public string Description { get; set; }
[ForeignKey("CategoryId")]
public virtual CategoryModel Category{ get; set; }
}
}
and the controller is:
namespace fardashahr.Controllers
{
public class ProductsController : Controller
{
// GET: Products
public ActionResult Index()
{
return RedirectToAction("Index", "Home");
}
public ActionResult Category(string name)
{
//declare a list of products
List<ProductModel> productList;
using(MainModel db = new MainModel())
{
//get category id
CategoryModel category = db.Categories.Where(x => x.CategorytUrl == name).FirstOrDefault();
int catId = category.Id;
//initialize the list
productList = db.Products.Where(x => x. == catId).ToList();
}
}
}
}
finaly, what I want to know is how to initialize a list of products.
In your models, you added virtual keyword which indicates that navigation property will be automatically loaded without the need of LINQ lambda .include() expression.
Hence you can immediately access the navigation property and load the list like this;
productList = db.Products.Where(x => x.ProductTemplate.CategoryId == catId).ToList();
string categoryNameOfFirstProduct = productList.FirstOrDefault().ProductTemplate.Category.Name;
string categoryNameOfFirstProduct = productList.FirstOrDefault().ProductTemplate.Category.CategorytUrl;

Get Id of inserted data (ASP.NET)

I have code in controller that write data from View to table
Here is code
[HttpGet]
public ActionResult WelcomeScreen()
{
// Формируем список команд для передачи в представление
SelectList teams = new SelectList(db.Vacancy, "VacancyId", "VacancyName");
ViewBag.Teams = teams;
SelectList teams2 = new SelectList(db.Companies, "CompanyID", "CompanyName");
ViewBag.Teams2 = teams2;
return View();
}
[HttpPost]
public ActionResult WelcomeScreen(Interview interview)
{
db.Interview.Add(interview);
db.SaveChanges();
int id = interview.Interview_Id;
return RedirectToAction("Index", "Questions");
}
Here is model
[Key]
public int Interview_Id { get; set; }
public string Greeting { get; set; }
public string Detail { get; set; }
public Nullable<int> VacancyId { get; set; }
public virtual Vacancy Vacancy { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Interwier> Interwiers { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<InvitationMail> InvitationMails { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<MassLink> MassLinks { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<QuestionBlock> QuestionBlocks { get; set; }
}
I need to have Interview_Id
I try to make it like this, but it not works int id = interview.Interview_Id;
How I can write id to variable?
If I understood your question right. You might have something like this.
public DbSet<Interview> Interviews {get; set;}
Rename Interview_Id to simply Id or InterviewId (no underscore).
public class Interview
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int InterviewId { get; set;}
// other properties
}
DAL Method.
public int Add(Interview inteview)
{
using(DbContext entities = new DbContext()
{
entities.Interviews.Add(interview);
entities.SaveChanges();
var id = interview.InterviewId;
return id;
}
}
From your Controller.
[HttpPost]
public ActionResult WelcomeScreen(Interview interview)
{
Dal dalObj = new Dal();
var inteviewId = dal.Add(interview);
//use this id
}
Hope this helps.
You can try this.
[HttpPost]
public ActionResult WelcomeScreen(Interview interview)
{
db.Interview.Add(interview);
db.SaveChanges();
int id = db.Interview.Max(x=>x.Interview_Id);
return RedirectToAction("Index", "Questions");
}
check you dbml
make sure you have this attribute set correctly
AutoSync=AutoSync.OnInsert
the full attribute usually like this
[global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_ID", AutoSync=AutoSync.OnInsert, DbType="Int NOT NULL IDENTITY", IsPrimaryKey=true, IsDbGenerated=true, UpdateCheck=UpdateCheck.Never)]

asp.net mvc ViewModel result wont appear

i have this table
[Table("Quiz")]
public class Quiz
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int QuizId { get; set; }
public string Content { get; set; }
public string Submitby { get; set; }
public virtual ICollection<Score> Scores { get; set; }
}
and this
[Table("Score")]
public class Score
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ScoreId { get; set; }
public int QuizId { get; set; }
public string Answer { get; set; }
public virtual Quiz Quiz { get; set; }
}
so i have this viewmodel
public class ScoreQuizViewModel
{
public Score Score { get; set; }
public Quiz Quiz { get; set; }
}
and make this controller
public ActionResult Details(int id = 0)
{
Quiz quiz = db.Quizs.Find(id);
if (quiz == null)
{
return HttpNotFound();
}
return View(new ScoreQuizViewModel());
}
the problem is, theres nothing shown on my view
im using
#model SeedSimple.Models.ScoreQuizViewModel
and accessing with
#Html.DisplayFor(model => model.Quiz.Content)
i can see the result if im not using viewmodel.
how i can fix this?
It appears you're never filling in your ScoreQuizViewModel your code should look like this:
public ActionResult Details(int id = 0)
{
Quiz quiz = db.Quizs.Find(id);
if (quiz == null)
{
return HttpNotFound();
}
return View(new ScoreQuizViewModel { Quiz = quiz });
}

ASP.NET MVC 4 Code First Many to Many Adding to Collection

I am using ASP.NET MVC 4 code first pattern for database layer. I have a many to many relationship between UserProfile and Task. When I try to add a task to the the collection of tasks of a user, it's added but if I try to query it and see if it's there it's not showing up.
My model:
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public string FirstName { get; set; }
public string SirName { get; set; }
public string Position { get; set; }
public string Email { get; set; }
public ICollection<TaskModels> Tasks {get; set; }
public bool? isActive { get; set; }
public UserProfile()
{
Tasks = new HashSet<TaskModels>();
}
}
public class TaskModels
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
public ICollection<UserProfile> Employees { get; set; }
public int TimeNeeded { get; set; }
public int TimeWorked { get; set; }
public string Status { get; set; }
public bool isActive { get; set; }
public TaskModels()
{
Employees = new HashSet<UserProfile>();
}
}
public class WorkLogModels
{
public int Id { get; set; }
public UserProfile Author { get; set; }
public DateTime TimeBeganWorking { get; set; }
public int TimeWorkedOn { get; set; }
public TaskModels Task { get; set; }
public string Description { get; set; }
}
public class TimeTrackerDb : DbContext
{
public TimeTrackerDb() : base("DefaultConnection")
{
}
public DbSet<UserProfile> UserProfiles { get; set; }
public DbSet<TaskModels> Tasks { get; set; }
public DbSet<WorkLogModels> WorkLogs { get; set; }
}
I try to check if a UserProfile already exists in a Task's Employees list and it's always empty.
[HttpPost]
public ActionResult Create(WorkLogModels worklogmodels)
{
var tasks = db.Tasks.Where(x => x.Name == worklogmodels.Task.Name).SingleOrDefault();
if (tasks == null)
{
return View(worklogmodels);
}
if (ModelState.IsValid)
{
var user = db.UserProfiles.Where(x => x.UserId == WebSecurity.CurrentUserId).FirstOrDefault();
var task = db.Tasks.Where(x => x.Name == worklogmodels.Task.Name).FirstOrDefault();
WorkLogModels log = new WorkLogModels();
log.Description = worklogmodels.Description;
log.TimeBeganWorking = worklogmodels.TimeBeganWorking;
log.TimeWorkedOn = worklogmodels.TimeWorkedOn;
log.Author = user;
log.Task = task;
db.WorkLogs.Add(log);
if (!db.UserProfiles.Where(x => x.UserId == WebSecurity.CurrentUserId).First().Tasks.Any(x=> x.Name == worklogmodels.Task.Name))
{
db.UserProfiles.Where(x => x.UserId == WebSecurity.CurrentUserId).FirstOrDefault().Tasks.Add(task);
db.Tasks.Where(x => x.Name == worklogmodels.Task.Name).FirstOrDefault().Employees.Add(user);
}
db.SaveChanges();
return RedirectToAction("Index");
}
return View(worklogmodels);
}
I've been fighting with this for two days now.
Any help will be greatly appreciated
EDIT:
I am not sure if I made myself clear. In the Crate action for the WorkLog Controller I am trying to put the current user in the current task's collection and vice versa. It works correctly the first time, but then if I do it again it fails to skip the if statement and tries to add it once again and throws an exception : System.Data.SqlClient.SqlException. It's trying to add the same record to the intermediate table.

Resources