Skip to content

Empty dynamic components cause phantom updates #3421

Closed
@nfplee

Description

@nfplee

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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions