简体   繁体   中英

Mapping two models with AutoMapper

I have a Customer model which has a one-to-one relationship with an Address model. I'm using AutoMapper to map these values. I want to create a new Customer in the db using my API, which ideally will insert a new address row based on the information provided to the customer model.

This is the SQL Exception I am receiving by Entity framework core: ---> Microsoft.Data.SqlClient.SqlException (0x80131904): Cannot insert the value NULL into column 'AddressId', table 'dbo.Customers'; column does not allow nulls. INSERT fails. ---> Microsoft.Data.SqlClient.SqlException (0x80131904): Cannot insert the value NULL into column 'AddressId', table 'dbo.Customers'; column does not allow nulls. INSERT fails.

Note - BaseEntity is just including common properties like the ID, Concurrency check etc

Customers Model:

public class Customer : BaseEntity
{
   public string FirstName { get; set; }
   public string LastName { get; set; }
   public string Phone { get; set; }
   public string Email { get; set; }
   public string UpdatedBy { get; set; }
   public DateTime? LastContact { get; set; }

   #region Navigation Properties
   public int? AddressId { get; set; }
   public string UserId { get; set; }
   public User User { get; set; }
   public virtual Address Address { get; set; }
   public virtual ICollection<Invoice> Invoices { get; set; }
   #endregion
}

My Address model:

public class Address : BaseEntity
{
   public string AddressLine1 { get; set; }
   public string AddressLine2 { get; set; }
   public string City { get; set; }
   public string State { get; set; }
   public string PostalCode { get; set; }

   #region Navigation Properties
   public virtual Customer Customer { get; set; }
   #endregion
}

I am using a DTO class for the request, as follows:

public class CustomerCreateDto
{
   public string FirstName { get; set; }
   public string LastName { get; set; }
   public string Phone { get; set; }
   public string Email { get; set; }
   public string AddressLine1 { get; set; }
   public string AddressLine2 { get; set; }
   public string City { get; set; }
   public string State { get; set; }
   public string PostalCode { get; set; }
   public string UserId { get; set; }
}

AddressDto:

public class AddressDto : BaseEntity
{
   [Required]
   public string AddressLine1 { get; set; }
   public string AddressLine2 { get; set; }
   [Required]
   public string City { get; set; }
   public string State { get; set; }
   [Required]
   [DataType(DataType.PostalCode)]
   public string PostalCode { get; set; }
}

CustomerDto

public class CustomerDto : BaseEntity
{
   public string FirstName { get; set; }
   public string LastName { get; set; }
   public string Phone { get; set; }
   public string Email { get; set; }
   public string UserId { get; set; }
   public int AddressId { get; set; }
   public string UpdatedBy { get; set; }
   public DateTime? LastContact { get; set; }
   public virtual UserDto User { get; set; }
   public virtual AddressDto Address { get; set; }
   public virtual ICollection<InvoiceDto> Invoices { get; set; }
}

Here is the AutoMapper configuration

public class MappingConfiguration : Profile
{
   public MappingConfiguration()
   {
      CreateMap<AddressDto, Address>().ReverseMap();
      CreateMap<CustomerDto, Customer>().ForMember(dst => dst.UserId,
       opt => opt.MapFrom(src => src.User.Id)).ForMember(dst => 
       dst.Address, 
       opt => opt.MapFrom(src => src.Address)).ReverseMap();
      CreateMap<CustomerCreateDto, Customer>().ReverseMap();
    }
}

And here is the Controller method. I know that I can do it by manually setting the value with the addressDto object like customer.Address = address but I want to see if there is a better way

[HttpPost]
public async Task < IActionResult > Create([FromBody] CustomerCreateDto customerDto) 
{
   if (customerDto == null || !ModelState.IsValid) {
        return BadRequest(ModelState);
   }

   var addressDto = new AddressDto {
        AddressLine1 = customerDto.AddressLine1,
        AddressLine2 = customerDto.AddressLine2,
        City = customerDto.City,
        State = customerDto.State,
        PostalCode = customerDto.PostalCode,
        CreatedDate = DateTime.Now,
    };

    var user = await _userManager.FindByIdAsync(customerDto.UserId);
    var address = _mapper.Map<Address>(addressDto);
    var customer = _mapper.Map<Customer>(customerDto);
    
    customer.CreatedDate = DateTime.Now;

     var result = await _customerRepository.CreateAsync(customer);
     if (result) return Created("Created", new { customer });
     // if we got this far it means the request failed
     return new 
       StatusCodeResult(StatusCodes.Status500InternalServerError);
}

The functionality I was looking for was achieved by changing the CustomerCreateDto to:

public class CustomerCreateDto
{
   public string FirstName { get; set; }
   public string LastName { get; set; }
   public string Phone { get; set; }
   public string Email { get; set; }
   public string UserId { get; set; }
   public virtual AddressDto Address { get; set; }
}

Mapping Configurations:

public class MappingConfiguration : Profile
{
   public MappingConfiguration()
   {
      CreateMap<AddressDto, Address>().ReverseMap();
      CreateMap<CustomerDto, Customer>().ForMember(dst => dst.UserId,
        opt => opt.MapFrom(src => src.User.Id));
      CreateMap<CustomerCreateDto, Customer>().ForMember(dst => dst.Address,
        opt => opt.MapFrom(src => src.Address)).ReverseMap().ReverseMap();
    }
}

My controller:

[HttpPost]
public async Task<IActionResult> Create([FromBody] CustomerCreateDto customerDto) 
{
   if (customerDto == null || !ModelState.IsValid) {
        return BadRequest(ModelState);
   }
   var customer = _mapper.Map<Customer>(customerDto);
    
   customer.CreatedDate = DateTime.Now;

   var result = await _customerRepository.CreateAsync(customer);
   if (result) return Created("Created", new { customer });

   return new StatusCodeResult(StatusCodes.Status500InternalServerError);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM