Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"Wrong" SQL Statement generated within Visual Studio 2022 targeting .NET 7 when entity is derived from a base class and not all properties are mapped #3207

Closed
vie-mo opened this issue Dec 1, 2022 · 6 comments

Comments

@vie-mo
Copy link

vie-mo commented Dec 1, 2022

I have a strange failure if i use NHibernate in my application in .NET 7 (port from .NET 6). I have made a littel Visual Studio 2022 solution to reproduce that behaviour and attached it.

In the VS solution there are 3 projects:

  • Common
  • ConsoleNet6
  • ConsoleNet7

In the project Common exists a SQLite database testdb.sqlite with two tables (but no rows):

Table Derived_Class:

  • Id
  • ClassProp1
  • CLASS_PROP_2
  • BASE_PROP_1
  • BASE_PROP_2
  • BASE_PROP_3

Table Derived_Class_2:

  • Id
  • ClassProp1
  • CLASS_PROP_2

NB: the naming with and without underscores was just for testing the mappings!

These tables are mapped to classes as follows:

public partial class DerivedClassMap : ClassMapping<DerivedClass>
{

	public DerivedClassMap()
	{
		Table("DERIVED_CLASS");
		DynamicInsert(false);
		DynamicUpdate(false);
		Lazy(false);

		Id(x => x.Id, m =>
		{
		  
			m.Generator(Generators.Assigned);
		});

		Property(x => x.ClassProp1);
		Property(x => x.ClassProp2, m => m.Column("CLASS_PROP_2"));

		Property(x => x.BaseProp1, m => m.Column("BASE_PROP_1"));
		Property(x => x.BaseProp2, m => m.Column("BASE_PROP_2"));
		Property(x => x.BaseProp3, m => m.Column("BASE_PROP_3"));
			
		ExtendMapping();
	}

	partial void ExtendMapping();
}   

public partial class DerivedClass2Map : ClassMapping<DerivedClass2>
{

	public DerivedClass2Map()
	{
		Table("DERIVED_CLASS_2");
		DynamicInsert(false);
		DynamicUpdate(false);
		Lazy(false);

		Id(x => x.Id, m =>
		{
		  
			m.Generator(Generators.Assigned);
		});

		Property(x => x.ClassProp1);
		Property(x => x.ClassProp2, m => m.Column("CLASS_PROP_2"));
			
		ExtendMapping();
	}

	partial void ExtendMapping();
}   

Both classes are derived from ObjectBase ...

public abstract class ObjectBase
{
	public string BaseProp1 { get; set; }
	public string BaseProp2 { get; set; }
	public string BaseProp3 { get; set; }
}

public partial class DerivedClass : ObjectBase
{
	#region [ Public Members ]
	public int Id { get; set; }
	public string ClassProp1 { get; set; }
	public string ClassProp2 { get; set; }
	#endregion
	...
}

public partial class DerivedClass2 : ObjectBase
{
	#region [ Public Members ]
	public int Id { get; set; }
	public string ClassProp1 { get; set; }
	public string ClassProp2 { get; set; }
	#endregion
	...
}

...but as you can see in the mapping, no property from the abstract base class is mapped for DerivedClass2.

If i run the ConsoleNet7 project within Visual Studio 2022 (with or without debugging) the line

Log.Logger.Information("Fetching from table \"DERVIED_CLASS\": all properties mapped (also that from the abstract base class)");
var derivedClasses = _session.Query<DerivedClass>().ToList();

executes correct and the line

