AutoMapper - Nested Collections Not Getting Mapped - c#

I have an ASP.NET Core application with the following entity classes:
public class CustomerEntity : IAuditableEntity {
private readonly List<AddressEntity> _addresses = new List<AddressEntity>();
public CustomerEntity() {
Id = default;
IsPremierCustomer = false;
}
public int Id { get; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int CustomerTypeId { get; set; }
public string EmailAddress { get; set; }
public bool IsPremierCustomer { get; private set; }
public CustomerTypeEntity CustomerType { get; }
public IReadOnlyCollection<AddressEntity> Addresses => _addresses;
public void AddAddress(AddressEntity address) {
_addresses.Add(address);
}
public void SetPremierStatus(bool premierCustomer) {
if (IsPremierCustomer == premierCustomer) {
return;
}
IsPremierCustomer = premierCustomer;
}
}
public class CustomerTypeEntity {
public CustomerTypeEntity(string description) {
Id = default;
Description = description;
}
public int Id { get; }
public string Description { get; }
}
public class AddressEntity : IAuditableEntity {
public AddressEntity() {
Id = default;
}
public int Id { get; }
public int CustomerId { get; set; }
public int AddressTypeId { get; set; }
public string Line1 { get; set; }
public string Line2 { get; set; }
public string Line3 { get; set; }
public string Province { get; set; }
public string PostalCode { get; set; }
public int CountryId { get; set; }
public AddressTypeEntity AddressType { get; }
public CountryEntity Country { get; }
}
public class AddressTypeEntity {
public AddressTypeEntity(string description) {
Id = default;
Description = description;
}
public int Id { get; }
public string Description { get; }
}
public class CountryEntity {
public CountryEntity(int numericCode, string alpha2Code, string alpha3Code, string englishName, string frenchName) {
NumericCode = numericCode;
Alpha2Code = alpha2Code;
Alpha3Code = alpha3Code;
EnglishName = englishName;
FrenchName = frenchName;
}
public int NumericCode { get; }
public string Alpha2Code { get; }
public string Alpha3Code { get; }
public string EnglishName { get; }
public string FrenchName { get; }
}
Following are the corresponding DTO classes:
public class CustomerWithAddressDTO {
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string CustomerType { get; set; }
public string EmailAddress { get; set; }
public bool IsPremierCustomer { get; set; }
public IEnumerable<AddressDTO> Addresses { get; set; }
}
public class AddressDTO {
public int Id { get; set; }
public string AddressType { get; set; }
public string Line1 { get; set; }
public string Line2 { get; set; }
public string Line3 { get; set; }
public string Province { get; set; }
public string PostalCode { get; set; }
public string Country { get; set; }
}
I have mapping defined as follows:
public class CustomerWithAddressProfile : Profile {
public CustomerWithAddressProfile() {
CreateMap<AddressEntity, AddressDTO>()
.ForMember(dest => dest.AddressType, src => src.MapFrom(x => x.AddressType.Description))
.ForMember(dest => dest.Country, src => src.MapFrom(x => x.Country.Alpha3Code));
CreateMap<CustomerEntity, CustomerWithAddressDTO>()
.ForMember(dest => dest.CustomerType, src => src.MapFrom(x => x.CustomerType.Description))
.ForMember(dest => dest.Addresses, src => src.MapFrom(x => x.Addresses));
}
}
In my code, I fetch the customers including addresses from database and map as below:
[HttpGet]
public ActionResult<IEnumerable<CustomerWithAddressDTO>> GetAllCustomers() {
var customers = _customerWithAddressRepository.FindAll();
var customerDTOs = _mapper.Map<ICollection<CustomerEntity>, IEnumerable<CustomerDTO>>(customers);
return Ok(customerDTOs);
}
The customers are getting populated (mapped) correctly. But the nested addresses are never getting populated. I have searched for a solution but still no luck. Does anyone has any thoughts.
Thanks in advance.

Related

JSONpatch to remove item from ICollection

I am following TPH in my feature and I have these models which are mapped to tables in database
public class HrFormV1
{
public HrFormV1()
{
...
}
public HrFormV1(HrFormV1DTO form)
{
...
}
public string Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string OrganizationId { get; set; }
public Organizations Organization { get; set; }
public List<HrFormSectionV1> FormSections { get; set; }
public DateTime Created { get; set; }
public DateTime Updated { get; set; }
public HrFormType Type { get; set; }
public string CreatedById { get; set; }
public User CreatedBy { get; set; }
public static void DefineDbModel(ModelBuilder modelBuilder)
{
modelBuilder.Entity<HrFormV1>().Property(f => f.Id).ValueGeneratedOnAdd();
}
}
public class HrFormSectionV1
{
public string Id { get; set; }
public int Index { get; set; }
public string FormId { get; set; }
public HrFormV1 Form { get; set; }
public HrFormSectionV1()
{
...
}
public HrFormSectionV1(HrFormSectionV1DTO section)
{
...
BuildingComponents = section.BuildingComponents.Select(b => #switch[b.GetType()](b)).ToList();
}
public ICollection<HrBuildingComponentsV1> BuildingComponents { get; set; }
public static void DefineDbModel(ModelBuilder modelBuilder)
{
modelBuilder.Entity<HrFormSectionV1>(entity =>
{
entity.Property(e => e.Id).ValueGeneratedOnAdd();
});
}
private static readonly Dictionary<Type, Func<HrBuildingComponentV1DTO,HrBuildingComponentsV1>> #switch = new Dictionary<Type, Func<HrBuildingComponentV1DTO,HrBuildingComponentsV1>> {
{ typeof(HrTextBuildingComponentV1DTO), b => new HrTextBuildingComponentV1((HrTextBuildingComponentV1DTO)b) },
};
}
{
public class HrBuildingComponentsV1
{
public HrBuildingComponentsV1() { }
public HrBuildingComponentsV1(HrBuildingComponentV1DTO buildingComponent)
{
Id = buildingComponent.Id;
Label = buildingComponent.Label;
Type = buildingComponent.Type;
Index = buildingComponent.Index;
IsFilter = buildingComponent.IsFilter;
SectionId = buildingComponent.SectionId;
}
public string Id { get; set; }
public string Label { get; set; }
public BuildingComponentType Type { get; set; }
public int? Index { get; set; }
public bool IsFilter { get; set; }
public string SectionId { get; set; }
public HrFormSectionV1 Section { get; set; }
}
public enum BuildingComponentType
{
...
}
}
public class HrTextBuildingComponentV1 : HrBuildingComponentsV1
{
public string Value { get; set; }
public HrTextBuildingComponentV1() : base() { }
public HrTextBuildingComponentV1(HrTextBuildingComponentV1DTO buildingComponentdto)
: base(buildingComponentdto) {
Value = buildingComponentdto.Value;
}
}
So now I have following tables
HrFormV1
HrFormSectionV1
HrBuildingComponentsV1 (TPH) here
I am using Asp.Net core JSON patch to update the records. Add and replace works fine but when I do remove then I get an error
I am removing using this json
{
"op": "remove",
"path": "/formsections/0/buildingcomponents/0"
}
Here I get an error that SectionId cannot be NULL. Can someone guide me here?

Mapping multi-level nestingobjects using automapper

I have dto object:
public class SwivelInfoToViewDTO
{
public ResponseValue Value { get; set; }
public List<ResponseDocument> Documents { get; set; }
public string Status { get; set; }
}
public class ResponseValue
{
public string SerialNumber { get; set; }
public string ProductCode { get; set; }
public string ProductName { get; set; }
}
public class ResponseDocument
{
public string Language { get; set; }
public int DocumentTypeId { get; set; }
public string DocumentType { get; set; }
}
And i have my sorce class:
public class SwivelInformationResponse
{
public ResponseValue Value { get; set; }
public string Status { get; set; }
}
public class ResponseValue
{
[JsonProperty("serial_number")]
public string SerialNumber { get; set; }
[JsonProperty("product_code")]
public string ProductCode { get; set; }
public List<ResponseDocument> Documents { get; set; }
}
public class ResponseDocument
{
[JsonProperty("language")]
public string Language { get; set; }
[JsonProperty("document_type_id")]
public int DocumentTypeId { get; set; }
[JsonProperty("document_type")]
public string DocumentType { get; set; }
}
I use auto-mapper and my profile looks like this:
public MappingProfile()
{
CreateMap<SwivelInformationResponse, SwivelInfoToViewDTO>()
.ForMember(x => x.Value, s => s.MapFrom(src => src.Value))
.ForMember(x => x.Documents, s => s.MapFrom(src => src.Value.Documents));
}
But somehow I get an error:
Error mapping types.
Destination Member:
Value
How do I make bindings correctly?
You forgot to map your ResponseValue and ResponseDocument classes. It's not the same classes so you need to map them as well.
public MappingProfile()
{
CreateMap<SourceNamespace.ResponseValue, DtoNamespace.ResponseValue>();
CreateMap<SourceNamespace.ResponseDocument, DtoNamespace.ResponseDocument>();
}

Creating Viewmodels and mapping to entities

I am creating ViewModels for my entities. The FirmViewModel contains two list collections called Addressess and Websites. I have also created viewmodels for Addresses and Websites. At the moment in the controller code, I am getting trouble assigning Addresses and Wesbite collections
of the Firm Entity to FirmViewModels Addresses and Websites collection. Its says cant cast it implicitly. This is the line in the controller that it complains Addresses = firm.Addresses; How do aassign
Systems.Collections.Generic.ICollection<Manager.Model.Address> to Systems.Collections.Generic.ICollection<Manager.WebUI.ViewModels.AddressViewModel>
NewFirmViewModel
public class NewFirmViewModel
{
public int FirmId { get; set; }
public string FirmName { get; set;}
public Nullable<DateTime> DateFounded { get; set; }
public ICollection<AddressViewModel> Addresses { get; set; }
public ICollection<WebsiteViewModel> Websites { get; set; }
public bool hasIntralinks { get; set; }
}
AddressViewModel
public class AddressViewModel
{
public int AddressId { get; set; }
public string Line1 { get; set; }
public string Line2 { get; set; }
public string Line3 { get; set; }
public string Phone { get; set; }
public bool IsHeadOffice { get; set; }
public int FirmId { get; set; }
}
WebsiteViewModel
public class WebsiteViewModel
{
private int FirmWebsiteId { get; set; }
private string WebsiteUrl { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public int FirmId { get; set; }
}
Entities - Firm
public class FIRM: Entity,IHasAUMs<FIRM_AUM>
{
public FIRM()
{
//this.FIRM_PERSON = new HashSet<FIRM_PERSON>();
this.MANAGERSTRATEGies = new HashSet<MANAGERSTRATEGY>();
this.FIRM_ACTIVITY = new HashSet<FIRM_ACTIVITY>();
this.FIRM_AUMs = new HashSet<FIRM_AUM>();
this.FIRM_REGISTRATION = new HashSet<FIRM_REGISTRATION>();
//this.ACTIVITies = new HashSet<ACTIVITY>();
Addresses = new HashSet<ADDRESS>();
//People = new HashSet<PERSON>();
// Websites = new HashSet<FIRM_WEBSITE>();
}
//public decimal ID { get; set; }
//
//
//
//
public string NAME { get; set; }
public string SHORT_NAME { get; set; }
public string ALTERNATE_NAME { get; set; }
public string WEBSITE { get; set; }
public string WEBSITE_USERNAME { get; set; }
public string WEBSITE_PASSWORD { get; set; }
public bool? INTRALINKS_FIRM { get; set; }
public string NOTES_TEXT { get; set; }
public string NOTES_HTML { get; set; }
public string HISTORY_TEXT { get; set; }
public string HISTORY_HTML { get; set; }
public string HISTORY_SUM_TEXT { get; set; }
public string HISTORY_SUM_HTML { get; set; }
public Nullable<decimal> OLD_ORG_REF { get; set; }
public Nullable<decimal> SOURCE_ID { get; set; }
[DisplayFormat(DataFormatString = PermalConstants.DateFormat)]
public Nullable<DateTime> DATE_FOUNDED { get; set; }
public virtual ICollection<ADDRESS> Addresses { get; set; }
// public ICollection<FIRM_WEBSITE> Websites { get; set; }
// public ICollection<PERSON> People { get; set; }
//public SOURCE SOURCE { get; set; }
// public ICollection<FIRM_PERSON> FIRM_PERSON { get; set; }
public ICollection<MANAGERSTRATEGY> MANAGERSTRATEGies { get; set; }
public ICollection<FIRM_ACTIVITY> FIRM_ACTIVITY { get; set; }
public ICollection<FIRM_REGISTRATION> FIRM_REGISTRATION { get; set; }
//public ICollection<ACTIVITY> ACTIVITies { get; set; }
public ICollection<FIRM_WEBSITE> Websites { get; set; }
public Nullable<int> KEY_CONTACT_ID { get; set; }
[NotMapped]
public ICollection<FIRM_AUM> AUMs
{
get
{
return this.FIRM_AUMs;
}
}
public ICollection<FIRM_AUM> FIRM_AUMs { get; set; }
}
ADDRESS
public class ADDRESS : Entity
{
public ADDRESS()
{
// DATE_CREATED = DateTime.Now;
}
public string LINE1 { get; set; }
public string LINE2 { get; set; }
public string LINE3 { get; set; }
public int CITY_ID { get; set; }
public string POSTAL_CODE { get; set; }
public string SWITCHBOARD_INT { get; set; }
public string NOTES { get; set; }
public int? OLD_ADDRESS_REF { get; set; }
public int? SOURCE_ID { get; set; }
public int FIRM_ID { get; set; }
[ForeignKey("FIRM_ID")]
public FIRM FIRM { get; set; }
[ForeignKey("CITY_ID")]
public CITY City { get; set; }
public ICollection<PERSON> People { get; set; }
// public SOURCE SOURCE { get; set; }
public bool IS_HEAD_OFFICE { get; set; }
[NotMapped]
public string AddressBlurb
{
get
{
return string.Join(",", new[] { LINE1, LINE2, City != null ? City.NAME : "", City != null && City.Country != null ? City.Country.NAME : "" }.Where(x => !string.IsNullOrEmpty(x)));
}
}
}
FIRM_WEBSITE
public class FIRM_WEBSITE : Entity
{
public FIRM_WEBSITE()
{
}
private string _WEBSITE_URL;
public string WEBSITE_URL
{
get
{
if (string.IsNullOrEmpty(_WEBSITE_URL))
return _WEBSITE_URL;
try
{
var ubuilder = new System.UriBuilder(_WEBSITE_URL ?? "");
return ubuilder.Uri.AbsoluteUri;
}
catch (UriFormatException ex)
{
return _WEBSITE_URL;
}
}
set { _WEBSITE_URL = value; }
}
public string USERNAME { get; set; }
public string PASSWORD { get; set; }
public int FIRM_ID { get; set; }
[ForeignKey("FIRM_ID")]
public FIRM FIRM { get; set; }
}
FirmController
public class FirmController : ApiControllerBase
{
[HttpGet]
[Route("api/Firm/{id}")]
public IHttpActionResult Details(int id)
{
var viewModel = GetFirmViewModel(id);
return Ok(viewModel);
}
private NewFirmViewModel GetFirmViewModel(int id)
{
var firmSvc = GetService<FIRM>();
var firm = firmSvc.GetWithIncludes(id, f => f.Addresses, f => f.Websites);
var firmVm = new NewFirmViewModel()
{
FirmId = firm.ID,
FirmName = firm.NAME,
DateFounded = firm.DATE_FOUNDED,
Addresses = firm.Addresses;
};
}
public virtual T GetWithIncludes(int id, params Expression<Func<T, object>>[] paths)
{
try
{
using (new TimedLogger(_perfLogger, GetCompletedText("GetWithIncludes"), typeof(T).Name))
{
return Authorize(_repo.GetWithIncludes(id, paths), AuthAccessLevel.Read);
}
}
catch (Exception ex) { Log(ex); throw; }
}

AutoMapper Map nested ICollection

I want to map one call to another, but I got exception all the time /An exception of type 'AutoMapper.AutoMapperMappingException' occurred in AutoMapper.dll but was not handled in user code/
Here are my Source Classes:
public class Snippet
{
public Snippet()
{
this.Labels = new HashSet<Label>();
this.Commennts = new HashSet<Comment>();
}
public int Id { get; set; }
[Required]
public string Title { get; set; }
public string Description { get; set; }
[Required]
public string Code { get; set; }
public int LanguageId { get; set; }
public string UserId { get; set; }
public DateTime CreatedOn { get; set; }
public ICollection<Label> Labels { get; set; }
public ICollection<Comment> Commennts { get; set; }
public virtual Language Language { get; set; }
public virtual ApplicationUser User { get; set; }
}
public class Label
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Snippet> Snippets { get; set; }
}
Here are my Destination classes:
public class SnippetModels
{
public class Output
{
public int Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public DateTime CreatedOn { get; set; }
public string Code { get; set; }
public string LanguageName { get; set; }
public string UserUsername { get; set; }
public IEnumerable<LabelModels.Output> Labels { get; set; }
//TODO Comments Labels
}
}
public class LabelModels
{
public class Output
{
public int Id { get; set; }
public string Name { get; set; }
}
}
public class HomeViewModel
{
public IEnumerable<SnippetModels.Output> Snippets { get; set; }
public IEnumerable<LabelModels.Output> Labels { get; set; }
}
And finally I tied everything that pop up in my mind but no success:
Mapper.CreateMap<Snippy.Models.Label, LabelModels.Output>();
Mapper.CreateMap<IEnumerable<Snippy.Models.Label>, IEnumerable<LabelModels.Output>>();
Mapper.CreateMap<Snippy.Models.Snippet, SnippetModels.Output>()
.ForMember(dest => dest.Labels, opt => opt.MapFrom(src => src.Labels));
Mapper.CreateMap<IEnumerable<Snippy.Models.Snippet>, IEnumerable<SnippetModels.Output>>();
var newestSnippetsDatabase = this.Data.Snippets
.All()
.OrderByDescending(s => s.CreatedOn)
.Take(HomeVisibleItemsCount)
.Select(s => s)
.ToList();
var homeScreenView = new HomeViewModel
{
Snippets = Mapper.Map<IEnumerable<SnippetModels.Output>>(newestSnippetsDatabase)
};

Paging with Xamarin

I am trying to load old Instagram pictures when the user pulls the UITableView to the bottom of the screen using Xamarin. However I seem to have no clue how to do it. I got it working fine in Objective-C it just seems different in C#. Here is how I am getting the initial posts:
public void getInstagramFeed(UITableView table){
IEnumerable<Account> accounts = AccountStore.Create ().FindAccountsForService ("Instagram");
var enumerable = accounts as IList<Account> ?? accounts.ToList ();
if (enumerable.Any ()) {
Account instagram = enumerable.First ();
var instagramAccessToken = instagram.Properties ["access_token"].ToString ();
var request = new RestRequest { RootElement = "data", Resource = "/users/self/feed" };
request.AddParameter ("access_token", instagramAccessToken);
var client = new RestClient ("https://api.instagram.com/v1");
client.ExecuteAsync (request, response => {
table.InvokeOnMainThread (() => {
RootObject rootObject = JsonConvert.DeserializeObject<RootObject> (response.Content);
var dataSource = new ObservableDataSource<Datum> (rootObject.data);
dataSource.Bind (table);
});
});
}
}
Here is the bind method for my datasource:
public void Bind(Datum datum)
{
this.datum = datum;
if (this.datum == null || this.datum.caption == null)
{
this.captionLabel.Text = "";
}
else
{
this.captionLabel.Text = datum.caption.text;
}
this.pictureImage.InvokeOnMainThread (() => this.pictureImage.SetImage (
url: new NSUrl (datum.images.standard_resolution.url)
)
);
this.profileImage.InvokeOnMainThread (() => this.profileImage.SetImage (
url: new NSUrl (datum.user.profile_picture)
)
);
this.nameLabel.Text = this.datum.user == null ? "user is null" : datum.user.full_name;
}
Here is the class for my tableView:
public TableView ()
{
}
public TableView (IntPtr handle) : base(handle)
{
}
public UITableViewCell GetCell (Datum item)
{
var newCell = this.DequeueReusableCell(InstagramCell.Key)
as InstagramCell ?? InstagramCell.Create();
newCell.Bind (item);
return newCell;
}
public float GetHeightForRow (NSIndexPath indexPath)
{
return 340f;
}
Thanks for all the help before hand, sorry for the long post :)
// PS This is how I did it in OBJc:
NSDictionary *page = instagramResponse[#"pagination"];
NSString *nextPage = page[#"next_url"];
[[InstagramClient sharedClient] getPath:[NSString stringWithFormat:#"%#",nextPage] parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
instagramResponse = [responseObject mutableCopy];
[instagramResponse addEntriesFromDictionary:responseObject];
[instapics addObjectsFromArray:responseObject[#"data"]];
[self updateArrays];
[self.tableView reloadData];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failure: %#", error);
}];
// Class for InstagramClient:
public class RootObject
{
public Pagination pagination { get; set; }
public Meta meta { get; set; }
public List<Datum> data { get; set; }
}
public class Datum
{
public object attribution { get; set; }
public List<string> tags { get; set; }
public string type { get; set; }
public object location { get; set; }
public Comments comments { get; set; }
public string filter { get; set; }
public string created_time { get; set; }
public string link { get; set; }
public Likes likes { get; set; }
public Images images { get; set; }
public List<object> users_in_photo { get; set; }
public Caption caption { get; set; }
public bool user_has_liked { get; set; }
public string id { get; set; }
public User user { get; set; }
public Videos videos { get; set; }
public override string ToString()
{
if (user == null)
{
return "User is null";
}
return user.full_name;
}
}
public class Videos
{
public LowResolution2 low_resolution { get; set; }
public StandardResolution2 standard_resolution { get; set; }
}
public class StandardResolution2
{
public string url { get; set; }
public int width { get; set; }
public int height { get; set; }
}
public class LowResolution2
{
public string url { get; set; }
public int width { get; set; }
public int height { get; set; }
}
public class User
{
public string username { get; set; }
public string website { get; set; }
public string profile_picture { get; set; }
public string full_name { get; set; }
public string bio { get; set; }
public string id { get; set; }
}
public class Caption
{
public string created_time { get; set; }
public string text { get; set; }
public From from { get; set; }
public string id { get; set; }
}
public class From
{
public string username { get; set; }
public string profile_picture { get; set; }
public string id { get; set; }
public string full_name { get; set; }
}
public class Images
{
public LowResolution low_resolution { get; set; }
public Thumbnail thumbnail { get; set; }
public StandardResolution standard_resolution { get; set; }
}
public class StandardResolution
{
public string url { get; set; }
public int width { get; set; }
public int height { get; set; }
}
public class Thumbnail
{
public string url { get; set; }
public int width { get; set; }
public int height { get; set; }
}
public class LowResolution
{
public string url { get; set; }
public int width { get; set; }
public int height { get; set; }
}
public class Likes
{
public int count { get; set; }
public List<Datum2> data { get; set; }
}
public class Datum2
{
public string username { get; set; }
public string profile_picture { get; set; }
public string id { get; set; }
public string full_name { get; set; }
}
public class Comments
{
public int count { get; set; }
public List<object> data { get; set; }
}
public class Meta
{
public int code { get; set; }
}
public class Pagination
{
public string next_url { get; set; }
public string next_max_id { get; set; }
}
I would recommend using a UiCollectionViewController for this if possible. Or nowadays preferably the equivalent in Xamarin.Forms. With UiCollectionViewController you can easily load more items as the user scrolls down

Categories

Resources