0
\$\begingroup\$

I am not sure of the correct way to do this.

I want to separate my Entity database model from the model I use throughout my application, as it will have fields in it that I may not want added to the database.

This means, after any Entity call I am doing mapping.

I have come up with this solution rather than use automapper or have mapping calls everywhere in my program (breaking the DRY principal).

My Entity DdSet Model:

public class PeopleTable {

    [Key]
    public string Id { get; set; }

    public string Name { get; set; }

    public string Age { get; set; }
}

The Model I would use throughout the application. It has some additional fields and a MapList() mapping function.

public class People : PeopleTable {

    public string Job { get; set; }
    public string Hobby { get; set; }


    public List<People> MapList(List<PeopleTable> input)
    {
        List<People> list = new List<People>();

        foreach (People entry in input)
        {
            People person = new People();
            person.Id = entry.Id;
            person.Name = entry.Name;
            person.Age = entry.Age;

            list.Add(person);
        }

        return list;
    }
}

Getting the data

List<PeopleTable> peopleTableList = await _context.PeopleTable.Select(s => s).ToListAsync();

List<People> peopleList = new People().MapList(peopleTableList);

foreach (person in peopleList){
    Console.WriteLine($"{person.Name} is {person.Age} years old");
}

Is this ok? What is best practice? I am thinking there must be something better using the class constructor instead?

Or does everyone just use automapper? I'd rather not have another dependency.

\$\endgroup\$
3
  • \$\begingroup\$ So much here is wrong: "people" is a plural (the word you're looking for is "person"), names shouldn't contain their type (e.g. "peopleList"), calling something "xxxxTable" because it maps to a table is bad,... I don't understand why Job and Hobby only exist on the model. The MapList method can be done shorter and better (with LINQ); and why is in in that class? \$\endgroup\$
    – BCdotWEB
    Sep 24 at 16:41
  • \$\begingroup\$ Please read codereview.stackexchange.com/help/how-to-ask ; the title of your question doesn't follow this site's rules. \$\endgroup\$
    – BCdotWEB
    Sep 24 at 16:42
  • \$\begingroup\$ People table contains many persons. Sorry for the simple example, it was more the structure of the mapping function I was interested in. \$\endgroup\$ Sep 25 at 6:34

1 Answer 1

1
\$\begingroup\$

Mapping objects is a subject in it's own. It is not how do you do it, but rather, which way is it going to be suitable to your overall project.

There are a lot of mapping NuGets out there such as AutoMapper and Mapperly, but if you don't want to add extra dependencies to your project, then I would suggest using extension methods instead.

Using extension methods would separate your mapping from your actual objects, and keep everything in one place, not to mention the maintainability of this approach.

Here is how things would go :

// define a DTO model (Data Transfer Object)
public sealed class PeopleDto 
{
    public string Id { get; set; }
    public string Name { get; set; }
    public string Age { get; set; }
    public string Job { get; set; }
    public string Hobby { get; set; }
}


public static class PeopleTableExtensions
{
    
    public static PeopleDto ToPeopleDto(this PeopleTable source) {
        
        if(source is null) return null; 
        
        return new PeopleDto {
            Id = source.Id,
            Name = source.Name, 
            Age = source.Age
        };              
        
    }
    
    public static IEnumerable<PeopleDto> ToPeopleDto(this IEnumerable<PeopleTable> source)
    {
        if(source is null) yield break; 

        foreach(var item in source) {
            
            if(item is null) continue;
            
            yield return source.ToPeopleDto();
        }
    }
    
    public static PeopleTable ToPeopleTable(this PeopleDto source) {
        
        if(source is null) return null; 
        
        return new PeopleTable {
            Id = source.Id,
            Name = source.Name, 
            Age = source.Age
        };              
    }
    
    public static IEnumerable<PeopleTable> ToPeopleTable(this IEnumerable<PeopleDto> source)
    {
        if(source is null) yield break; 

        foreach(var item in source) {
            
            if(item is null) continue;
            
            yield return source.ToPeopleTable();
        }
    }
}

Usage :

var peopleTableList = await _context.PeopleTable.Select(s => s).ToListAsync();

var peopleDotList = peopleTableList.ToPeopleDto();

foreach (person in peopleDotList){
    Console.WriteLine($"{person.Name} is {person.Age} years old");
}
\$\endgroup\$
2
  • \$\begingroup\$ Thanks! This is more like it! So I can just add 'using PeopleTableExtensions' and then use that extension method when needed? Sounds good! \$\endgroup\$ Sep 29 at 6:46
  • \$\begingroup\$ @ScottishSmile that's something manageable based on your needs. but yes, you can define them under any namespace you think would be best fit. \$\endgroup\$
    – iSR5
    Sep 29 at 8:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.