0

I'll try explaining what I'm trying to do - Currently I have a model for each table I'm using, and a ViewModel to contain only a part of the table content .

I also add an extra property or two to the ModelView, that comes from another table. My question is, how do I assign the value(I have a function that bring me that property) to the viewModel object ?

The ViewModel :

public class StreetViewModel
{
    public Int64 Id { get; set; }
    public Int64 CityId { get; set; }
    public string Name { get; set; }
    public string CityName { get; set; } // THIS IS THE EXTRA PROPERTY
}

My Controller action to assign the object :

public IActionResult GetStreetByName(string streetName)
{
        var results = _repository.GetStreetByName(streetName); //Return a list of object of type Street
        return Ok(Mapper.Map<IEnumerable<StreetViewModel>>(results)); // convert Street object to STREETVIEWMODEL object and return it.
}

My GetStreetByName method:

public IEnumerable<Street> GetStreetByName(string streetName)
{
    return _context.Street.Where(t => t.Name.Contains(streetName)).ToList();
}

This method will return a list of object type Street for me, in each one I have CityId , and I want to also select the CityName which appears only in the City table.

I also have a method to get city object by an ID:

public City GetCityNameById(int cityId)
{
    return _context.City.Where(t => t.Id == cityId).FirstOrDefault();
}

Where and how can I assign a cityName to each record of Street ?

Martin Lindgren
  • 374
  • 2
  • 14
sagi
  • 40,026
  • 6
  • 59
  • 84

1 Answers1

3

If you are using the AutoMapper, by native convention on this tool you can nagivate on the properties using the property name on the TDestionation, for sample. Let's supose you have these entities:

public class City
{
   public string Name { get; set; }
}

public class Street 
{
   public City City { get; set; }
}

Then, when you define a property on the StreetViewModel called CityName, the AutoMapper will look for a property called CityName on your TSource and it will not be found. Then, it will try to look at City and it will be found and after a property called Name. It is a convetion by autoMapper using the case of the letters on the property name, so, pay attention on it.

AutoMapper - custom mapping

In the other hand, you can configure it on the mapping. For sample:

config.CreateMap<Street, StreetViewModel>()
   .ForMember(t => t.CityName, opt => opt.MapFrom(x => x.City.Name));

Important - SELECT N+1 problem

Remember to fetch the City property when you hit the query on the database using the ORM (Entity Framework or NHibernate) to avoid the lazy loading. If you have many records on the Street result set, it will cause SELECT N+1 problem. With Entity Framework you can use Include and in NHibernate you can use Fetch.

Felipe Oriani
  • 37,948
  • 19
  • 131
  • 194
  • Question - My `GetStreetByName` method is selecting a list of streets. Will the Automapper automatically attach each of them to the correct City by `CityId` ? Or how does this attachment occurs (My DB has relations but my Project not, I just created classes based on my DB and didn't created them one from another) . And also, I didn't understand what you mean by "fetch the city..." , how exactly? I'm fairly new to .net and c# so sorry for the beginner questions – sagi Sep 04 '17 at 13:19
  • Fetch the city, means that when the ORM tool hits the query on the database, it will generate a join to take the data from city in the same query of the street. If you dont do that, it will execute a lazy loading to fetch the city's name. – Felipe Oriani Sep 04 '17 at 13:39
  • Ok , so, how can I use the Entity framework to make this relations ? As I said, I created them in VS from scratch, though they already exists in the DB. So where do I specify the relations ? `city.id -> street.cityId` – sagi Sep 04 '17 at 13:49