How to configure map of List attribute in AutoMapper? - automapper

I'm pretty newbie to AutoMapper. I have four classes:
public class Source
{
public int Id {get;set;}
public List<SourceItem> items {get;set;}
}
public class SourceItem
{
public int Id {get;set;}
public string firstName {get;set;}
public string lastName {get;set;}
}
public class Destination
{
public int Id {get;set;}
public List<SourceItemDestination> items {get;set;}
}
public class SourceItemDestination
{
public int Id {get;set;}
public string firstName {get;set;}
}
Is it possible to create a mapping profile to map Source to Destination allowing to bring the list of SourceItemDestination when performing mapper.Map?

Done! Here are my classes:
using AutoMapper;
using AutoMapper.EquivalencyExpression;
using Infrastructure.Data.Context;
using Microsoft.Extensions.DependencyInjection;
using System;
using WebAPI.AutoMapper;
namespace WebAPI.Configurations
{
public static class AutoMapperConfig
{
public static void AddAutoMapperConfiguration(this IServiceCollection services)
{
if (services == null) throw new ArgumentNullException(nameof(services));
services.AddAutoMapper((serviceProvider, automapper) =>
{
automapper.AddCollectionMappers();
automapper.UseEntityFrameworkCoreModel<CellLabDbContext>(serviceProvider);
}, typeof(MappingProfile).Assembly);
}
}
}
using ApplicationCore.Domain;
using AutoMapper;
namespace WebAPI.AutoMapper
{
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<SourceItem, SourceItemDestination>().ReverseMap();
CreateMap<Source, Destination>().ReverseMap();
}
}
}
Important tip: I had problems when mapping the lists because the property items of Source class were read only:
public class Source
{
public int Id {get;set;}
public List<SourceItem> items {get;}
}
By doing this the mapper does not map the list. However, it does not raise any error or warning.

Related

Assigning null to a foreign key in EntityFramework Code first approach

I want to make a virtual object (Details) to null, my sample Class details below:
public class test
{
public List<stud> students{get;set;}
}
public class stud
{
public int id{get;set;}
public string name{get;set;}
[ForeignKey("Details")]
public int DetailId { get; set; }
[JsonIgnore]
public virtual Details Details{ get; set; }
}
public class Details
{
public string a {get;set;}
public string b{get;set;}
}
test.students= await db.students.where(x=>x.id==12).ToListAsync();
foreach(var a in test.students)
{
a.Details=null;
db.savechanges();//but the Details obj is not being set to null.
}
Can someone please help me to set Details obj as null?

Entity Framework code first relationship using fluent API to string

I have the following classses
public pratial class Address
{
public Guid AddressID{ get; set; }
public AddressType AddressType{ get; set; }
}
public partial class AddressType
{
public string TypeName{ get; set; }
}
In my derived DBContext class I have overridden OnModelCreating
protected override OnModelCreating(DBModelBuilder modelBuilder)
{
modelBuilder.Entity<Address>().HasKey( p => p.AddressID );
modelBuilder.Entity<Address>().Property ( p => p.AddressID)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<Address>().HasRequired( p => p.AddressType);
modelBuilder.Entity<AddrssType>().HasKey( p => p.TypeName );
...
}
This creates fine I fill out a record in the database where
My Tables in the database end up looking like this
Addresses Table
AddressID (PK, uniqueidentified, not null)
AddressType_TypeName(FK, nvarchar(32), not null)
AddressTypes Table
TypeName (PK, uniqueidentifies, not null)
Now I put some data in the tables
AddressTypes Record
TypeName I put in Business
in the Addresses Record
AddressType_TypeName I put in Business
When I run a unit test on this I expect to get back in for my record
List<Address> addresses = context.Addresses.ToList()
Assert.AreEqual(addresses[0].AddressType.TypeName, "Business");
But this fails telling me AddressType is null
How do I set up the relationship between Address and AddressType so that I get back the AddressType that I've hooked up?
To load related entities you must tell it Entity Framework, either by using eager loading:
using System.Data.Entity;
//...
var addresses = context.Addresses.Include(a => a.AddressType).ToList()
...or by lazy loading which is enabled by default if you mark your navigation properties as virtual:
public virtual AddressType AddressType { get; set; }
Eager loading loads the parent and related data together in a single database roundtrip. Lazy loading needs two roundtrips, the second happens under the convers when you access the navigation property in the line addresses[0].AddressType.TypeName.
Edit
Test project to show that lazy loading in this example works (EF 5.0, .NET 4.0, SQL Server Express 2008 R2 as database). I only put virtual in front of AddressType. The rest is identical to your model:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;
namespace EFLazyLoading
{
public partial class Address
{
public Guid AddressID{ get; set; }
public virtual AddressType AddressType{ get; set; }
}
public partial class AddressType
{
public string TypeName{ get; set; }
}
public class MyContext : DbContext
{
public DbSet<Address> Addresses { get; set; }
public DbSet<AddressType> AddressTypes { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Address>().HasKey( p => p.AddressID );
modelBuilder.Entity<Address>().Property ( p => p.AddressID)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<Address>().HasRequired( p => p.AddressType);
modelBuilder.Entity<AddressType>().HasKey( p => p.TypeName );
}
}
class Program
{
static void Main(string[] args)
{
Database.SetInitializer(new DropCreateDatabaseAlways<MyContext>());
using (var ctx = new MyContext())
{
var address = new Address
{
AddressType = new AddressType { TypeName = "Business" }
};
ctx.Addresses.Add(address);
ctx.SaveChanges();
}
using (var ctx = new MyContext())
{
List<Address> addresses = ctx.Addresses.ToList();
string typeName = addresses[0].AddressType.TypeName;
}
}
}
}
The result of typeName in the last line is as expected:

