automapper always convert null objects and it not throwing any exception here is my models and the methos i used...what i want when i initialize ProductDetailsModel inside ProductModel model i want to be initialized also inside Product model... both objects ProductDetailsModel and CategoryModel inside ProductModel they not initialized in Product
public class Product
{
[Key]
public int ID { get; set; }
public int? IDCategory { get; set; }
public string Name { get; set; }
public string Price { get; set; }
public int Quantity { get; set; }
public List<Image> Images { get; set; }
[ForeignKey("IDCategory")]
public Category Category { get; set; }
public virtual ProductDetails ProductDetails { get; set; }
}
public class ProductModel
{
[Key]
public int ID { get; set; }
[Required(ErrorMessage ="حقل الزامي")]
public int? IDCategory { get; set; }
public string Name { get; set; }
[Required(ErrorMessage = "حقل الزامي")]
[RegularExpression("([1-9][0-9]*)", ErrorMessage = "يجب ادخال ارقام فقط")]
public string Price { get; set; }
[Required(ErrorMessage = "حقل الزامي")]
[RegularExpression("([1-9][0-9]*)", ErrorMessage = "يجب ادخال ارقام فقط")]
public string Quantity { get; set; }
public List<ImageModel> Images { get; set; } = new List<ImageModel>();
public CategoryModel CategoryModel { get; set; } = new CategoryModel();
public ProductDetailsModel ProductModelDetails { get; set; } = new ProductDetailsModel();
}
public class ProfileMapper : Profile
{
public ProfileMapper()
{
CreateMap<ProductModel, Product>().ReverseMap();
CreateMap<ImageModel, Image>().ReverseMap();
CreateMap<CategoryModel, Category>().ReverseMap();
CreateMap<ProductDetailsModel, ProductDetails>().ReverseMap();
}
}
public async Task Add(ProductModel entity)
{
try
{
var theObject = mapper.Map<ProductModel, Product>(entity);
await Db.Product.AddAsync(theObject);
}
catch (Exception e)
{
await jSRuntime.ToastrError(e.Message);
}
}
private async Task AddProductsForm()
{
await UnitOfWork.ProductRepository.Add(new ProductModel {ProductModelDetails = new ProductDetailsModel { Details="gg"},Images = new List<ImageModel> { new ImageModel { ImageUrl = defaultImage } } });
await UnitOfWork.Complete();
ProductModels = (ICollection<ProductModel>)await UnitOfWork.ProductRepository.GetALL();
}
Your mapping does not include the name changes.
For example from Category => CategoryModel
CreateMap<Product, ProductModel>()
.ForMember(dest => dest.CategoryModel, opt => opt.MapFrom(src => src.Category))
.ReverseMap();
Related
As I said in the title, I'm trying to convert in the get method a model object to its DTO.
My method is to get users and is the next piece of code:
// GET: api/Users
[HttpGet]
public async Task<ActionResult<IEnumerable<UserDTO>>> GetUsers()
{
var users = _context.Users.ToList();
var userDtos = new List<UserDTO>();
foreach (var user in users)
{
userDtos.Add(new UserDTO
{
IdUser = user.UserProfessionId,
UserName = user.UserName,
UserCompany = user.UserCompany,
UserMail = user.UserMail,
UserProfession = user.UserProfession,
UserProfessionField = user.UserProfessionField
});
}
return userDtos;
}
These are my model and DTO for user:
namespace Sims.Models
{
public partial class User
{
public User()
{
DataUsages = new HashSet<DataUsage>();
}
public long IdUser { get; set; }
public int UserProfessionId { get; set; }
public int UserProfessionFieldId { get; set; }
public string? UserName { get; set; }
public string? UserMail { get; set; }
public string? UserCompany { get; set; }
public byte[]? UserPicture { get; set; }
public virtual Profession UserProfession { get; set; } = null!;
public virtual ProfessionField UserProfessionField { get; set; } = null!;
public virtual ICollection<DataUsage> DataUsages { get; set; }
}
}
and
namespace sims.DTO
{
public partial class UserDTO
{
public long IdUser { get; set; }
public string? UserName { get; set; }
public string? UserMail { get; set; }
public string? UserCompany { get; set; }
public virtual ProfessionDTO UserProfession { get; set; } = null!;
public virtual ProfessionFieldDTO UserProfessionField { get; set; } = null!;
}
}
Profession and ProfessionField are also models and have their own DTO. But in the get method, the two following lines contain the same error as it "cannot implicitly convert type '....Models.Profession' to '....DTO.ProfessionDTO'".
Do you have any idea ?
In case, here is an example of the Profession Model and DTO:
namespace Sims.Models
{
public partial class Profession
{
public Profession()
{
ProfessionFields = new HashSet<ProfessionField>();
Users = new HashSet<User>();
}
public int IdProfession { get; set; }
public string ProfessionName { get; set; } = null!;
public virtual ICollection<ProfessionField> ProfessionFields { get; set; }
public virtual ICollection<User> Users { get; set; }
}
}
and
namespace sims.DTO
{
public class ProfessionDTO
{
public int IdProfession { get; set; }
public string ProfessionName { get; set; } = null!;
}
}
Thanks for reading
The UserProfession property is of type ProfessionDTO:
public virtual ProfessionDTO UserProfession { get; set; } = null!;
But you're trying to populate it with an object of type Profession:
UserProfession = user.UserProfession,
Just as the error states, they are different types and can't be substituted for one another. Populate the property with an instance of ProfessionDTO instead:
UserProfession = new UserProfessionDTO
{
IdProfession = user.UserProfession.IdProfession,
ProfessionName = user.UserProfession.ProfessionName
},
If the user.UserProfession field is null then you'd need to check for that. For example:
UserProfession = user.UserProfession == null ?
null as UserProfessionDTO :
new UserProfessionDTO
{
IdProfession = user.UserProfession?.IdProfession,
ProfessionName = user.UserProfession?.ProfessionName
},
I have the following classes.
public class SomeModel
{
[Key]
public int Id { get; set; }
[Required]
public string UserId { get; set; }
public virtual User User { get; set; }
[Required]
public string Name { get; set; }
}
And:
public class SomeModelDetailsResponseModel : IMapFrom<SomeModel>, IHaveCustomMappings
{
public int Id { get; set; }
public string UserId { get; set; }
public string Name { get; set; }
public string UserName { get; set; }
public void CreateMappings(IConfiguration configuration)
{
configuration.CreateMap<SomeModel, SomeModelDetailsResponseModel>("name").AfterMap((b, r) =>
{
r.UserName = b.User.FirstName + b.User.LastName;
});
}
}
For some reason, when I project an IQueryable of SomeModel to an IQueryable of SomeModelDetailsResponseModel the UserName property turns out to be null.
Assuming these are you class definitions:
public class User
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class SomeModel
{
public int Id { get; set; }
public string UserId { get; set; }
public virtual User User { get; set; }
public string Name { get; set; }
}
public class SomeModelDetailsResponseModel
{
public int Id { get; set; }
public string UserId { get; set; }
public string Name { get; set; }
public string UserName { get; set; }
}
Solution 1
Do your mapping like this:
var config = new MapperConfiguration(
cfg =>
{
cfg.CreateMap<SomeModel, SomeModelDetailsResponseModel>().AfterMap((b, r) =>
{
r.UserName = b.User.FirstName + b.User.LastName;
});
});
var mapper = config.CreateMapper();
var response = mapper.Map<SomeModel, SomeModelDetailsResponseModel>(new SomeModel()
{
User = new User()
{
FirstName = "FN",
LastName = "LN"
}
});
Since you have your input as IQueryable<SomeModel> and you want to project it into IQueryable<SomeModelDetailsResponseModel>, then you can do this:
var result = q.Select(m => mapper.Map<SomeModel, SomeModelDetailsResponseModel>(m));
where q is your IQueryable<SomeModel> instance.
Solution 2
If you want to use ProjectTo<>, then initialize your mapper as the following:
Mapper.Initialize(cfg =>
{
cfg.CreateMap<SomeModel, SomeModelDetailsResponseModel>()
.ForMember(r => r.UserName, c => c.MapFrom(o => o.User.FirstName + o.User.LastName));
});
Then, do your projection as this:
var result = q.ProjectTo<SomeModelDetailsResponseModel>().ToArray();
Where q is your IQueryable<SomeModel>.
I have my models like this:
Goup.cs
GroupUser (pivot table)
ApplicationUser (User) -> 4. Profile
And now I want to show the data in Profile on a details page when the User belongs to the group. I'm doing this like this:
private IEnumerable<GroupUser> GetUsers(int groupId)
{
IEnumerable<GroupUser> model = null;
if(groupId == 0)
{
model = _kletsContext.GroupUser.OrderByDescending(o => o.GroupId).AsEnumerable();
}
else
{
model = _kletsContext.GroupUser.Where(g => g.GroupId == groupId).Include(p => p.User.Profile).OrderByDescending(o => o.GroupId).AsEnumerable();
}
return model;
}
This works, if I just want to display the UserId, ... (so the data in the Pivot table) with this code:
#model IEnumerable<App.Models.GroupUser>
#if(Model != null && Model.Count() > 0)
{
#foreach(var user in Model)
{
#user.UserId</h2>
}
}
But for some reason I can't display the data in the Included tables?
Normally you would do something like this: #user.User.Profile.XXXX but then I get the error: System.NullReferenceException: Object reference not set to an instance of an object
So this would mean the return is null, but there are users in the pivot table with a profile.
The models:
Group.cs:
namespace App.Models
{
public class Group : Item
{
public Group() : base()
{
}
[Key]
public Int16 Id { get; set; }
public string Name { get; set; }
public string Images { get; set; }
/* Foreign Keys */
public Nullable<Int16> RegionId { get; set; }
public virtual Region Region { get; set; }
public virtual ICollection<Lets> Lets { get; set; }
public virtual ICollection<GroupUser> Users { get; set; }
}
}
ApplicationUser:
namespace App.Models.Identity
{
public class ApplicationUser : IdentityUser
{
public string Description { get; set; }
public DateTime CreatedAt { get; set; }
public Nullable<DateTime> UpdatedAt { get; set; }
public Nullable<DateTime> DeletedAt { get; set; }
/* Virtual or Navigation Properties */
public virtual Profile Profile { get; set; }
public virtual ICollection<GroupUser> Groups { get; set; }
public virtual ICollection<Lets> Lets { get; set; }
public virtual ICollection<Category> Categories { get; set; }
public virtual ICollection<Region> Regions { get; set; }
public virtual ICollection<Status> Status { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
}
}
GroupUser:
namespace App.Models
{
public class GroupUser
{
public GroupUser()
{
}
public Nullable<Int16> GroupId { get; set; }
public string UserId { get; set; }
public virtual Group Group { get; set; }
public virtual ApplicationUser User { get; set; }
}
}
Profile.cs:
namespace App.Models
{
public class Profile : Item
{
public Profile() : base()
{
}
[Key]
public string UserId { get; set; }
public string FirstName { get; set; }
public string SurName { get; set; }
public string Email { get; set; }
public string Gender { get; set; }
public Int16 Age { get; set; }
public string City { get; set; }
public string Image { get; set; }
public Int16 Credits { get; set; }
public Int16 Postalcode { get; set; }
}
}
How can i display the nested data with razor?
model = _kletsContext.GroupUser.Where(g => g.GroupId == groupId)
.Include(gu => gu.User)
.ThenInclude(u => u.Profile)
.OrderByDescending(o => o.GroupId)
.AsEnumerable();
Don't get freaked out when intellisense doesn't work for the ThenInclude, just type it, it will compile.
try to include the user-reference
model = _kletsContext.GroupUser.Where(g => g.GroupId == groupId).Include(p => p.User).Include(p => p.User.Profile).OrderByDescending(o => o.GroupId).AsEnumerable();
I have following three classes
Om_MembershipCharges Class
public class Om_MembershipCharges
{
[Key]
public Int32 MembershipChargesID { get; set; }
public Decimal Amount { get; set; }
public Int16 PerMonth { get; set; }
public Int16? CountryID { get; set; }
public Int16 MemebershipTypeID { get; set; }
public virtual Om_MembershipType MemebershipType { get; set; }
public virtual Om_Country Country { get; set; }
}
Om_MembershipType Class
public class Om_MembershipType
{
[Key]
public Int16 MemebershipTypeID { get; set; }
public String MemebershipType { get; set; }
public Boolean IsDefaultMembership { get; set; }
public virtual ICollection<Om_MembershipCharges> MembershipCharges { get; set; }
}
Om_Country Class
public class Om_Country
{
[Key]
public Int16 CountryID { get; set; }
public String CountryName { get; set; }
public Boolean IsActive { get; set; }
public Int16? CurrencyID { get; set; }
public Int32? MembershipChargesID { get; set; }
public virtual Om_Currency Currency { get; set; }
public Om_MembershipCharges MembershipCharges { get; set; }
}
Below is my method that fetches all the Membership Charges using MembershipCharges Property in Om_MembershipType Class. I am using Include to get the collection.
public async Task<KeyValuePair<String, Om_MembershipType>> ListByMType(String mType)
{
try
{
using (var membershipChargesContext = new DatabaseTables())
{
using (var transaction = new TransactionScope(
TransactionScopeOption.Required,
new TransactionOptions { IsolationLevel = IsolationLevel.ReadUncommitted },
TransactionScopeAsyncFlowOption.Enabled))
{
membershipChargesContext.Configuration.ProxyCreationEnabled = false;
var data = await membershipChargesContext
.tblMembershipType
.Where(i => i.MemebershipType == membershipType)
.Include(i => i.MembershipCharges)
.FirstOrDefaultAsync();
transaction.Complete();
return new KeyValuePair<String, Om_MembershipType>("", data);
}
}
}
catch (Exception ex)
{
var exception = ex.Message;
if (ex.InnerException != null)
exception = ex.InnerException.ToString();
return new KeyValuePair<String, Om_MembershipType>(exception, null);
}
}
Is there any way to get the Country Instance in the Membership Charges collection ?
Is there any way to get the Country Instance in the Membership Charges collection?
Yes:
var data = await membershipChargesContext
.tblMembershipType
.Where(i => i.MemebershipType == membershipType)
.Include(i => i.MembershipCharges.Select(m => m.Country))
.FirstOrDefaultAsync();
You can expand an expression in the Include statement by subsequent Select statements to include nested navigation properties.
I hav been using automapper for sometime trying figure out how to handle different situation. I came across below situation and need some help figuring out the best approach. Below are my EF related classes;
public sealed class Invoice
{
public int InvoiceID { get; set; }
public DateTime InvoiceDate { get; set; }
public string CustomerName { get; set; }
public string CustomerAddress { get; set; }
public double? DiscountAmt { get; set; }
public Transaction InvoiceTransaction { get; set; }
public int TransactionID { get; set; }
}
public sealed class Transaction
{
public Transaction()
{
this.TransactionItems = new List<TransactionDetail>();
}
public int TransactionID { get; set; }
public DateTime TransactionDate { get; set; }
public DateTime TransactionLogDate { get; set; }
public TransactionType TransactionType { get; set; }
public IList<TransactionDetail> TransactionItems { get; set; }
public Invoice RefferingInvoice { get; set; }
public string Remarks { get; set; }
}
public sealed class TransactionDetail
{
public int TransactionID { get; set; }
public string ProductItemcode { get; set; }
public Product Product { get; set; }
public double Qty
{
get
{
return Math.Abs(this.StockChangeQty);
}
}
public double StockChangeQty { get; set; }
public double? UnitPrice { get; set; }
}
public sealed class Product
{
public Product()
{
this.StockTransactions = new List<TransactionDetail>();
}
public string ItemCode { get; set; }
public string ProductName { get; set; }
public string Manufacturer { get; set; }
public double UnitPrice { get; set; }
public IList<TransactionDetail> StockTransactions { get; set; }
public double Qty
{
get
{
if (this.StockTransactions.Count == 0)
{
return 0;
}
else
{
return this.StockTransactions.Sum(x => x.StockChangeQty);
}
}
}
public bool Discontinued { get; set; }
}
These are my view model classes;
public class InvoiceReportViewModel
{
public InvoiceReportViewModel()
{
LineItems = new List<InvoiceReportLineItemViewModel>();
}
public int InvoiceID { get; set; }
public DateTime InvoiceDate { get; set; }
public string CustomerName { get; set; }
public string CustomerAddress { get; set; }
public double? DiscountAmt { get; set; }
public string Remarks { get; set; }
public string StringInvoiceNo
{
get
{
return InvoiceID.ToString("########");
}
}
public IList<InvoiceReportLineItemViewModel> LineItems { get; set; }
}
public class InvoiceReportLineItemViewModel
{
public string ItemCode { get; set; }
public string ProductName { get; set; }
public string Manufacturer { get; set; }
public double? UnitPrice { get; set; }
public double Qty { get; set; }
public double LineTotal
{
get
{
if (UnitPrice.HasValue)
{
return UnitPrice.Value * Qty;
}
else
{
return 0;
}
}
}
}
My requirement is to convert the Invoice EF object to InvoiceReportViewModel object.
To do this I need to setup the profile. This is where I run into a problem; as it's not straight forward. The only way I see this done is by create my own Resolver by extending TypeConverter and manually doing the conversion by overriding ConvertCore method.
If there another way of getting this done (something with less work)???
Also I feel I could Map TransactionDetails EF class to InvoiceReportLineItemViewModel class by using the Mapper.CreateMap()..ForMember(...
But how can I use the mapper to convert it within the ConvertCore method?
Thanks in advance
In your case I do not see any requirements to use any custom converters.
You can convert Invoice EF object to InvoiceReportViewModel using simple Mapper.CreateMap like following:
public class InvoiceProfile: Profile
{
protected override void Configure()
{
Mapper.CreateMap<Invoice, InvoiceReportViewModel>()
.ForMember(c => c.CustomerName, op => op.MapFrom(v => v.CustomerName))
.ForMember(c => c.DiscountAmt, op => op.MapFrom(v => v.DiscountAmt))
.ForMember(c => c.InvoiceDate, op => op.MapFrom(v => v.InvoiceDate))
.ForMember(c => c.LineItems, op => op.MapFrom(v => v.InvoiceTransaction.TransactionItems));
Mapper.CreateMap<TransactionDetail, InvoiceReportLineItemViewModel>()
.ForMember(c => c.ProductName, op => op.MapFrom(v => v.Product.ProductName))
.ForMember(c => c.Qty, op => op.MapFrom(v => v.Qty))
//and so on;
}
}
Do not forget to register "InvoiceProfile"