-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial commit with repro case for nhibernate/nhibernate-core#1277
- Loading branch information
0 parents
commit 1fd5e72
Showing
11 changed files
with
332 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
.vs/ | ||
bin/ | ||
obj/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
|
||
Microsoft Visual Studio Solution File, Format Version 12.00 | ||
# Visual Studio 15 | ||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NHibernate.Tests", "NHibernate.Tests\NHibernate.Tests.csproj", "{2244649E-15A9-4A35-8B26-8A174F7CC50A}" | ||
EndProject | ||
Global | ||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||
Debug|Any CPU = Debug|Any CPU | ||
Release|Any CPU = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
{2244649E-15A9-4A35-8B26-8A174F7CC50A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
{2244649E-15A9-4A35-8B26-8A174F7CC50A}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
{2244649E-15A9-4A35-8B26-8A174F7CC50A}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
{2244649E-15A9-4A35-8B26-8A174F7CC50A}.Release|Any CPU.Build.0 = Release|Any CPU | ||
EndGlobalSection | ||
EndGlobal |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
BEGIN TRANSACTION; | ||
|
||
-- Schema | ||
CREATE TABLE Person ( | ||
PersonId INT PRIMARY KEY, | ||
Name VARCHAR | ||
); | ||
CREATE TABLE Address ( | ||
AddressId INT PRIMARY KEY, | ||
AddressPersonId INT NOT NULL, | ||
City VARCHAR, | ||
CONSTRAINT FK_ADDRESS_HAS_PERSON FOREIGN KEY (AddressPersonId) REFERENCES Person | ||
); | ||
CREATE TABLE PayrollInfo ( | ||
PayrollInfoId INT PRIMARY KEY, | ||
PayrollInfoPersonId INT NOT NULL, | ||
PayrollNumber INT, | ||
CONSTRAINT FK_PAYROLLINFO_HAS_PERSON FOREIGN KEY (PayrollInfoId) REFERENCES Person | ||
); | ||
|
||
COMMIT; | ||
|
||
BEGIN TRANSACTION; | ||
|
||
-- Data | ||
INSERT INTO Person(PersonId, Name) | ||
VALUES(1, 'Jane Doe'); | ||
INSERT INTO Address(AddressPersonId, City) | ||
VALUES(1, 'Nowhere'); | ||
INSERT INTO PayrollInfo(PayrollInfoPersonId, PayrollNumber) | ||
VALUES(1, 1234); | ||
|
||
COMMIT; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
using System; | ||
using System.Data; | ||
using System.Data.Common; | ||
using System.IO; | ||
using System.Reflection; | ||
|
||
namespace NHibernate.Tests | ||
{ | ||
/// <summary> | ||
/// Initializer class executes the script <c>CreateDb.sql</c> on the specified connection. | ||
/// </summary> | ||
public class DbInitializer | ||
{ | ||
public void InitializeDatabase(DbConnection connection) | ||
{ | ||
if (connection == null) | ||
throw new ArgumentNullException(nameof(connection)); | ||
|
||
if (connection.State != ConnectionState.Open) | ||
connection.Open(); | ||
|
||
var initScript = GetInitScript(); | ||
ExecuteScript(connection, initScript); | ||
} | ||
|
||
string GetInitScript() | ||
{ | ||
using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("NHibernate.Tests.CreateDb.sql")) | ||
using(var reader = new StreamReader(stream)) | ||
{ | ||
return reader.ReadToEnd(); | ||
} | ||
} | ||
|
||
void ExecuteScript(DbConnection connection, string script) | ||
{ | ||
using (var command = connection.CreateCommand()) | ||
{ | ||
command.CommandText = script; | ||
command.ExecuteNonQuery(); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
using System; | ||
using NUnit.Framework; | ||
using System.Linq; | ||
|
||
namespace NHibernate.Tests | ||
{ | ||
[TestFixture] | ||
public class MappingWithTwoJoinsTests | ||
{ | ||
[Test] | ||
public void NHibernate_should_not_throw_when_querying_a_Person_mapped_using_MbC() | ||
{ | ||
var sessionFactoryCreator = new SessionFactoryCreator(); | ||
|
||
using (var sessionFactory = sessionFactoryCreator.GetSessionFactoryWithMbcMappings()) | ||
using(var session = sessionFactory.OpenSession()) | ||
{ | ||
var initializer = new DbInitializer(); | ||
initializer.InitializeDatabase(session.Connection); | ||
|
||
Assert.That(() => session.Query<Person>().FirstOrDefault(x => x.Name == "Jane Doe"), Throws.Nothing); | ||
} | ||
} | ||
|
||
[Test] | ||
public void NHibernate_should_not_throw_when_querying_a_Person_mapped_using_XML() | ||
{ | ||
var sessionFactoryCreator = new SessionFactoryCreator(); | ||
|
||
using (var sessionFactory = sessionFactoryCreator.GetSessionFactoryWithXmlMappings()) | ||
using (var session = sessionFactory.OpenSession()) | ||
{ | ||
var initializer = new DbInitializer(); | ||
initializer.InitializeDatabase(session.Connection); | ||
|
||
Assert.That(() => session.Query<Person>().FirstOrDefault(x => x.Name == "Jane Doe"), Throws.Nothing); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>netcoreapp2.2</TargetFramework> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="NHibernate" Version="5.3.2" /> | ||
<PackageReference Include="NUnit" Version="3.12.0" /> | ||
<PackageReference Include="NUnit3TestAdapter" Version="3.16.1" /> | ||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" /> | ||
<PackageReference Include="System.Data.SQLite.Core" Version="1.0.113.1" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<None Remove="Mappings\PersonMap.hbm.xml" /> | ||
<None Remove="CreateDb.sql" /> | ||
<None Remove="PersonMap.hbm.xml" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<EmbeddedResource Include="CreateDb.sql" /> | ||
<EmbeddedResource Include="PersonMap.hbm.xml" /> | ||
</ItemGroup> | ||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
using System; | ||
namespace NHibernate.Tests | ||
{ | ||
public class Person | ||
{ | ||
public virtual int PersonId { get; set; } | ||
|
||
// Comes from the Person table | ||
public virtual string Name { get; set; } | ||
|
||
// Comes from the Address table | ||
public virtual string City { get; set; } | ||
|
||
// Comes from the PayrollInfo table | ||
public virtual int PayrollNumber { get; set; } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
using NHibernate.Mapping.ByCode.Conformist; | ||
|
||
namespace NHibernate.Tests | ||
{ | ||
public class PersonMap : ClassMapping<Person> | ||
{ | ||
// Note that the XML mapping in the same directory as this is equivalent. | ||
// Only one of the two mappings should be used at a time. | ||
|
||
public PersonMap() | ||
{ | ||
Id(x => x.PersonId); | ||
|
||
Property(x => x.Name); | ||
|
||
Join("PayrollInfo", j => | ||
{ | ||
j.Key(k => k.Column("PayrollInfoPersonId")); | ||
j.Property(x => x.PayrollNumber); | ||
}); | ||
|
||
Join("Address", j => | ||
{ | ||
j.Key(k => k.Column("AddressPersonId")); | ||
j.Property(x => x.City); | ||
}); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" | ||
namespace="NHibernate.Tests" | ||
assembly="NHibernate.Tests"> | ||
|
||
<!-- | ||
Note that the Mapping-by-Code mapping in the same directory as this is equivalent. | ||
Only one of the two mappings should be used at a time. | ||
--> | ||
|
||
<class name="Person" table="Person"> | ||
<id name="PersonId" /> | ||
<property name="Name" /> | ||
|
||
<join table="PayrollInfo"> | ||
<key column="PayrollInfoPersonId" /> | ||
<property name="PayrollNumber" /> | ||
</join> | ||
|
||
<join table="Address"> | ||
<key column="AddressPersonId" /> | ||
<property name="City" /> | ||
</join> | ||
</class> | ||
|
||
</hibernate-mapping> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
using System; | ||
using System.Reflection; | ||
using NHibernate.Cfg; | ||
using NHibernate.Mapping.ByCode; | ||
|
||
namespace NHibernate.Tests | ||
{ | ||
public class SessionFactoryCreator | ||
{ | ||
public ISessionFactory GetSessionFactoryWithMbcMappings() | ||
{ | ||
var config = GetConfigWithoutMappings(); | ||
AddMbcMappings(config); | ||
return config.BuildSessionFactory(); | ||
} | ||
|
||
public ISessionFactory GetSessionFactoryWithXmlMappings() | ||
{ | ||
var config = GetConfigWithoutMappings(); | ||
AddXmlMappings(config); | ||
return config.BuildSessionFactory(); | ||
} | ||
|
||
Configuration GetConfigWithoutMappings() | ||
{ | ||
var config = new Configuration(); | ||
|
||
// For simplicity of reproduction I'm using SQLite in-memory DB, but | ||
// I have seen the same problem reproduced using MS-SQL 2012 as well, so | ||
// I don't think that db/dialect is relevant. | ||
config.DataBaseIntegration(db => | ||
{ | ||
db.Driver<Driver.SQLite20Driver>(); | ||
db.Dialect<Dialect.SQLiteDialect>(); | ||
db.ConnectionString = "Data Source=:memory:;Version=3;New=True;"; | ||
}); | ||
|
||
return config; | ||
} | ||
|
||
void AddMbcMappings(Configuration config) | ||
{ | ||
var mapper = new ConventionModelMapper(); | ||
mapper.AddMapping<PersonMap>(); | ||
var mapping = mapper.CompileMappingForAllExplicitlyAddedEntities(); | ||
|
||
config.AddDeserializedMapping(mapping, "MbC-Mappings"); | ||
} | ||
|
||
void AddXmlMappings(Configuration config) | ||
{ | ||
config.AddAssembly(Assembly.GetExecutingAssembly()); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
# Mapping-By-Code `Join` repro case | ||
This repository is a reproduction case for [a bug in NHibernate] whereby Mapping By Code (MbC) produces incorrect SQL if there is more than one `Join` in a single class mapping. It would seem that parts of the join mapping from the first of the joins are accidentally overrwriting/overriding the same parts of the join mapping from subsequent joins in the same class mapping file. More information may be found at the linked issue. | ||
|
||
The reproduction case framed as a pair of NUnit test cases, so to see the results just clone the repo and run **dotnet test**. | ||
|
||
* The test case `NHibernate_should_not_throw_when_querying_a_Person_mapped_using_XML` passes, and shows that NHibernate behaves correctly when not using MbC | ||
* The test case `NHibernate_should_not_throw_when_querying_a_Person_mapped_using_MbC` fails, demonstrating the bug | ||
|
||
Note that in this reproduction case, to make it as easy as possible to run, I have used an in-memory SQLite database. I have also seen this same bug reproduced against MS-SQL server 2012, so I do not think that the database, driver or dialect are related to the problem. | ||
|
||
### What the tests do | ||
The test project sets up two instances of `ISessionFactory`, which *should theoretically be equivalent*. One session factory uses MbC (see the class `PersonMap`) and the other session factory uses XML HBM mappings (see the file `PersonMap.hbm.xml`). | ||
|
||
A class named `DbInitializer` is used to create a sample database schema matching those mappings, with some sample data. The tests then query this data using a Linq query. | ||
|
||
* In the case of the XML-based mappings, everything works OK and no exception is raised. | ||
|
||
* In the case of the MbC-based mappings, the query throws an exception, due to incorrect SQL being sent to the database driver. | ||
|
||
### The exception & SQL | ||
The exception raised from the MbC test case is an `NHibernate.Exceptions.GenericADOException`, wrapping a native ADO exception from the database driver, complaining about invalid SQL logic. | ||
|
||
The SQL which is sent to the database (in the failing test) is as follows. *I have made trivial whitespace changes for readability, as well as adding a comment to point out the error*. | ||
|
||
```sql | ||
select | ||
person0_.PersonId as personid1_0_, | ||
person0_.Name as name2_0_, | ||
person0_1_.PayrollNumber as payrollnumber2_1_, | ||
person0_2_.City as city2_2_ | ||
|
||
from | ||
Person person0_ | ||
inner join PayrollInfo person0_1_ | ||
on person0_.PersonId=person0_1_.AddressPersonId | ||
-- ^^^ This is the error, it is the wrong column name, | ||
-- it should be PayrollInfoPersonId | ||
inner join Address person0_2_ | ||
on person0_.PersonId=person0_2_.AddressPersonId | ||
|
||
where person0_.Name=@p0 | ||
|
||
limit 1 | ||
``` | ||
|
||
[a bug in NHibernate]: https://github.com/nhibernate/nhibernate-core/issues/1277 |