Log.Logger.Information("Fetching from table \"DERVIED_CLASS_2\": no base class properties mapped");
var derivedClasses2 = _session.Query<DerivedClass2>().ToList();`

which resides in the DummyService class in the project Common, produces the following error message:

01.12.2022 17:17:34.664 +01:00 [ ERR] [NHibernate.AdoNet.AbstractBatcher] Could not execute query: select derivedcla0_.Id as id1_0_, derivedcla0_.ClassProp1 as classprop2_0_, derivedcla0_.CLASS_PROP_2 as class3_0_, derivedcla0_.BASE_PROP_1 as base4_0_, derivedcla0_.BASE_PROP_2 as base5_0_, derivedcla0_.BASE_PROP_3 as base6_0_ from DERIVED_CLASS_2 derivedcla0_
code = Error (1), message = System.Data.SQLite.SQLiteException (0x800007BF): SQL logic error
no such column: derivedcla0_.BASE_PROP_1

And that is correct...there is no column named BASE_PROP_1 on table DERIVED_CLASS_2 only on DERIVED_CLASS !!

So why is this statement generated this way and why with the correct (!!) column name from table DERIVED_CLASS ??

If i run the same statements within the .NET 6 console project, then the result or behaviour is correct and no error is displayed for the second select:

01.12.2022 17:38:35.745 +01:00 [ INF] [] Application Starting
01.12.2022 17:38:36.680 +01:00 [ INF] [] Fetching from table "DERVIED_CLASS": all properties mapped (also that from the abstract base class)
01.12.2022 17:38:36.880 +01:00 [ DBG] [NHibernate.SQL] select derivedcla0_.Id as id1_1_, derivedcla0_.ClassProp1 as classprop2_1_, derivedcla0_.CLASS_PROP_2 as class3_1_, derivedcla0_.BASE_PROP_1 as base4_1_, derivedcla0_.BASE_PROP_2 as base5_1_, derivedcla0_.BASE_PROP_3 as base6_1_ from DERIVED_CLASS derivedcla0_
NHibernate: select derivedcla0_.Id as id1_1_, derivedcla0_.ClassProp1 as classprop2_1_, derivedcla0_.CLASS_PROP_2 as class3_1_, derivedcla0_.BASE_PROP_1 as base4_1_, derivedcla0_.BASE_PROP_2 as base5_1_, derivedcla0_.BASE_PROP_3 as base6_1_ from DERIVED_CLASS derivedcla0_
01.12.2022 17:38:36.894 +01:00 [ INF] [] Fetching from table "DERVIED_CLASS_2": no base class properties mapped
01.12.2022 17:38:36.895 +01:00 [ DBG] [NHibernate.SQL] select derivedcla0_.Id as id1_0_, derivedcla0_.ClassProp1 as classprop2_0_, derivedcla0_.CLASS_PROP_2 as class3_0_ from DERIVED_CLASS_2 derivedcla0_
NHibernate: select derivedcla0_.Id as id1_0_, derivedcla0_.ClassProp1 as classprop2_0_, derivedcla0_.CLASS_PROP_2 as class3_0_ from DERIVED_CLASS_2 derivedcla0_

I also get these behaviour if i use Oracle or SQL Server but for testing and submitting the SQLite Version is better.

And now the absolut strange thing....

If i execute either the .NET6 or .NET7 console app directly in a command prompt on my machine, then both of them, also the .NET7 one, are working correct !!! The described error is only within Visual Studio 2022 targeting .NET 7 present.

I also checked with a complete clean install of Windows 11 (all patches) and a clean install of Visual Studio 2022 and got the same result.

Maybe i am missing something, but i don't know what.

Solution1.zip

@bahusoid
Copy link
Member

bahusoid commented Dec 2, 2022

I don't have time to investigate but my bet it's some MappingByCode quirk (maybe due to some .net 7 bug). Check how xml mappings look like in .net 7 and .net 6 using something like this:

//Somewhere in your application you have code similar to
var mapper  = new ModelMapper();
HbmMapping mapping = mapper.CompileMapping...();

//Add this
var mappingXml = mapping.AsString();
File.WriteAllText("mappings.xml", mappingXml);

@alfeg
Copy link

alfeg commented Dec 4, 2022

Wow. Can confirm that. This is a blocker issue for us to migrate to net7. But I were not yet able to do a proper repro.

But there is definitely some issue with Conform mapping.
I can confirm that xml mapping start to contain property mappings derived from other objects.

@bahusoid here a mapping diff:
https://gist.github.com/alfeg/6cb7023c040a14deda96d6cc361cfc98/revisions

mappings.zip

net6

<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" namespace="Common.Entities" assembly="Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" xmlns="urn:nhibernate-mapping-2.2">
  <class name="DerivedClass2" lazy="false" table="DERIVED_CLASS_2">
    <id name="Id" type="Int32">
      <generator class="assigned" />
    </id>
    <property name="ClassProp1" />
    <property name="ClassProp2" column="CLASS_PROP_2" />
  </class>
  <class name="DerivedClass" lazy="false" table="DERIVED_CLASS">
    <id name="Id" type="Int32">
      <generator class="assigned" />
    </id>
    <property name="ClassProp1" />
    <property name="ClassProp2" column="CLASS_PROP_2" />
    <property name="BaseProp1" column="BASE_PROP_1" />
    <property name="BaseProp2" column="BASE_PROP_2" />
    <property name="BaseProp3" column="BASE_PROP_3" />
  </class>
</hibernate-mapping>

net7

<?xml version="1.0" encoding="utf-8"?>
<hibernate-mapping xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" namespace="Common.Entities" assembly="Common, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" xmlns="urn:nhibernate-mapping-2.2">
  <class name="DerivedClass2" lazy="false" table="DERIVED_CLASS_2">
    <id name="Id" type="Int32">
      <generator class="assigned" />
    </id>
    <property name="ClassProp1" />
    <property name="ClassProp2" column="CLASS_PROP_2" />
    <property name="BaseProp1" column="BASE_PROP_1" />
    <property name="BaseProp2" column="BASE_PROP_2" />
    <property name="BaseProp3" column="BASE_PROP_3" />
  </class>
  <class name="DerivedClass" lazy="false" table="DERIVED_CLASS">
    <id name="Id" type="Int32">
      <generator class="assigned" />
    </id>
    <property name="ClassProp1" />
    <property name="ClassProp2" column="CLASS_PROP_2" />
    <property name="BaseProp1" column="BASE_PROP_1" />
    <property name="BaseProp2" column="BASE_PROP_2" />
    <property name="BaseProp3" column="BASE_PROP_3" />
  </class>
</hibernate-mapping>

@alfeg
Copy link

alfeg commented Dec 4, 2022

Seems that this issue is related to dotnet/runtime#78218
"Only" affects when debugging with hot-reload enabled.

In NH code method ExplicitlyDeclaredModel.IsPersistentProperty() compare PersistentMembers to MemberInfo by equality

public virtual bool IsPersistentProperty(MemberInfo member)
{
return PersistentMembers.Contains(member);
}

@alfeg
Copy link

alfeg commented Dec 4, 2022

Sorry for spam.

@vie-mo this issue in NH-core can be closed.

There is nothing NH team can do at this point of time.
Workaround is to not use Visual Studio and disable hot reload until hotfix released (works fine in Rider with hot reload disabled)

So we just need to wait for a net7 hotfix release.
All code changes on runtime side is already merged: dotnet/runtime#78258

@fredericDelaporte
Copy link
Member

Thanks for your findings, closing as external.

@alfeg
Copy link

alfeg commented Dec 13, 2022

FYI, net 7.0.101 released with the fix.
@vie-mo repro is no longer repro!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants