C# Automapper IQueryable - LINQ 2 SQLite - Query returns only parent, nested child always null

Viewed 351

Hi people of the internet,

I've been searching on SO for a couple of days now, tried loads of possible solutions but I'm not able to solve my issue in the following test-project. Please excuse my lack of skill as I'm a self-taught programmer for just over a year now and I'm this is my first question on SO.

The situation: I've got 2 entity classes Car and Wheel and want to store and query them from a single db table using c#'s linq to sql(ite).

    public class Car
    {
        public Int64? Id { get; private set; }
        public string Manufacturer { get; private set; } = "Porsche";
        public double Mileage { get; private set; }
        public Wheel Wheel { get; private set; }

        public Car()
        {
            this.Id = Program.CarCount;
            Mileage = new System.Random(Program.CarCount).Next(0, 500000);

            Program.CarCount++;
        }

        public Car CreateWheels()
        {
           // Adds Wheel 2 Car
        }
    }

    public class Wheel
    {
        public Int64? Id { get; set; }
        public string Manufacturer { get; set; } = "Continental";

        public Wheel() { Id = Program.WheelCount; }
    }

Unfortunately as it is a test project for a different project I'm working on I'm not able to split up the raw-data into multiple OR-tables.

To work with a single table instead I flatten them into a single CarDto class that matches my single db table. I flatten both classes into the CarDto to be used with LINQ 2 SQLite using AutoMap and assigning the individual db columns.

    [Table(Name = "Cars")]
    [AutoMap(typeof(Car), ReverseMap = true)]
    public class CarDto
    {
        public CarDto(){ }

        [Column(IsPrimaryKey = true)]
        public Int64? Id { get; set; }
        [Column]
        public string Manufacturer { get; set; }
        [Column]
        public double Mileage { get; set; }
        [Column]
        public Int64? WheelId { get; set; }
        [Column]
        public string WheelManufacturer { get; set; }
    }

Mapping between DTO-class and entity-class is done with a mapper CarMapper using AutoMapper v.9x

    public class CarMapper
    {
        public IMapper Map => Config.CreateMapper();
        IConfigurationProvider Config;

        public CarMapper()
        {
            Config = new MapperConfiguration(cfg =>
            {
                cfg.AddMaps(typeof(CarMapper).Assembly);
            });
            Config.AssertConfigurationIsValid();
        }
    }

Interaction with the db is handled via a repository class CarRepocontaining the mapper and the DataContext class of C# Linq2SQL

    public class CarContext : DataContext
    {
        public Table<CarDto> CarDtos;
        public CarContext(IDbConnection connection) : base(connection) { }
    }

    public class CarRepo
    {
        private string conn;
        CarMapper mp = new CarMapper();
        CarContext CarContext => new CarContext(new SQLiteConnection(conn));
        public IQueryable<Car> Cars { get => Qry(); }

        public CarRepo(string connectionString) { this.conn = connectionString; }

        private IQueryable<Car> Qry()
        {
            return mp.Map.ProjectTo<Car>(CarContext.CarDtos);
        }

        public List<Car> GetAllCarsFromDb()
        {
            var dtosFromDb = new List<CarDto>();
            using (var db = CarContext)
                return mp.Map.Map<List<CarDto>, List<Car>>(db.CarDtos.ToList());
        }

        public void InsertCars(List<Car> cars)
        {
            using (var db = CarContext)
            {
                db.CarDtos.InsertAllOnSubmit(mp.Map.Map<List<Car>, List<CarDto>>(cars));
                db.SubmitChanges();
            }
        }
    }

The problem:

Insertion and mapping the CarDto-table to Car works flawlessly. Running repo.Cars.Where(car => car.Id <= 5).ToList(); results as expected except for the nested object Wheel, which always returns as null. The problem exists when projecting a car-query and probably lies in the mapping of the query expression. Looking at the resulting query this seems obvious.

SELECT[t0].[Id], [t0].[Manufacturer], [t0].[Mileage]
FROM[Cars] AS[t0]
WHERE[t0].[Id] <= @p0

Running the following mapping mp.Map.Map<List<CarDto>, List<Car>>(db.CarDtos.ToList()); works and delivers Car including Wheel instances.

I tried loads of solutions (custom mappings,... ) and went through the AutoMapper documentation but wasn't able to fix my problem. Hope someone can help me.

Cheers from germany! Henrik

EDIT: P.s. I added a gist https://gitlab.com/snippets/1957648 https://gist.github.com/henrikherr/29eb2913d403ab1d6bede52ed011869a

1 Answers

As @LucianBargaoanu pointed out the ProjectTo doesn't work with ForPath. He also correctly pointed out that the logic/usage of my dto and entities is reversed.

In addition I wouldn't have to do the iqueryable-mapping if my db-design would match my Carand Wheel classes in a more class/object-related way or if my repo would expose and handle the dto class.

You're relying implicitly on ForPath here and that doesn't work with ProjectTo. But you entities/dtos are exactly reversed. Car should be in the DB, with a Wheel FK entity. And CarDto would show up in the UI, or whatever uses the DB. – Lucian Bargaoanu

Related