Automapper: Auto map collection property for a dto object

I have a domain object
public class ProductModel
{
public long Id {get;set;}
public string Name {get;set;}
public string SerialNumber {get;set;}
}
Single Dto class:
public class ProductDto
{
public long Id {get;set;}
public string Name {get;set;}
public string SerialNumber {get;set;}
}
Single Dto class that is a list of Dto object:
public class ProductListDto : List<ProductDto>
{
public List<ProductDto> Products;
public ProductListDto()
{
Products = new List<ProductDto>();
}
}
And I'd like to map a list of domain objects to list of Dto objects such that the "Products" property of ProductListDto object AUTOMATICALLY is mapped with a list of ProductModel objects:
ProductListDto dto = new ProductListDto();
Mapper.CreateMap<ProductModel, ProductDto>();
/* dto = (ProductListDto) Mapper.Map<List<ProductModel>, List<ProductDto>>((List<ProductModel>)model); this code line causes error. It is commented out. */
dto.Products = Mapper.Map<List<ProductModel>, List<ProductDto>>((List<ProductModel>)model); // (*) works OK but need to specify "Products" property
The code line (*) works OK, but I'd like to know if there is another way to AUTOMATICALLY (implicitly) map that "Products" property of dto object other than the code line (*)?
That means I do not have to write code like the left hand side of code line (*).
You will need to create a mapping for it. Something like this should work:
namespace StackOverflow
{
using System.Collections.Generic;
using AutoMapper;
public class MyProfile : Profile
{
public override string ProfileName
{
get
{
return "MyProfile";
}
}
protected override void Configure()
{
Mapper.CreateMap<ProductModel, ProductDto>();
Mapper.CreateMap<List<ProductModel>, ProductListDto>()
.ForMember(dest => dest.Products,
opt => opt.MapFrom(
src => Mapper.Map<List<ProductModel>,
List<ProductDto>>(src)));
}
}
}
Then in your code you can do:
dto = Mapper.Map<List<ProductModel>, ProductListDto>((List<ProductModel>)model);
Here are a couple of unit tests to show how it works:
namespace StackOverflow
{
using System.Collections.Generic;
using AutoMapper;
using NUnit.Framework;
[TestFixture]
public class MappingTests
{
[Test]
public void AutoMapper_Configuration_IsValid()
{
Mapper.Initialize(m => m.AddProfile<MyProfile>());
Mapper.AssertConfigurationIsValid();
}
[Test]
public void AutoMapper_DriverMapping_IsValid()
{
Mapper.Initialize(m => m.AddProfile<MyProfile>());
Mapper.AssertConfigurationIsValid();
var products = new List<ProductModel>
{
new ProductModel
{
Id = 1,
Name = "StackOverflow Rocks",
SerialNumber = "1234"
},
new ProductModel
{
Id = 2,
Name = "I Also Rock",
SerialNumber = "4321"
}
};
var productsDto =
Mapper.Map<List<ProductModel>, ProductListDto>(products);
Assert.That(productsDto, Is.Not.Null);
Assert.That(productsDto.Products, Is.Not.Null);
Assert.That(productsDto.Products.Count, Is.EqualTo(2));
}
}
}

Flat object to nested objects in Automapper

I use Automapper all the time, and for some reason the issue is escaping me. Below is an example of the code I'm using (names have been changed.)
Problem
All objects are mapped but the Keys object.
Code
I have a flat object coming from a WCF service.
public class FlatDTO
{
public string Key1 {get;set;}
public string Key2 {get;set;}
public string Name {get;set;}
public DateTime Date {get;set;}
}
This is the structure for my Business Object:
public class BaseModel
{
public Datetime Date{get;set;}
}
public class Keys
{
public string Key1 {get;set;}
public string Key2 {get;set;}
}
public class Summary : BaseModel
{
public Keys List {get;set;}
public string Name{get;set;}
public Summary ()
{
List = new Keys();
}
}
Below is my profile for Automapper (the init of the profile is done in the global.aspx page)
internal class MapDTO : Profile
{
protected override void configure()
{
Mapper.CreateMap<FlatDTO,BaseModel>().IgnoreAllNonExisting();
Mapper.CreateMap<FlatDTO,Role>().IgnoreAllNonExisting();
Mapper.CreateMap<FlatDTO,Summary>().IgnoreAllNonExisting();
}
Help with this 'simple' issue would be great.

AutoMapper Custom Collections

I am getting an AutoMapper Exception when i try to Map my DomainModel to ViewModel,
Here is my Domain Model Class,
public class Source{
public string Name {get;set;}
..............
public CustomCollection EmpDetails {get;set;}
}
public class CustomCollection:CollectionBase,IEnumerable<ClassA> {
public CustomCollection{}
public int Add(ClassA classA)
{
return List.Add(classA);
}
..........
}
public class ClassA{
public string EmpNumber {get;set;}
public string EmpName {get;set;}
public string Designation {get;set;}
}
Here is my ViewModel
public class SourceDTO{
public string Name {get;set;}
public IList<EmpDTO> EmpDetails {get;set;}
}
public class EmpDTO{
public string EmpNumber {get;set;}
public string EmpName {get;set;}
public string Designation {get;set;}
}
Here is my AutoMapper Code,
AutoMapper.Mapper.CreateMap<Source, SourceDTO>()
.ForMember(x => x.EmpDetails, y => y.MapFrom(src => src.EmpDetails));
I am getting error here, It doesn't say any detail inner exceptions,
I hope someone can help us out.
You need to include a mapping to go from ClassA to EmpDTO:
Mapper.CreateMap<ClassA, EmpDTO>();
And you should be all set.

Resources