Description
I've noticed whilst profiling recently that I keep getting random calls to update particular entities when I flush changes to the database, even if they haven't changed.
After looking into this further, i've discovered this seems to happen when the entity has a dynamic component with nullable properties.
For example, say I have the following application:
using var sessionFactory = BuildConfiguration().BuildSessionFactory();
using var session = sessionFactory.OpenSession();
var products = await session.Query<Product>().ToListAsync();
await session.FlushAsync();
Console.ReadLine();
Configuration BuildConfiguration() {
var configuration = new Configuration();
configuration.DataBaseIntegration(db => {
db.ConnectionString = @"Data Source=TDB2008R2;Initial Catalog=KIT5-TEMP;Persist Security Info=True;User ID=sa;Password=m3spexec4@sadrAb;Connect Timeout=30";
db.Dialect<MsSql2008Dialect>();
});
var mapper = new ModelMapper();
mapper.AddMappings(Assembly.GetCallingAssembly().ExportedTypes);
configuration.AddMapping(mapper.CompileMappingForAllExplicitlyAddedEntities());
return configuration;
}
With the following model and mapping:
public class Product {
public virtual int Id { get; set; }
public virtual string Name { get; set; } = default!;
private IDictionary<string, object?>? _attributes;
public virtual IDictionary<string, object?> Attributes {
get => _attributes ??= new Dictionary<string, object?>();
set => _attributes = value;
}
}
public class ProductMapping : ClassMapping<Product> {
public ProductMapping() {
Table("Products");
Id(x => x.Id, m => m.Generator(Generators.Identity));
Property(x => x.Name);
Component(x => x.Attributes, new {
Sku = (string?)null
}, dc => {
dc.Property(x => x.Sku);
});
}
}
Here's the SQL to setup the database table with some sample data:
CREATE TABLE [dbo].[Products](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NOT NULL,
[Sku] [nvarchar](50) NULL,
CONSTRAINT [PK_Products] PRIMARY KEY CLUSTERED ([Id] ASC)
)
INSERT INTO [dbo].[Products] ([Name], [Sku]) VALUES ('Test 1', NULL)
INSERT INTO [dbo].[Products] ([Name], [Sku]) VALUES ('Test 2', 'ABC')
INSERT INTO [dbo].[Products] ([Name], [Sku]) VALUES ('Test 3', NULL)
Now when profiling the application, notice how it will issue 2 updates (only the products where the Sku
is null) to the database when you flush the changes.
This seems like a bug to me but I thought I'd check before creating a test case.
I have discovered that this isn't an issue if I change my Attributes
property to the following:
public virtual IDictionary<string, object?> Attributes { get; set; } = new Dictionary<string, object?>();
However this will still set the Attributes
to null against the products where all the values of the Attributes
are null and will likely lead to much bigger problems.