Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hydrate non required string properties from null to empty string

If I have a model class

public class Foo
{
    public string Property1 { get; set; } = string.Empty;
    ... other properties omitted for brevity
}

It gets save to the database as null since it is not a required property. But now when I retrieve the entity that property is null. I have a lot of not required string properties on this entity and I don't want to have to do a lot of null checking on those properties, I just want them to be rehydrated as empty strings if they are null in the database. I don't mind them being stored in the db as null but when the entity is retrieved from the db I would like to have it mapped back to empty string if it is null in the db.

I'm thinking this must be a fairly common scenario and was wondering if I'm missing some simple way to do it with a configuration setting or something. I am fairly new to using EF.

In my OnModelCreating for this property I have:

.HasMaxLength(50)
.HasDefaultValue(string.Empty)

but it still gets stored as null and rehydrated as null. Again I don't mind it being stored as null but I want it hydrated as empty string if it is null in the db.

I tried modifying the model class like this:

private string property1 = string.empty;
public string Property1
{
    get { return property1; }
    set { property1 = value ?? string.Empty; }
} 

thinking that EF must have to use the setter but this still resulted in a null property. So far the only way I've been able to solve it by making the property like this:

private string property1 = string.empty;
public string Property1
{
    get { return property1 ?? string.empty; }
    set { property1 = value ?? string.Empty; }
} 

I'd really rather not have to make all my properties like that.

Can anyone suggest a cleaner way to do this or correct me if I'm doing something wrong or unusual or thinking about it wrong. Am I missing some easier way to achieve this?

I don't want to make the property required since empty string would not satisfy that case either.

like image 509
Joe Audette Avatar asked Dec 07 '15 17:12

Joe Audette


1 Answers

The question's original Entity Framework version was EF7, the first ef-core version that was renamed to EF-core 1 later. The described behavior considerably differs from the current EF-core version (2.1.1).

Let me mention the two key points:

1

It gets save to the database as null since it is not a required property. But now when I retrieve the entity that property is null.

That's not what happens currently.

Take a simple class:

public class Item
{
    public int ID { get; set; }
    public string Name { get; set; }
    public string Code { get; set; } = string.Empty;
}

When adding an Item of which only Name is set, the following SQL is executed:

exec sp_executesql N'SET NOCOUNT ON;
INSERT INTO [Items] ([Code], [Name])
VALUES (@p0, @p1);
SELECT [ID]
FROM [Items]
WHERE @@ROWCOUNT = 1 AND [ID] = scope_identity();

',N'@p0 nvarchar(4000),@p1 nvarchar(50)',@p0=N'',@p1=N'Item1'

The empty string is part of the insert statement. Also, when retrieving the item from the database, its Code is an empty string.

2

In my OnModelCreating for this property I have:

.HasMaxLength(50)
.HasDefaultValue(string.Empty)

but it still gets stored as null and rehydrated as null.

That, too, is different now. Take the same class but now with:

    public string Code { get; set; }  // removed "= string.Empty;"

...and mapping for Code:

modelBuilder.Entity<Item>().Property(p => p.Code).HasMaxLength(50).HasDefaultValue(string.Empty);

...then this is the resulting table:

CREATE TABLE [Items] (
    [ID] int NOT NULL IDENTITY,
    [Name] nvarchar(50) NOT NULL,
    [Code] nvarchar(50) NULL DEFAULT N'', -- Default constraint
    CONSTRAINT [PK_Items] PRIMARY KEY ([ID])
);

As you see, the mapping instruction is translated into a database default.

When adding an Item of which only Name is set, the following SQL is executed:

exec sp_executesql N'SET NOCOUNT ON;
INSERT INTO [Items] ([Name])
VALUES (@p0);
SELECT [ID], [Code]
FROM [Items]
WHERE @@ROWCOUNT = 1 AND [ID] = scope_identity();

',N'@p0 nvarchar(50)',@p0=N'Item1'

So EF inserts the item and reads back the generated default value in order to keep the tracked entity in sync with the database. Likewise, an item read from the database later has an empty string in Code.

These findings confirm that EF7 was a very immature version of EF-core (although I didn't confirm that it really displayed the described behavior). There have been more, and more profound, breaking changes since it. I hope we will soon forget about these early EF-core versions. Since version 2.0, EF-core is finally developing into a production-ready ORM.

like image 64
Gert Arnold Avatar answered Nov 14 '22 12:11

Gert Arnold