-
Notifications
You must be signed in to change notification settings - Fork 305
Open
idhase/NPoco
#1Labels
Description
When querying using NPoco.Linq Include() to pull in referenced data NPoco seems to ignore properties in the referenced objects that are mapped using Complex mapping. The type of reference, OneToOne or Foreign, does not seem to matter.
Tested on SQL Server 2019 using .NET Core 5.0 and Npoco version 5.3.1.
To reproduce create a .NET Core Console application and replace Program.cs with the following code:
using System;
using System.Linq;
using System.Reflection;
using Microsoft.Data.SqlClient;
using NPoco;
using NPoco.FluentMappings;
using NPoco.Linq;
namespace NPocoIncludeTest
{
class Program
{
static void Main(string[] args)
{
var connectionString = "<ConnectionStringToNpocoDatabaseGoesHere>";
var maps = Assembly.GetAssembly(typeof(CarMap)).GetTypes().Where(t => typeof(IMap).IsAssignableFrom(t))
.Select(t => Activator.CreateInstance(t) as IMap).ToArray();
var factory = DatabaseFactory.Config(x =>
{
x.UsingDatabase(() =>
new Database(connectionString, DatabaseType.SqlServer2012, SqlClientFactory.Instance));
x.WithFluentConfig(FluentMappingConfiguration.Configure(maps));
});
// Insert test data...
using (var db = factory.GetDatabase())
{
Car car = null;
Engine engine = null;
Chassis chassis = null;
var carId = new Guid("EB9CF396-1EA7-4125-8DF2-FC47B32985BC");
var engineId = new Guid("16708CC6-9919-4928-B22C-923B2DF3CD69");
var chassisId = new Guid("1AADE8B2-3736-40DE-ADA1-9BC2323D8C6B");
engine = new Engine
{
Id = engineId,
Cylinders = 12,
Identity = new Identity
{
Manufacturer = "Lamborghini",
Serial = "1234567890"
}
};
db.Insert(engine);
car = new Car
{
Id = carId,
Make = "Skodaghini",
Engine = engine
};
db.Insert(car);
chassis = new Chassis
{
Id = chassisId,
CarId = carId,
Wheels = 4,
Identity = new Identity()
{
Manufacturer = "Skoda",
Serial = "9876543210"
}
};
db.Insert(chassis);
// Fetch data...
Console.WriteLine("Fetch Car using 'Include'");
Console.WriteLine("--------------------------------------------");
car = db.Query<Car>()
.Include(c => c.Chassis, JoinType.Inner)
.Include(c => c.Engine, JoinType.Inner)
.SingleOrDefault(c => c.Id == carId);
PrintNullStatus("Car.Engine", car.Engine.Identity);
PrintNullStatus("Car.Chassis", car.Chassis.Identity);
Console.WriteLine("\nFetch Engine and Chassis separately");
Console.WriteLine("--------------------------------------------");
engine = db.Query<Engine>().SingleOrDefault(e => e.Id == engineId);
PrintNullStatus("Engine", engine.Identity);
chassis = db.Query<Chassis>().SingleOrDefault(c => c.Id == chassisId);
PrintNullStatus("Chassis", chassis.Identity);
Console.WriteLine("\nFetch Car using SQL statement");
Console.WriteLine("--------------------------------------------");
car = db.Query<Car>(
"Select c.*, e.*, ch.* from Cars c JOIN Engines e ON e.Id = c.EngineId JOIN Chassis ch ON ch.CarId = c.Id WHERE c.Id = @0",
carId).SingleOrDefault();
PrintNullStatus("Car.Engine", car.Engine.Identity);
PrintNullStatus("Car.Chassis", car.Chassis.Identity);
Console.WriteLine("\nPress ENTER to exit");
Console.ReadLine();
}
}
private static void PrintNullStatus(string owner, Identity i)
{
Console.WriteLine($"{owner}.Identity is{(i != null ? " not" : "")} null!");
}
public class Car
{
public Guid Id { get; set; }
public string Make { get; set; }
public Engine Engine { get; set; }
public Chassis Chassis { get; set; }
}
public class Engine
{
public Guid Id { get; set; }
public int Cylinders { get; set; }
public Identity Identity { get; set; }
}
public class Chassis
{
public Guid Id { get; set; }
public int Wheels { get; set; }
public Guid CarId { get; set; }
public Identity Identity { get; set; }
}
public class Identity
{
public string Serial { get; set; }
public string Manufacturer { get; set; }
}
public class CarMap : Map<Car>
{
public CarMap()
{
TableName("Cars").PrimaryKey(x => x.Id, autoIncrement: false);
Columns(x =>
{
x.Column(y => y.Make);
x.Column(y => y.Engine).Reference(engine => engine.Id, ReferenceType.Foreign).WithName("EngineId");
x.Column(y => y.Chassis).Reference(chassis => chassis.CarId, ReferenceType.OneToOne).WithName("Id");
});
}
}
public class EngineMap : Map<Engine>
{
public EngineMap()
{
TableName("Engines").PrimaryKey(x => x.Id, false);
Columns(x =>
{
x.Column(y => y.Id);
x.Column(y => y.Cylinders);
x.Column(y => y.Identity).ComplexMapping("Identity");
});
}
}
public class ChassisMap : Map<Chassis>
{
public ChassisMap()
{
TableName("Chassis").PrimaryKey(x => x.Id, false);
Columns(x =>
{
x.Column(y => y.Id);
x.Column(y => y.Wheels);
x.Column(y => y.Identity).ComplexMapping("Identity");
});
}
}
}
}Create a SQL Server database and run the following sql script to create the tables
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Cars](
[Id] [uniqueidentifier] NOT NULL,
[Make] [nvarchar](50) NOT NULL,
[EngineId] [uniqueidentifier] NOT NULL,
CONSTRAINT [PK_Cars] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Chassis](
[Id] [uniqueidentifier] NOT NULL,
[CarId] [uniqueidentifier] NOT NULL,
[Wheels] [tinyint] NOT NULL,
[Identity__Serial] [nvarchar](50) NOT NULL,
[Identity__Manufacturer] [nvarchar](50) NOT NULL,
CONSTRAINT [PK_Chassis] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Engines](
[Id] [uniqueidentifier] NOT NULL,
[Cylinders] [tinyint] NOT NULL,
[Identity__Serial] [nvarchar](50) NOT NULL,
[Identity__Manufacturer] [nvarchar](50) NOT NULL,
CONSTRAINT [PK_Engines] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Cars] WITH CHECK ADD CONSTRAINT [FK_Cars_Engines] FOREIGN KEY([EngineId])
REFERENCES [dbo].[Engines] ([Id])
GO
ALTER TABLE [dbo].[Cars] CHECK CONSTRAINT [FK_Cars_Engines]
GO
ALTER TABLE [dbo].[Chassis] WITH CHECK ADD CONSTRAINT [FK_Chassis_Cars] FOREIGN KEY([CarId])
REFERENCES [dbo].[Cars] ([Id])
GO
ALTER TABLE [dbo].[Chassis] CHECK CONSTRAINT [FK_Chassis_Cars]
GO
The output when running this application is:
Fetch Car using 'Include'
--------------------------------------------
Car.Engine.Identity is null!
Car.Chassis.Identity is null!
Fetch Engine and Chassis separately
--------------------------------------------
Engine.Identity is not null!
Chassis.Identity is not null!
Fetch Car using SQL statement
--------------------------------------------
Car.Engine.Identity is not null!
Car.Chassis.Identity is not null!
Press ENTER to exit