AutoMapper setting all properties to NULL on destination - asp.net-core-mvc

In an ASP.NET Core 1.1 Web API, I am trying to map an entity model to a DTO using AutoMapper.
The entity model:
namespace InspectionsData.Models
{
[Table("property")]
public class Property
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column("property_id")]
public int? Id { get; set; }
[Column("show_inventory")]
public bool ShowInventory { get; set; }
[Column("latitude")]
public double? Latitude { get; set; }
[Column("longitude")]
public double? Longitude { get; set; }
[Column("property_type_id")]
public int? PropertyTypeId { get; set; }
[ForeignKey("PropertyTypeId")]
[Display(Name = "Property Type")]
public PropertyType PropertyType { get; set; }
[Column("inspection_frequency_id")]
public int? InspectionFrequencyId { get; set; }
[ForeignKey("InspectionFrequencyId")]
[Display(Name = "Inspection Frequency")]
public InspectionFrequency InspectionFrequency { get; set; }
[Column("group_id")]
public int? GroupId { get; set; }
[ForeignKey("GroupId")]
[Display(Name = "Group")]
public Group Group { get; set; }
[Column("added_by_id")]
public int? AddedById { get; set; }
[ForeignKey("AddedById")]
[Display(Name = "Added By")]
public virtual User AddedBy { get; set; }
[Column("added_date")]
[Display(Name = "Added Date")]
public DateTime AddedDate { get; set; }
[Column("deleted_by_id")]
public int? DeletedById { get; set; }
[ForeignKey("DeletedById")]
[Display(Name = "Deleted By")]
public virtual User DeletedBy { get; set; }
[Column("deleted_date")]
[Display(Name = "Deleted Date")]
public DateTime? DeletedDate { get; set; }
}
and the DTO:
namespace InspectionsData.DTOs
{
public class PropertyDto
{
public int? Id { get; set; }
public bool ShowInventory { get; set; }
public double? Latitude { get; set; }
public double? Longitude { get; set; }
public PropertyType PropertyType { get; set; }
public InspectionFrequency InspectionFrequency { get; set; }
public DateTime NextInspectionDate { get; set; }
}
}
The mapping is done in a configuration file:
public class AutoMapperProfileConfiguration : Profile
{
public AutoMapperProfileConfiguration()
{
// Add as many of these lines as you need to map your objects
var map = CreateMap<Property, PropertyDto>();
map.ForAllMembers(opt => opt.Ignore());
map.ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id));
map.ForMember(dest => dest.ShowInventory, opt => opt.MapFrom(src => src.ShowInventory));
map.ForMember(dest => dest.Latitude, opt => opt.MapFrom(src => src.Latitude));
map.ForMember(dest => dest.Longitude, opt => opt.MapFrom(src => src.Longitude));
map.ForMember(dest => dest.PropertyType, opt => opt.MapFrom(src => src.PropertyType));
map.ForMember(dest => dest.InspectionFrequency, opt => opt.MapFrom(src => src.InspectionFrequency));
}
}
And the setting up of AutoMapper in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
var config = new AutoMapper.MapperConfiguration(cfg =>
{
cfg.AddProfile(new AutoMapperProfileConfiguration());
});
var mapper = config.CreateMapper();
services.AddSingleton(mapper);
}
In my controller action, I execute the mapping:
[HttpGet]
public async Task<IActionResult> GetProperty()
{
var properties = _context.Property
.Include(t => t.PropertyType)
.Include(f => f.InspectionFrequency)
.Where(a => a.DeletedDate == null && a.GroupId == 1);
var propertiesDto = _mapper.Map<IEnumerable<PropertyDto>>(properties);
return Ok(propertiesDto);
}
It doesn't give an error, but all the properties in all the objects in the propertiesDto list are default values (NULL for objects and nullable types, FALSE for booleans, 0 for integers, etc.) Any ideas where I'm going wrong?

It's because the following line
map.ForAllMembers(opt => opt.Ignore());
is letting AM ignore all member mappings, including the ones you have configured explicitly.
Simply use ForAllOtherMembers instead:
map.ForAllOtherMembers(opt => opt.Ignore());

Related

gridview error in mvc "data cannot be null parameter name source" How can i solve this?

In my asp.net mvc project I have a model and a filled list.
I want to view this list with gridview but I get this error Value cannot be null Parameter name: source how can I solve this?
this my model:
namespace MalEkBilgiTanim.Models
{
public class EkBilgiModel
{
public string Mal_ekbilgi_adi { get; set; }
public string Mal_ekbilgi_no { get; set; }
public string Mal_grup_no { get; set; }
public string Mal_grup_adi { get; set; }
public string Mal_altgrup_no { get; set; }
public string Mal_altgrup_adi { get; set; }
public string Mal_sinif_no { get; set; }
public string Mal_sinif_adi { get; set; }
public string Calisir { get; set; }
public string Mal_ekbilgi_detay_no { get; set; }
public string Mal_ekbilgi_detay_adi { get; set; }
}
}
public ActionResult SinifEslestirme(string ekbilgidetay)
{
var liste = Classes.SinifEslestirme.SinifEkBilgiDetayGetirmeListesi(ekbilgidetay);
return View(liste);
}
and grid code in cshtml
#Html.Grid(Model).Columns(col =>
{
col.Add(x => x.Mal_grup_no);
col.Add(x => x.Mal_grup_adi);
col.Add(x => x.Mal_altgrup_no);
col.Add(x => x.Mal_altgrup_adi);
col.Add(x => x.Mal_sinif_no);
col.Add(x => x.Mal_sinif_adi);
col.Add(x => x.Calisir);
})

LINQ - How to get recipes with all ingredients match

I'm having trouble finding recipes that contain all of selected ingredients.
Models:
Recipe
public int Id { get; set; }
[StringLength(64)]
public string Name { get; set; }
public string Description { get; set; }
[StringLength(256)]
public string CoverURL { get; set; }
public int PreparationTime { get; set; }
public DateTime AddedDate { get; set; }
public RecipeCategory Category { get; set; }
public List<RecipeImage> Images { get; set; }
public List<RecipeIngredient> Ingredients { get; set; }
public List<RecipeSpice> Spices { get; set; }
Ingredient
public int Id { get; set; }
[StringLength(64)]
public string Name { get; set; }
public List<RecipeIngredient> Recipes { get; set; }
[NotMapped]
public bool IsSelected { get; set; }
RecipeIngredient
public int Id { get; set; }
public double Quantity { get; set; }
public Unit Unit { get; set; }
public Recipe Recipe { get; set; }
public Ingredient Ingredient { get; set; }
And I tried
List<Ingredient> selectedIngredients = new List<Ingredient>();
selectedIngredients = _vm.Ingredients.Select(x => x).Where(x => x.IsSelected).ToList();
List<Recipe> recipes = new List<Recipe>();
recipes = _vm.Recipes.Where(x => selectedIngredients.Contains(x.Ingredients));
But it return a error:
cannot convert from
'System.Collections.Generic.List<RecipeApp.Model.RecipeIngredient>' to
'RecipeApp.Model.Ingredient'
I know it's a problem becouse "selectedIngredients" is a List of Ingredients, and a "x.Ingredients" is a list of RecipeIngredients. But I can't do it properly.
I did it, but maybe someone will do it better?
List<Recipe> recipes = new List<Recipe>();
List<Ingredient> selectedIngredients = new List<Ingredient>();
selectedIngredients = _vm.Ingredients.Select(x => x).Where(x => x.IsSelected).ToList();
var recipeIngredients = _vm.Recipes.Select(r => r.Ingredients).ToList();
foreach(var ingredientsList in recipeIngredients)
{
var recipe = ingredientsList.Select(x => x.Recipe)
.Where(u => ingredientsList
.Select(l => l.Ingredient.Id)
.Intersect(selectedIngredients
.Select(l2 => l2.Id))
.Contains(u.Id))
.FirstOrDefault();
if (recipe != null)
recipes.Add(recipe);

How to debug AutoMapper "Missing type map configuration or unsupported mapping" error

I have seen plenty of examples of this error occuring, for a wide variety of causes and I have gone through all the causes I can see, but still i get the error, so I am wondering if some one can give some information about what this error actually means, so i can try finding the cause. Here is some code:
Controller:
[HttpPost]
public ActionResult Edit(ProfileViewModel model)
{
if (ModelState.IsValid)
{
var person = new UserAttribute();
person = Mapper.Map<ProfileViewModel, UserAttribute>(model);
db.UserAttribute.Add(person);
db.SaveChanges();
}
View Model
public class ProfileViewModel
{
[Display(Name = "First Name")]
[StringLength(20)]
[Required]
public string FirstName { get; set; }
[Display(Name = "Last Name")]
[StringLength(30)]
[Required]
public string LastName { get; set; }
[Display(Name = "Gender")]
[Required]
public string Gender { get; set; }
[Display(Name = "Date of Birth")]
[DisplayFormat(DataFormatString = "{0:MM/dd/yyyy}", ApplyFormatInEditMode = true)]
public DateTime DOB { get; set; }
[Display(Name = "Hair Color")]
public string HairColor { get; set; }
[Display(Name = "Eye Color")]
public string EyeColor { get; set; }
[Display(Name = "Body Type")]
public string Weight { get; set; }
[Display(Name = "Height")]
public string HeightFeet { get; set; }
public string HeightInches { get; set; }
public int UserId { get; set; }
public IEnumerable<SelectListItem> WeightList { get; set; }
public IEnumerable<SelectListItem> EyeColorList { get; set; }
public IEnumerable<SelectListItem> HairColorList { get; set; }
public IEnumerable<SelectListItem> HeightFeetList { get; set; }
public IEnumerable<SelectListItem> HeightInchesList { get; set; }
public IEnumerable<SelectListItem> GenderList { get; set; }
}
UserAttribute model:
public int ProfileId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Gender { get; set; }
public System.DateTime DOB { get; set; }
public string HairColor { get; set; }
public string EyeColor { get; set; }
public string HeightFeet { get; set; }
public string Weight { get; set; }
public int UserId { get; set; }
public string HeightInches { get; set; }
Mapping config:
public class AutoMapperConfiguration
{
public static void Configure()
{
Mapper.Initialize(x => x.AddProfile<ViewToDomainMapProfile>());
Mapper.Initialize(x => x.AddProfile<DomainToViewMapProfile>());
}
}
public class ViewToDomainMapProfile : Profile
{
public override string ProfileName
{
get { return "ViewToDomainMapProfile"; }
}
protected override void Configure()
{
Mapper.CreateMap<ProfileViewModel, UserAttribute>()
.ForSourceMember(x => x.GenderList, y => y.Ignore())
.ForSourceMember(x => x.HairColorList, y => y.Ignore())
.ForSourceMember(x => x.EyeColorList, y => y.Ignore())
.ForSourceMember(x => x.WeightList, y => y.Ignore())
.ForSourceMember(x => x.HeightFeetList, y => y.Ignore())
.ForSourceMember(x => x.HeightInchesList, y => y.Ignore());
}
}
and the config is called in the global asax:
AutoMapperConfiguration.Configure();
Using Mapper.AssertConfigurationIsValid(); produces the following exception:
AutoMapper.AutoMapperConfigurationException :
Unmapped members were found. Review the types and members below.
Add a custom mapping expression, ignore, add a custom resolver, or modify the source/destination type
==============================================================================================
ProfileViewModel -> UserAttribute (Destination member list)
----------------------------------------------------------------------------------------------
ProfileId
So, you need to add mapping for ProfileId.
Overall, it's a good practice to use Mapper.AssertConfigurationIsValid(); either in your unit tests (you have them, right?), or after your mapper configuration. It'll display detailed information for such a misconfigurations.
For the viewmodel => userattribute
I noticed that ProfileId is a destination property, but not a source property.
public int ProfileId { get; set; }
Do you need to add code to ingore this destination member?
Other:
I might also suggest using or customizing the automapper to map properties that present that match by name exclusively.
Also, when possible, please avoid model names ending in the word Attritribute as by convention this is used almost exclusively for actual attributes. (my apologies for nitpicking)

Linq Issue dealing with null values in select

I have the following entities in my solution
public class UtilityAccount : IObjectWithState
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid UtilityAccountID { get; set; }
public string Account { get; set; }
[ForeignKey("Person")]
public Guid PersonID { get; set; }
public virtual Person Person { get; set; }
public string ForeignID { get; set; }
[NotMapped]
public ObjectState ObjectState { get; set; }
public virtual ICollection<Utility> Utilities { get; set; }
public UtilityAccount()
{
Utilities = new List<Utility>();
}
}
public class Utility : IObjectWithState
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid UtilityID { get; set; }
[ForeignKey("UtilityAccount")]
public Guid UtilityAccountID { get; set; }
public virtual UtilityAccount UtilityAccount { get; set; }
public Guid? ServiceAddressID { get; set; }
[ForeignKey("ServiceAddressID")]
public virtual Address ServiceAddress { get; set; }
[NotMapped]
public ObjectState ObjectState { get; set; }
public double CurrentBalance { get; set; }
public double? PendingPaymentTotal { get; set; }
public string ForeignID { get; set; }
[ForeignKey("UtilityType")]
public Guid UtilityTypeID { get; set; }
public virtual UtilityType UtilityType { get; set; }
public virtual ICollection<UtilityBill> UtilityBills { get; set; }
public virtual ICollection<IncomingUtilityPayment> IncomingPayments { get; set; }
public Utility()
{
UtilityBills = new List<UtilityBill>();
IncomingPayments = new List<IncomingUtilityPayment>();
}
}
public class IncomingUtilityPayment : IObjectWithState
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid IncomingPaymentID { get; set; }
public string ForeignID { get; set; }
[ForeignKey("Utility")]
public Guid UtilityID { get; set; }
public virtual Utility Utility { get; set; }
public DateTime PaymentDate { get; set; }
public IncomingPaymentStatus IncomingPaymentStatus { get; set; }
public double? UtilityAmount { get; set; }
public double? ConvenienceFee { get; set; }
public double? TotalAmount { get; set; }
public string AuthCode { get; set; }
public string AuthReference { get; set; }
public string TenderType { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int PaymentIdent { get; set; }
[NotMapped]
public ObjectState ObjectState { get; set; }
}
My problem is that I am trying to use Linq to retrieve information about a UtilityAccount and I am running into issues with the IncomingPayments for a Utility. Below is the select statement I am trying to use.
returnVal = repo.AllIncluding(o => o.Person, o => o.Utilities, o => o.Utilities.Select(p => p.UtilityType), o => o.Person.BillingAddress, o => o.Utilities.Select(p => p.ServiceAddress), o => o.Utilities.Select(p => p.IncomingPayments.Where(q => q.IncomingPaymentStatus == IncomingPaymentStatus.Pending || q.IncomingPaymentStatus == IncomingPaymentStatus.Processed )));
Everything ran fine until I added this clause to the statement.
o => o.Utilities.Select(p => p.IncomingPayments.Where(q => q.IncomingPaymentStatus == IncomingPaymentStatus.Pending || q.IncomingPaymentStatus == IncomingPaymentStatus.Processed ))
I think my issue ends up being something I am writing wrong in my Linq clause. The error I am getting is
The Include path expression must refer to a navigation property defined on the type. Use dotted paths for reference navigation properties and the Select operator for collection navigation properties.
Parameter name: path
I can use the following statement with no issues
o => o.Utilities.Select(p => p.IncomingPayments)
as soon as I add the where clause in I get the error
I'm not familiar with EntityFramework nor linq-to-entities, but if it's just like linq-to-object you can:
add a .Where(p => p.IncomingPayments != null) before chaining with your .Select() like this
o.Utilities.Where(p => p.IncomingPayments != null)
.Select(p => p.IncomingPayments.Where(q => q.IncomingPaymentStatus == IncomingPaymentStatus.Pending || q.IncomingPaymentStatus == IncomingPaymentStatus.Processed))
The result will be a nested IEnumerable, i.e. IEnumerable<IEnumerable<IncomingUtilityPayment>>
If you actually need a IEnumerable<IncomingUtilityPayment> then .SelectMany() come in to play.
o.Utilities.Where(p => p.IncomingPayments != null)
.SelectMany(p => p.IncomingPayments)
.Where(q => q.IncomingPaymentStatus == IncomingPaymentStatus.Pending || q.IncomingPaymentStatus == IncomingPaymentStatus.Processed)
Hope this help

How does Automapper work from a nested model to a flat model?

I have a nested view model similar to this:
public class EmployeeViewModel
{
//...
public string EmployeeFirstName { get; set; }
public string EmployeeLastName { get; set; }
public AddressViewModel{ get; set; }
}
The AddressViewModel looks like this:
public class AddressViewModel
{
public string Street {get; set;}
public string City {get; set;}
public string State {get; set;}
public string Zip {get; set;}
}
Then, there's an Employee domain object like so:
public class Employee
{
public string EmployeeFirstName { get; set; }
public string EmployeeLastName { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
}
I'm trying to map the EmployeeViewModel to the Employee domain object. This is what I came up with and it works, but I was wondering if there is an easier way to do this:
Mapper.CreateMap<EmployeeViewModel, Employee>().ForMember(destination => destination.Street, opt => opt.MapFrom(src => src.AddressViewModel.Street))
.ForMember(destination => destination.City, opt => opt.MapFrom(src => src.AddressViewModel.City))
.ForMember(destination => destination.State, opt => opt.MapFrom(src => src.AddressViewModel.State))
.ForMember(destination => destination.Zip, opt => opt.MapFrom(src => src.AddressViewModel.Zip));
As you can see, the property names in the Employee domain object and the AddressViewModel are the same. So, it seems like there should be an easier way to do this.
Thanks
You may checkout the flattening sample in the documentation. And here's an example:
public class AddressViewModel
{
public string Street { get; set; }
}
public class EmployeeViewModel
{
public string EmployeeFirstName { get; set; }
public AddressViewModel Address { get; set; }
}
public class Employee
{
public string EmployeeFirstName { get; set; }
public string AddressStreet { get; set; }
}
class Program
{
static void Main()
{
Mapper.CreateMap<EmployeeViewModel, Employee>();
var result = Mapper.Map<EmployeeViewModel, Employee>(new EmployeeViewModel
{
EmployeeFirstName = "first name",
Address = new AddressViewModel
{
Street = "some street"
}
});
Console.WriteLine(result.EmployeeFirstName);
Console.WriteLine(result.AddressStreet);
}
}
Notice how for the flattening to work out of the box the target property is called AddressStreet.

Resources