This is my Domain Object Type
[Table("CredentialingCallDetail")]
[BsonIgnoreExtraElements]
public class CredentialingCallDetail : FullAuditedEntity<ObjectId>
{
public string RepresentativeName { get; set; }
public string PhoneNumber { get; set; }
public string PhoneExtension { get; set; }
public string CallResultStatus { get; set; }
public string IsFacilityCredentialed { get; set; }
public string Provider { get; set; }
public string PIN { get; set; }
public List<LicensedProfessionalCredentialed> LicensedProfessionalCredentials { get; set; }
}
And this is my Data Transfer Objet
[AutoMapTo(typeof(CredentialingCallDetail))]
public class CreateCredentialingCallDetailInput
{
[BsonIgnore]
public string Id { get; set; }
[Required]
public string RepresentativeName { get; set; }
[Required]
public string PhoneNumber { get; set; }
public string PhoneExtension { get; set; }
[Required]
public string CallResultStatus { get; set; }
public string IsFacilityCredentialed { get; set; }
public string Provider { get; set; }
public string PIN { get; set; }
public string Status { get; set; }
public List<LicensedProfessionalCredentialedDto> LicensedProfessionalCredentials { get; set; }
public CreateCredentialingCallDetailInput()
{
LicensedProfessionalCredentials = new List<LicensedProfessionalCredentialedDto>();
}
}
When I map CreateCredentialingCallDetailInput to CredentialingCallDetail i.e
CredentialingCallDetail newCredentialingCallDetail = input.CredentialingCallDetail.MapTo<CredentialingCallDetail>();
I get the exception
There is a mismatch between the type of Id , Automapper is not mapping string to ObjectId,Is There any way i can change the setting on fly , i.e change setting to ignore Id Mapping ?
Answer can be found in this question (yes question!).
You can do this in two ways.Check the question for details.
Quick answer for you.
You can ignore extra elements when defining mapping.
CreateMap<CreateCredentialingCallDetailInput, CredentialingCallDetail >()
.ForSourceMember(src => src.Id, opt => opt.Ignore())
Just add 2nd line to your existing mapping.
This looks ambiguous,
CredentialingCallDetail newCredentialingCallDetail =
input.CredentialingCallDetail.MapTo<CredentialingCallDetail>();
Should not it be something like this
CredentialingCallDetail newCredentialingCallDetail =
CreateCredentialingCallDetailInput.MapTo<CredentialingCallDetail>();
Related
I'm not yet dependent to either Mapster or AutoMapper. For now I'm using handwritten mappings because I couldn't find a mapper who could do this with smaller code.
The problem is how do we map flatten structures to complex objects? I think a lot of people could benefit from a good mapping example for such a complex object. I've got even a mapping condition based on CopyOfficeAddressAsInvoiceAddress whether or not the office address needs to be copied as invoice address. I've looked all over the place but couldn't get it to work.
Maybe I should also use a different naming to make it more clear for the mapping algorithm?!
The biggest question could such a map being resolved by a mapper or is this to complex? Al the demo's I've seen were using dto and model objects that are quite similar to each other. I didn't get the point of mapping an object to another object that 99% similar to each other.
I have a Command (I'm using Mediatr) that looks like as follows:
public class Command : IRequest<IActionResult>
{
public string AccountName { get; set; }
public string ContactFirstName { get; set; }
public string ContactLastName { get; set; }
public string ContactEMail { get; set; }
public string ContactPhoneNumber { get; set; }
public string BankAccount { get; set; }
public string Bank { get; set; }
public string OfficeName { get; set; }
public string OfficeAddressStreet { get; set; }
public int OfficeAddressStreetNumber { get; set; }
public string? OfficeAddressStreetNumberAddition { get; set; }
public string OfficeAddressPostalcode { get; set; }
public string OfficeAddressCity { get; set; }
public string OfficeAddressCountry { get; set; }
public string? OfficeInvoiceAddressStreet { get; set; } = null;
public int? OfficeInvoiceAddressStreetNumber { get; set; } = null;
public string? OfficeInvoiceAddressStreetNumberAddition { get; set; } = null;
public string? OfficeInvoiceAddressPostalcode { get; set; } = null;
public string? OfficeInvoiceAddressCity { get; set; } = null;
public string? OfficeInvoiceAddressCountry { get; set; } = null;
//[Ignore]
public bool? CopyOfficeAddressAsInvoiceAddress { get; set; } = false;
public string? AssociationIdentifier { get; set; } = null;
}
And I want it to be mapped to the following models:
public class Account
{
public int Id { get; set; }
public string AccountName { get; set; }
public IList<Contact> Users { get; set; }
public IList<Office> Offices { get; set; }
public string Bank { get; set; }
public string BankAccount { get; set; }
public string? AssociationIdentifier { get; set; }
}
public class Office
{
public int Id { get; set; }
public string Name { get; set; }
public Address ContactAddress { get; set; }
public Address InvoiceAddress { get; set; }
public bool HeadQuarter { get; set; }
}
public class Address
{
public int Id { get; set; }
public string Street { get; set; }
public string Postalcode { get; set; }
public int StreetNumber { get; set; }
public string StreetNumberAddition { get; set; }
public string City { get; set; }
public string Country { get; set; }
}
public class Contact
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string EMail { get; set; }
public string PhoneNumber { get; set; }
}
First of all, my experience is mainly using Automapper, and it is definitely possible to map complex types like this.
But your command does not need to be completely flat. There is nothing inherently wrong with DTOs being similar to your domain models. Using Automapper this is fairly easy as properties with the same name are mapped 1:1.
It could be that you are submitting a form with all the properties flattened in one object. In that case you could define either a seperate map for this object and each domain object.
CreateMap<AccountDto, Account>(); // mapping logic omitted
CreateMap<AccountDto, Office>();
...
Or you could map the one object to a range of objects using Tuples.
CreateMap<AccountDto, (Account, Office, ...)>(); // mapping logic omitted
But if you define seperate DTOs and make mapping profiles for them, it will probably ease your whole mapping experience. For copying the address, you can simply do something like this, in that case.
if (copyAddress)
{
office.InvoiceAddress = _mapper.Map<Address>(addressDto);
}
I currently have JSON coming in as follows:
{"36879":[{"min_qty":1,"discount_type":"%","csp_price":10}],"57950":[{"min_qty":1,"discount_type":"flat","csp_price":650}]}
This contains a list of the following records
ProductId
MinQty
DiscountType
Price
I need to deserialize this into the following model:
public class CustomerSpecificPricing
{
string productId { get; set; }
public virtual List<CustomerSpecificPricingDetail> CustomerSpecificPricingDetails { get; set; }
}
public class CustomerSpecificPricingDetail
{
public string min_qty { get; set; }
public string discount_type { get; set; }
public string csp_price { get; set; }
}
The problem is that the "productId" of each record is missing the key name.
If I run my JSON through J2C, I get the following:
public class 36879 {
public int min_qty { get; set; }
public string discount_type { get; set; }
public int csp_price { get; set; }
}
public class 57950 {
public int min_qty { get; set; }
public string discount_type { get; set; }
public int csp_price { get; set; }
}
public class Root {
public List<_36879> _36879 { get; set; }
public List<_57950> _57950 { get; set; }
}
Which is obviously incorrect.
How would I deserialize my object correctly?
You would need to deserialize it into a dictionary first and then map it into the format you require after. Something like this should work:
var dict = JsonConvert.DeserializeObject<Dictionary<string, IEnumerable<CustomerSpecificPricingDetail>>>();
var result = dict.Select(kvp => new CustomerSpecificPricing { ProductId = Int32.Parse(kvp.Key), CustomerSpecificPricingDetails = kvp.Value });
Id also recommend you follow the conventional standards of naming. In this case properties in classes should be PascalCase,
e.g. your classes now become:
public class CustomerSpecificPricing
{
[JsonProperty("productId ")]
public string ProductId { get; set; }
public virtual List<CustomerSpecificPricingDetail> CustomerSpecificPricingDetails { get; set; }
}
and
public class CustomerSpecificPricingDetail
{
[JsonProperty("min_qty")]
public string MinQty { get; set; }
[JsonProperty("discount_type ")]
public string DiscountType { get; set; }
[JsonProperty("csp_price ")]
public string CspPrice { get; set; }
}
I'm attempting to deserialize the following json:
{"PatientNameID":{"ID":514,"Name":{"First":"Laura","Middle":"X","Last":"Coelho","Suffix":"","Full":"Laura X Coelho","Preferred":""}},"PatientNumber":"254","ChartNumber":"254","Gender":{"LookupType":"Gender","Code":"F","Description":"Female","Order":1,"Active":true,"AlternateCodes":null},"DOB":"4/9/1953","PhoneNumber":"3521029496","SSN":"*****0161"}
This is the class and subclasses into which I'm trying to deserialize the above JSON:
public class PatientList3
{
public Pat PatientNameID { get; set; }
public string PatientNumber { get; set; }
public string ChartNumber { get; set; }
public Gender2 Gender { get; set; }
public string DOB { get; set; }
public string PhoneNumber { get; set; }
public string SSN { get; set; }
}
public class Pat
{
public int ID { get; set; }
public PtName Name { get; set; }
}
public class PtName
{
public string First { get; set; }
public string Middle { get; set; }
public string Last { get; set; }
public string Suffix { get; set; }
public string Full { get; set; }
public string Preferred { get; set; }
}
public class Gender2
{
string LookupType { get; set; }
string Code { get; set; }
string Description { get; set; }
int Order { get; set; }
bool Active { get; set; }
List<AlternateCodes> AlternateCodes { get; set; }
}
public class AlternateCodes
{
string Code { get; set; }
string Description { get; set; }
string CodeSystem { get; set; }
string CodeSystemName { get; set; }
}
Everything goes well when I deserialize it except all of the values in the Gender2 class are null.
I've referred to the following two posts for answers but nothing seems to be doing to trick.
JsonConvert.DeserializeObject<T>(JsonString) returning all Properties<T> as Null
DeSerializing JSON returns null C#
Fix the properties on Gender2 & AlternateCodes they are not public! The deserializer will not be able to find your any of your properties so that is probably the reason this fails to populate.
The problem is with access modifiers of properties Gender2 and AlternateCodes class, default access modifiers for properties is private. You should change it to:
public class Gender2
{
public string LookupType { get; set; }
public string Code { get; set; }
public string Description { get; set; }
public int Order { get; set; }
public bool Active { get; set; }
public List<AlternateCodes> AlternateCodes { get; set; }
}
public class AlternateCodes
{
public string Code { get; set; }
public string Description { get; set; }
public string CodeSystem { get; set; }
public string CodeSystemName { get; set; }
}
After setting properties to public it deserializes successfully:
Followed the steps mentioned in Automapper wiki to configure nested mapping with complex objects. but not working :
public class Student
{
public int ID{get;set;}
public string Name { get; set; }
public string Standard { get; set; }
public List<string> Course { get; set; }
public int Age { get; set; }
public string FatherName { get; set; }
public string MotherName{ get; set; }
public char Gender { get; set; }
public DateTime DOB { get; set; }
public string BloodGroup { get; set; }
public int TestCondition { get; set; }
public Address Address { get; set; }
}
public class Address
{
public int ID { get; set; }
public string Line1 { get; set; }
public string Line2 { get; set; }
public string Zip { get; set; }
}
DTO :
public class StudentDTO
{
public string Name { get; set; }
public string Standard { get; set; }
public char Gender { get; set; }
public DateTime DOB { get; set; }
public string BG { get; set; }
public int TestCondition { get; set; }
public AddressDTO AddressDTO { get; set; }
}
public class AddressDTO
{
public int ID { get; set; }
public string Line1 { get; set; }
public string Line2 { get; set; }
public string Zip { get; set; }
}
Configuration :
public class AutoMapperProfileConfig : Profile
{
public AutoMapperProfileConfig()
{
CreateMap<Student, StudentDTO>().ForMember(dest => dest.DOB, opt=>opt.Ignore())
.ForMember(x=>x.BG,opt=>opt.MapFrom(y=>y.BloodGroup))/*.AfterMap((src, dest) => dest.Name = "After MAP")*/
.ForMember(x=>x.TestCondition, opt=>opt.Condition(src => (src.TestCondition >= 100))
);
CreateMap<Address, AddressDTO>();
Execution :
Student student = new Student();
Address add = new Address();
add.Line1 = "abcd";
student.Address = add;
var studentLite = Mapper.Map<StudentDTO>(student);
Although studentDTO is getting mapped properly the AddressDTO is null.
You're mapping Student.Address to StudentDTO.AddressDTO. As the name of your target property has changed, AutoMapper doesn't know what to do with it. If you changed your propertyname in StudentDTO from AddressDTO into Address, Automapper should be able pick it up and map it.
An alternative solution would of course be to instruct AutoMapper explicitly to map Student.Address to StudentDTO.AddressDTO.
In my import function, i am replacing existing documents or adding new ones using the Upsert option:
var builder = Builders<FacilityDocument>.Filter;
var filter = builder.Eq(x => x.Language.LCID, lcid)
& builder.Eq(x => x.Name, facility.Name)
& builder.Eq(x => x.NameDetailed, facility.NameDetailed)
& builder.Gte(x => x.ImportedDate, new DateTime(DateTime.Now.Year, 1, 1));
collection.ReplaceOne(filter, facility, new UpdateOptions { IsUpsert = upsert });
Now in my POCO class, i have it configured to use GUID instead of an ObjectID, like this:
[BsonId]
[BsonIgnoreIfDefault]
public Guid ID { get; set; }
The problem is that in the database, instead of generating a GUID, its defaulting back to ObjectId:
And what it should be doing, is this (which works when using the regular Insert method):
Much appreciated if anyone have a solution for this.
Update
This is my entire facilitydocument class:
[BsonId]
[BsonIgnoreIfDefault]
public Guid ID { get; set; }
public string Name { get; set; }
public string NameDetailed { get; set; }
public CommuneDocument Commune { get; set; }
public string Address { get; set; }
public string Email { get; set; }
public string Homepage { get; set; }
public FacilityTypeDocument FacilityType { get; set; }
public FacilityPlacement FacilityPlacement { get; set; }
public int CoursesCount { get; set; }
public CoursesData[] CoursesData { get; set; }
public OwnershipDocument Ownership { get; set; }
public OperationDocument Operation { get; set; }
public string ExternalRemarks { get; set; }
public GisLocationData GisLocationData { get; set; }
public string ContactPersonName { get; set; }
public string ContactPersonEmail { get; set; }
public string InternalRemarks { get; set; }
public bool Active { get; set; }
public LanguageDocument Language { get; set; }
public DateTime ImportedDate { get; set; }
public string ImportedBy { get; set; }
public string UpdatedBy { get; set; }
public DateTime UpdatedDate { get; set; }
public List<ChangeLog> ChangeLog { get; set; }
Update 2
If i set my ID before the replace method, i get an error like this:
A write operation resulted in an error. The _id field cannot be changed from {_id: BinData(3, BF515DEF5743F547BD3EABB1A89DAC4D)} to {_id: BinData(3, 6364DF16640A4346B62E6B866BF76069)}
This is how i set it:
facilityDocument.ID = Guid.NewGuid();