EF Core connect Entity to View. One to Many - c#

I have simple project to show the problem, I want to connect ICollection<Post> with view View_BlogPosts. This is just simplified scenario, in real live i need to connect entity with big View with many columns from different tables.
The most interesting part of code is: OnModelCreating(ModelBuilder modelBuilder) where there is configuration View with Entity: Post (One Blog to Many Posts). But its not working now, this line of code: var test = db.BlogWithPosts.ToList(); returns empty collection of Posts.
How to fix this?
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace Samples
{
public class Program
{
private static void Main()
{
SetupDatabase();
using (var db = new BloggingContext())
{
var test = db.BlogWithPosts.ToList();
}
}
private static void SetupDatabase()
{
using (var db = new BloggingContext())
{
if (db.Database.EnsureCreated())
{
db.Blogs.Add(
new Blog
{
Name = "Fish Blog",
Url = "http://sample.com/blogs/fish",
Posts = new List<Post>
{
new Post { Title = "Fish care 101" },
new Post { Title = "Caring for tropical fish" },
new Post { Title = "Types of ornamental fish" }
}
});
db.Blogs.Add(
new Blog
{
Name = "Cats Blog",
Url = "http://sample.com/blogs/cats",
Posts = new List<Post>
{
new Post { Title = "Cat care 101" },
new Post { Title = "Caring for tropical cats" },
new Post { Title = "Types of ornamental cats" }
}
});
db.Blogs.Add(
new Blog
{
Name = "Catfish Blog",
Url = "http://sample.com/blogs/catfish",
Posts = new List<Post>
{
new Post { Title = "Catfish care 101" }, new Post { Title = "History of the catfish name" }
}
});
db.SaveChanges();
db.Database.ExecuteSqlRaw(
#"CREATE VIEW View_BlogPosts AS
SELECT b.Name , b.BlogId, b.Url FROM Blogs b");
}
}
}
}
public class BloggingContext : DbContext
{
private static readonly ILoggerFactory _loggerFactory
= LoggerFactory.Create(
builder => builder.AddConsole().AddFilter((c, l) => l == LogLevel.Information && !c.EndsWith("Connection")));
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
public DbSet<BlogWithPosts> BlogWithPosts { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseSqlServer(
// #"Server=(localdb)\mssqllocaldb;Database=Sample.KeylessEntityTypes;Trusted_Connection=True;ConnectRetryCount=0;")
#"Server=.\SQLEXPRESS;Database=test_view;Trusted_Connection=True;")
.UseLoggerFactory(_loggerFactory);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<BlogWithPosts>(eb =>
{
//eb.HasNoKey();
eb.ToView("View_BlogPosts");
eb.HasKey(bwp => bwp.BlogId);
eb.Property(v => v.BlogName).HasColumnName("Name");
eb
.HasMany(bwp => bwp.Posts)
.WithOne()
.HasForeignKey(p => p.BlogId);
});
}
}
public class Blog
{
public int BlogId { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public ICollection<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
}
public class BlogWithPosts
{
public int BlogId { get; set; }
public string BlogName { get; set; }
public ICollection<Post> Posts { get; set; } = new List<Post>();
}
}
EDIT :
Thanks #Neil W for answer:
It is good point but after var test = db.BlogWithPosts.Include(bwp => bwp.Posts).ToList(); there is still no Posts.
I have checked Database after run program and in Post table I find out, that there is second Id added:BlogId1
I have filled BlogId column same as BlogId1 like this:
and posts appeared
But how to set configuration that second id: BlogId1 will not appear.

You need to ask for the related entities when accessing the context, using Include:
var test = db.BlogWithPosts.Include(bwp => bwp.Posts).ToList();

Thanks for all answers and comments. As #atiyar point there should be explicitly relation between Post and Blog which stops creating BlogId1 column. So working example is like below:
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace Samples
{
public class Program
{
private static void Main()
{
SetupDatabase();
using (var db = new BloggingContext())
{
var test = db.BlogWithPosts.Include(bp => bp.Posts).ToList();
}
}
private static void SetupDatabase()
{
using (var db = new BloggingContext())
{
if (db.Database.EnsureCreated())
{
db.Blogs.Add(
new Blog
{
Name = "Fish Blog",
Url = "http://sample.com/blogs/fish",
Posts = new List<Post>
{
new Post { Title = "Fish care 101" },
new Post { Title = "Caring for tropical fish" },
new Post { Title = "Types of ornamental fish" }
}
});
db.Blogs.Add(
new Blog
{
Name = "Cats Blog",
Url = "http://sample.com/blogs/cats",
Posts = new List<Post>
{
new Post { Title = "Cat care 101" },
new Post { Title = "Caring for tropical cats" },
new Post { Title = "Types of ornamental cats" }
}
});
db.Blogs.Add(
new Blog
{
Name = "Catfish Blog",
Url = "http://sample.com/blogs/catfish",
Posts = new List<Post>
{
new Post { Title = "Catfish care 101" }, new Post { Title = "History of the catfish name" }
}
});
db.SaveChanges();
db.Database.ExecuteSqlRaw(
#"CREATE VIEW View_BlogPosts AS
SELECT b.Name , b.BlogId, b.Url FROM Blogs b");
}
}
}
}
public class BloggingContext : DbContext
{
private static readonly ILoggerFactory _loggerFactory
= LoggerFactory.Create(
builder => builder.AddConsole().AddFilter((c, l) => l == LogLevel.Information && !c.EndsWith("Connection")));
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
public DbSet<BlogWithPosts> BlogWithPosts { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseSqlServer(
// #"Server=(localdb)\mssqllocaldb;Database=Sample.KeylessEntityTypes;Trusted_Connection=True;ConnectRetryCount=0;")
#"Server=.\SQLEXPRESS;Database=test_view;Trusted_Connection=True;")
.UseLoggerFactory(_loggerFactory);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<BlogWithPosts>(eb =>
{
//eb.HasNoKey();
eb.ToView("View_BlogPosts");
eb.HasKey(bwp => bwp.BlogId);
eb.Property(v => v.BlogName).HasColumnName("Name");
eb
.HasMany(bwp => bwp.Posts)
.WithOne()
.HasForeignKey(p => p.BlogId);
});
modelBuilder.Entity<Blog>(blog =>
{
blog.HasMany(bwp => bwp.Posts)
.WithOne(b => b.Blog)
.HasForeignKey(p => p.BlogId);
});
}
}
public class Blog
{
public int BlogId { get; set; }
public string Name { get; set; }
public string Url { get; set; }
public ICollection<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public Blog Blog { get; set; }
}
public class BlogWithPosts
{
public int BlogId { get; set; }
public string BlogName { get; set; }
public ICollection<Post> Posts { get; set; } = new List<Post>();
}
}
What is important this example was tested on EF Core 5.0.2

Related

C# Bad Performance OData when using extension

I have a Web API for OData services. I have a lot of table with many relations. Here is some of the table:
MSADDRESSCOUNTRY
public partial class MSADDRESSCOUNTRY
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage","CA2214:DoNotCallOverridableMethodsInConstructors")]
public MSADDRESSCOUNTRY()
{
this.MSADDRESSPROVINCEs = new HashSet<MSADDRESSPROVINCE>();
}
public int ID { get; set; }
public string CODE { get; set; }
public string COUNTRYNAME { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage","CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<MSADDRESSPROVINCE> MSADDRESSPROVINCEs { get; set; }
}
MSADDRESSPROVINCE
public partial class MSADDRESSPROVINCE
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public MSADDRESSPROVINCE()
{
this.MSADDRESSDISTRICTs = new HashSet<MSADDRESSDISTRICT>();
}
public int ID { get; set; }
public Nullable<int> COUNTRYID { get; set; }
public string PROVINCENAME { get; set; }
public virtual MSADDRESSCOUNTRY MSADDRESSCOUNTRY { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage","CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<MSADDRESSDISTRICT> MSADDRESSDISTRICTs { get; set; }
}
MSADDRESSDISTRICT
public partial class MSADDRESSDISTRICT
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public MSADDRESSDISTRICT()
{
this.MSADDRESSSUBDISTRICTs = new HashSet<MSADDRESSSUBDISTRICT>();
}
public int ID { get; set; }
public Nullable<int> PROVINCEID { get; set; }
public string DISTRICTNAME { get; set; }
public virtual MSADDRESSPROVINCE MSADDRESSPROVINCE { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<MSADDRESSSUBDISTRICT> MSADDRESSSUBDISTRICTs { get; set; }
}
I create DTO object model for every table with the property is the same with Database object model.
I want the client can use $expand keyword to get child data and/or parent data.
For MSADDRESSCOUNTRY I need to write the code like this.
[EnableQuery(MaxExpansionDepth = 4)]
public IQueryable<MsAddressCountryObject> Get()
{
return db.MSADDRESSCOUNTRies.Select(c => new MsAddressCountryObject
{
ID = c.ID,
CODE = c.CODE,
COUNTRYNAME = c.COUNTRYNAME,
MSADDRESSPROVINCEs = c.MSADDRESSPROVINCEs.Select(data => new MsAddressProvinceObject()
{
ID = data.ID,
COUNTRYID = data.COUNTRYID,
PROVINCENAME = data.PROVINCENAME,
MSADDRESSCOUNTRY = new MsAddressCountryObject()
{
ID = data.MSADDRESSCOUNTRY.ID,
CODE = data.MSADDRESSCOUNTRY.CODE,
COUNTRYNAME = data.MSADDRESSCOUNTRY.COUNTRYNAME,
},
MSADDRESSDISTRICTs = data.MSADDRESSDISTRICTs.Select(dist => new MsAddressDistrictObject()
{
ID = dist.ID,
PROVINCEID = dist.PROVINCEID,
DISTRICTNAME = dist.DISTRICTNAME,
})
})
});
}
For MSADDRESSPROVINCE I need to write the code like this.
[EnableQuery(MaxExpansionDepth = 4)]
public IQueryable<MsAddressProvinceObject> Get()
{
return db.MSADDRESSPROVINCEs.Select(data => new MsAddressProvinceObject()
{
ID = data.ID,
COUNTRYID = data.COUNTRYID,
PROVINCENAME = data.PROVINCENAME,
MSADDRESSCOUNTRY = new MsAddressCountryObject()
{
ID = data.MSADDRESSCOUNTRY.ID,
CODE = data.MSADDRESSCOUNTRY.CODE,
COUNTRYNAME = data.MSADDRESSCOUNTRY.COUNTRYNAME,
},
MSADDRESSDISTRICTs = data.MSADDRESSDISTRICTs.Select(dist => new MsAddressDistrictObject()
{
ID = dist.ID,
PROVINCEID = dist.PROVINCEID,
DISTRICTNAME = dist.DISTRICTNAME
})
});
}
That code works fast. But if I add/change/remove column, I have to modify the controller manually, one by one for all controller. For example, if I want to add geological coordinate in MSADDRESSDISTRICT, I have to change the code in Country Controller, Province Controller and District Controller.
So I decide to create extension method like this.
public static MsAddressCountryObject ToDTO(this MSADDRESSCOUNTRY data)
{
return new MsAddressCountryObject()
{
ID = data.ID,
CODE = data.CODE,
COUNTRYNAME = data.COUNTRYNAME,
};
}
public static IQueryable<MsAddressCountryObject ToDTO(this IEnumerable<MSADDRESSCOUNTRY datas)
{
return datas.Select(country =
{
var obj = country?.ToDTO();
obj.MSADDRESSPROVINCEs = country.MSADDRESSPROVINCEs?.ToDTO();
return obj;
}).AsQueryable();
}
public static MsAddressProvinceObject ToDTO(this MSADDRESSPROVINCE data)
{
return new MsAddressProvinceObject()
{
ID = data.ID,
COUNTRYID = data.COUNTRYID,
PROVINCENAME = data.PROVINCENAME,
MSADDRESSCOUNTRY = data.MSADDRESSCOUNTRY?.ToDTO()
};
}
public static IQueryable<MsAddressProvinceObject ToDTO(this IEnumerable<MSADDRESSPROVINCE datas)
{
return datas.Select(province =
{
var obj = province?.ToDTO();
obj.MSADDRESSDISTRICTs = province.MSADDRESSDISTRICTs.ToDTO();
return obj;
}).AsQueryable();
}
public static MsAddressDistrictObject ToDTO(this MSADDRESSDISTRICT data)
{
return new MsAddressDistrictObject()
{
ID = data.ID,
PROVINCEID = data.PROVINCEID,
DISTRICTNAME = data.DISTRICTNAME,
MSADDRESSPROVINCE = data.MSADDRESSPROVINCE?.ToDTO()
};
}
public static IQueryable<MsAddressDistrictObject ToDTO(this IEnumerable<MSADDRESSDISTRICT datas)
{
return datas.Select(district =
{
var obj = district?.ToDTO();
obj.MSADDRESSSUBDISTRICTs = district.MSADDRESSSUBDISTRICTs?.ToDTO();
return obj;
}).AsQueryable();
}
And the controller just like this.
[EnableQuery(MaxExpansionDepth = 4)]
public IQueryable<MsAddressCountryObject Get()
{
return db.MSADDRESSCOUNTRies.ToDTO()
}
And that makes the performance really bad. I think the extension is making a lot of memory allocation or some thing that make the result not being delivered directly to the client.
My goal is to create the code easy to maintain, and the performance not drop significantly.
I have many relation in other table. I want the $expand works without write all parent/child Select statement manually and one by one.
I have try to not calling ToDTO() from all the extension method. The result is the performance is fast. But I lost all the relation or I need to write the parent/child Select statement for all method.
Any suggestion will help.
Thanks.

Select the last 4 posts of the site on the main page

I want to display the last 4 articles of the last site on the main page in a concise way. I have made a model view and I only have a problem with ef core.
ViewModel:
public class ShowBlogForHomePageViewModel
{
public int BlogId { get; set; }
public string BlogTitle { get; set; }
public string BlogUrl { get; set; }
public string BlogImageName { get; set; }
public string MetaDescriptionBlog { get; set; }
}
IBlogService:
List<ShowBlogForHomePageViewModel> GetBlogPostForHome();
BlogService:
public List<ShowBlogForHomePageViewModel> GetBlogPostForHome()
{
var ListBlogs= _context.Blogs.OrderByDescending(b => new ShowBlogForHomePageViewModel()
{
BlogId = b.BlogId,
BlogImageName = b.BlogImageName,
BlogTitle = b.BlogTitle,
MetaDescriptionBlog = b.MetaDescriptionBlog,
BlogUrl = b.BlogUrl
}).Take(4).ToList();
return null;
}
I think this part is true . please check it:
public List<ShowBlogForHomePageViewModel> GetBlogPostForHome()
{
return _context.Blogs.OrderBy(b=>b.BlogId).Select(b => new ShowBlogForHomePageViewModel()
{
BlogId = b.BlogId,
BlogImageName = b.BlogImageName,
BlogTitle = b.BlogTitle,
MetaDescriptionBlog = b.MetaDescriptionBlog,
BlogUrl = b.BlogTitle
}).TakeLast(4).ToList();
}
try this
public List<ShowBlogForHomePageViewModel> GetBlogPostForHome()
{
return _context.Blogs
.OrderByDescending(b => b.BlogId)
.Select(b => new ShowBlogForHomePageViewModel
{
BlogId = b.BlogId,
BlogImageName = b.BlogImageName,
BlogTitle = b.BlogTitle,
MetaDescriptionBlog = b.MetaDescriptionBlog,
BlogUrl = b.BlogUrl
}).Take(4)
.ToList();
}

Pass list from model class to DbInitliazer

I'm new to C#. I'm working on a web app project. I want to know how to initialize the list in my DbInitializer class. For example, this is the Model:
using System;
using System.Collections.Generic;
namespace Manager.Model
{
public class Vendor
{
public int VendorID { get; set; }
public string CardName { get; set; }
public string WebsiteLink { get; set; }
public DateTime PartnerSince { get; set; }
public List<Rep> Reps { get; set; }
public string SupportNo { get; set; }
public string SupportEmail { get; set; }
public string Rebate { get; set; }
public string Spiff { get; set; }
public string Quote { get; set; }
}
public class Rep
{
public string RepName { get; set; }
public string RepPosition { get; set; }
public string RepNo { get; set; }
public string RepEmail { get; set; }
}
}
How would I pass this list in the Initialize method?
public static void Initialize(ManagementContext context)
{
context.Database.EnsureCreated();
// Look for any students.
if (context.Vendors.Any())
{
return; // DB has been seeded
}
var vendors = new Vendor[]
{
new Vendor{CardName="Vendor1", WebsiteLink="www.vendor1.com", PartnerSince=DateTime.Parse("10-10-2012"), SupportNo="521-586-8956", SupportEmail="nikki#vendor1.com"},
};
foreach (Vendor v in vendors)
{
context.Vendors.Add(v);
}
context.SaveChanges();
If you'd like to do everything inline:
Vendor[] vendors = new Vendor[]
{
new Vendor() // first vendor
{
CardName="Vendor1",
WebsiteLink="www.vendor1.com",
PartnerSince=DateTime.Parse("10-10-2012"),
SupportNo="521-586-8956",
SupportEmail="nikki#vendor1.com",
Reps = new List<Rep>()
{
new Rep() // first rep
{
RepName = "name",
RepPosition = "pos",
RepNo = "no",
RepEmail = "email"
}
// , new Rep(){...} // second rep, etc...
}
}
// , new Vendor(){....} // second vendor, etc...
};
Or simply prepare the Reps first:
List<Rep> Reps1 = new List<Rep>(); // Reps 1 for Vendor 1
Reps1.Add(new Rep()
{
RepName = "name",
RepPosition = "pos",
RepNo = "no",
RepEmail = "email"
});
// you may add more rep
then assign it in vendor
Vendor[] vendors = new Vendor[]
{
new Vendor() // first vendor
{
CardName="Vendor1",
WebsiteLink="www.vendor1.com",
PartnerSince=DateTime.Parse("10-10-2012"),
SupportNo="521-586-8956",
SupportEmail="nikki#vendor1.com",
Reps = Reps1
}
// , new Vendor(){....} // second vendor, etc...
};
For question if you change into string[] RepNames,
string[] RepNames1 = new string[]
{
"name1",
"name2" // , etc....
}
then assign it in vendor
Vendor[] vendors = new Vendor[]
{
new Vendor() // first vendor
{
CardName="Vendor1",
WebsiteLink="www.vendor1.com",
PartnerSince=DateTime.Parse("10-10-2012"),
SupportNo="521-586-8956",
SupportEmail="nikki#vendor1.com",
RepNames = RepNames1
}
// , new Vendor(){....} // second vendor, etc...
};

How to map objects

Here is my how my bindings currently look:
MerchantAccountRequest request = new MerchantAccountRequest
{
Individual = new IndividualRequest
{
FirstName = merchant.MerchantIndividual.FirstName,
LastName = merchant.MerchantIndividual.LastName,
Email = merchant.MerchantIndividual.Email,
Phone = merchant.MerchantIndividual.Phone,
DateOfBirth = merchant.MerchantIndividual.DateOfBirth,
Ssn = merchant.MerchantIndividual.Ssn,
Address = new AddressRequest
{
StreetAddress = merchant.MerchantIndividual.StreetAddress,
Locality = merchant.MerchantIndividual.Locality,
Region = merchant.MerchantIndividual.Region,
PostalCode = merchant.MerchantIndividual.PostalCode
}
},
Business = new BusinessRequest
{
LegalName = merchant.MerchantBusiness.LegalName,
DbaName = merchant.MerchantBusiness.DbaName,
TaxId = merchant.MerchantBusiness.TaxId,
Address = new AddressRequest
{
StreetAddress = merchant.MerchantBusiness.StreetAddress,
Locality = merchant.MerchantBusiness.Locality,
Region = merchant.MerchantBusiness.Region,
PostalCode = merchant.MerchantBusiness.PostalCode
}
},
Funding = new FundingRequest
{
Descriptor = merchant.MerchantFunding.Descriptor,
Destination = FundingDestination.BANK,
Email = merchant.MerchantFunding.Email,
MobilePhone = merchant.MerchantFunding.MobilePhone,
AccountNumber = merchant.MerchantFunding.AccountNumber,
RoutingNumber = merchant.MerchantFunding.RoutingNumber
},
TosAccepted = merchant.TosAccepted,
MasterMerchantAccountId = merchant.MasterMerchantAccountId,
Id = merchant.MerchantId
};
And I need to use AutoMapper to achieve the above.
This is what I've tried:
CreateMapperProfile
EDIT:
CreateMap<Classes.Merchant, MerchantAccountRequest>()
.ForMember(dest => dest.Individual, source => source.MapFrom(s => s.MerchantIndividual))
.ForMember(dest => dest.Business, source => source.MapFrom(s => s.MerchantBusiness))
.ForMember(dest => dest.Funding, source => source.MapFrom(s => s.MerchantFunding))
.ForMember(dest => dest.Id, source => source.MapFrom(s => s.MerchantId));
Here is the Merchant class:
public partial class Merchant :
{
public int Id { get; set; }
public string MerchantId { get; set; }
public virtual MerchantIndividual MerchantIndividual { get; set; }
public virtual MerchantBusiness MerchantBusiness { get; set; }
public virtual MerchantFunding MerchantFunding { get; set; }
public bool TosAccepted { get; set; }
public string MasterMerchantAccountId { get; set; }
public bool isSubMerchant { get; set; }
}
And the mappings;
EDIT:
MerchantAccountRequest request = _mapper.Map<MerchantAccountRequest>(merchant);
request.Individual = _mapper.Map<IndividualRequest>(merchant.MerchantIndividual);
request.Business = _mapper.Map<BusinessRequest>(merchant.MerchantBusiness);
request.Funding = _mapper.Map<FundingRequest>(merchant.MerchantFunding);
Can the first line of code MerchantAccountRequest request = _mapper.Map<MerchantAccountRequest>(merchant); above do all the mapings?
..
..
..
..
How can I create the correct mappings?
You don't need to call _mapper.Map on the properties of the request.
Just call MerchantAccountRequest request = _mapper.Map<MerchantAccountRequest>(merchant); and assuming you have a map for each type you should be fine.
I think something along the lines of the following should get you going down the right path.
class Program
{
static void Main(string[] args)
{
var config = new MapperConfiguration(cfg => {
cfg.CreateMap<Merchant, MerchantAccountRequest>()
.ForMember(dest => dest.Individual, c => c.MapFrom(source => source.MerchantIndividual));
cfg.CreateMap<MerchantIndividual, IndividualRequest>();
});
config.AssertConfigurationIsValid();
var mapper = config.CreateMapper();
var merchant = new Merchant
{
Id = 1,
MerchantIndividual = new MerchantIndividual { FirstName = "John Doe" }
};
var merchantAccountRequest = mapper.Map<Merchant, MerchantAccountRequest>(merchant);
}
}
public class Merchant
{
public int Id { get; set; }
public MerchantIndividual MerchantIndividual { get; set; }
}
public class MerchantIndividual
{
public string FirstName { get; set; }
}
public class MerchantAccountRequest
{
public int Id { get; set; }
public IndividualRequest Individual { get; set; }
}
public class IndividualRequest
{
public string FirstName { get; set; }
}

How to map nested Property with Automapper

I am trying to map Student with StudentDto, this is what I am doing but it is complaining about the nested property which is of type List<StudentContact>
Both the objects, StudentDto and Student have exactly the same properties, this is what i am using to try to map the objects.
var config = new MapperConfiguration(
cfg => cfg.CreateMap<StudentDto, Student>());
var mapper = config.CreateMapper();
var driverActivationResponse = mapper.Map <List<Student> > (studentDto);// "studentDto" is List<StudentDto>
my classes
public class StudentDto
{
public StudentDto()
{
if(StudentContacts==null) StudentContacts=new List<StudentContact>();
}
public string Id { get; set; }
public List<StudentContact> StudentContacts { get; set; }
}
public class Student
{
public Student()
{
if(StudentContacts==null) StudentContacts=new List<StudentContact>();
}
public string Id { get; set; }
public List<StudentContact> StudentContacts { get; set; }
}
public class StudentContact
{
public string ContactName { get; set; }
public string PrimaryContactNo { get; set; }
}
This should help -
AutoMapper.Mapper.CreateMap<Student, StudentDto>()
.ForMember(a => a.StudentContacts, b => b.ResolveUsing(c => c.StudentContacts));
var map = Mapper.Map<StudentDto>(new Student
{
Id = "100",
StudentContacts = new List<StudentContact>
{
new StudentContact{ContactName = "test",PrimaryContactNo = "tset"}
}
});
you cannot map like mapper.Map <List<Student>>(studentDto);. The top level member cannot be a list when using automapper.
Does it help to specify the source collection type and destination collection type in your Map call?
var driverActivationResponse = mapper.Map<List<Student>, List<StudentDto>>(studentDto);
It looks like the AutoMapper code you have is correct. If you're still getting an error, something else must be wrong. Perhaps your studentDto is not really a List<StudentDto>?
In any case, here's an entire example that works without error:
using System;
using System.Collections.Generic;
using System.Linq;
using AutoMapper;
namespace ConsoleSandbox
{
class Program
{
public static void Main()
{
var config = new MapperConfiguration(
cfg => cfg.CreateMap<StudentDto, Student>());
var mapper = config.CreateMapper();
var studentDtos = new[]
{
new StudentDto
{
Id = "1",
StudentContacts = new[]
{
new StudentContact { ContactName = "Dan", PrimaryContactNo = "123" },
new StudentContact { ContactName = "Stan", PrimaryContactNo = "456" },
}.ToList()
},
new StudentDto
{
Id = "2",
StudentContacts = new[]
{
new StudentContact { ContactName = "Foo", PrimaryContactNo = "789" },
new StudentContact { ContactName = "Bar", PrimaryContactNo = "101112" },
}.ToList()
},
}.ToList();
var driverActivationResponse = mapper.Map<List<Student>>(studentDtos);
Console.WriteLine($"Contacts Count: {driverActivationResponse.Count}");
Console.ReadKey();
}
}
public class StudentDto
{
public string Id { get; set; }
public List<StudentContact> StudentContacts { get; set; }
public StudentDto()
{
if (StudentContacts == null) StudentContacts = new List<StudentContact>();
}
}
public class Student
{
public string Id { get; set; }
public List<StudentContact> StudentContacts { get; set; }
public Student()
{
if (StudentContacts == null) StudentContacts = new List<StudentContact>();
}
}
public class StudentContact
{
public string ContactName { get; set; }
public string PrimaryContactNo { get; set; }
}
}

Categories

Resources