Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to work with DTOs in Controller of WebAPI?

I'm a bit confused how to use DTOs in my WebAPI controllers. I'm using the Database first concept of Entity Framework. The following Entity Data Model was generated:

//Generated class by EDM:
public partial class Address
    {
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
        public Address()
        {
            this.Member = new HashSet<Member>();
        }

        public int Id { get; set; }
        public string Street { get; set; }
        public Nullable<short> Street_Number { get; set; }
        public Nullable<decimal> Zip { get; set; }
        public string City { get; set; }
        public string Country { get; set; }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
        public virtual ICollection<Member> Member { get; set; }
    }

To use Data Annotations I need to define a AddressDTO because every time when I need to modify the EDM the data model will generated again and the Data Annotations are lost.

So next, I defined the AddressDTO:

public class AddressDTO
    {
        public int Id { get; set; }
        [Required]
        [StringLength(100,ErrorMessage="The max. length of the street name is 100 characters.")]
        public string Street { get; set; }
        public Nullable<short> Street_Number { get; set; }
        [Required]
        public Nullable<decimal> Zip { get; set; }
        [Required]
        [RegularExpression(@"[a-z,A-Z]",ErrorMessage="Only characters are allowed.")]
        public string City { get; set; }
        [Required]
        public string Country { get; set; }
    }

And the controller looks like the code below:

    [RoutePrefix("api/address")]
    public class AddressController : ApiController
    {
        private PersonEntities db = new PersonEntities();

        // GET: api/Address
        [HttpGet]
        [ResponseType(typeof(AddressDTO))]
        public async Task<IHttpActionResult> GetAll()
        {
            var addressList = await db.Address.ToListAsync();

            return Ok(addressList);
        }
     }

When I'm starting the RestAPI to display the result in browser, I always get the following json result:

[
    {
        "Member": [ ],
        "Id": 1,
        "Street": "Example Street",
        "Street_Number": 1,
        "Zip": 12345.0,
        "City": "New York",
        "Country": "USA" 
    },...
]

But I need the following desired result:

[
        {
            "Street": "Example Street",
            "Street_Number": 1,
            "Zip": 12345.0,
            "City": "New York",
            "Country": "USA" 
        },...
    ]

Has anyone an idea how could I resolve that?

like image 850
yuro Avatar asked Nov 12 '16 11:11

yuro


2 Answers

Your new edit simply means you don't need Id and Member properties in your AddressDTO model. And yo should shape the result to be a List<AddressDTO> while you are returning List<Address>.

So just remove unused properties from AddressDTO and then shape the result by selecting new AddressDTO and setting properties:

var result = db.Address.Select(x=> new AddressDTO()
                                   { 
                                       Street = x.Id, 
                                       Street_Number = x.Street_Number,
                                       City = x.City,
                                       Country = x.Country
                                   }).ToListAsync();

If you want to make the linq shorter, you can add such constructor to AddressDTO:

public AddressDTO(Address x)
{
    Street = x.Id;
    Street_Number = x.Street_Number;
    City = x.City;
    Country = x.Country;
}

Then in controller:

var result = db.Address.Select(x=> new AddressDTO(x)).ToListAsync();

Also in a large project, you can rely on some object mappers like auto mapper as mentioned in an answer.

like image 75
Reza Aghaei Avatar answered Oct 09 '22 22:10

Reza Aghaei


Use a library like Automapper to handle your needs.

Then you just need to modify the following line to this for example:

var addressList = await Mapper.Map<IList<AddressDto>>(db.Address.ToListAsync());

This will convert your Address list to an AddressDto list where you can control what gets converted or not so not to pass Members for example to your view. And Automapper will automatically know what to convert if the field names are the same.

like image 7
PmanAce Avatar answered Oct 09 '22 23:10

PmanAce