Ho un modello che ha alcune colonne definite con valori predefiniti come
table.Column<bool>(nullable: false, defaultValueSql: "1")
Quando salvi una nuova entità nel database utilizzando context.SaveChanges()
, ho notato che le colonne con valori predefiniti non sono incluse nell'inserto nella query generata da Entity Framework, quindi i valori generati nel database sono quelli predefiniti anziché quelli che sto passando nel modello.
Devo impostare alcune proprietà nel contesto per poter impostare queste proprietà tramite il codice? Sto usando EF Core, ma non so se si tratta di un comportamento generale di tutte le versioni EF.
AGGIORNAMENTO: il codice è piuttosto semplice. Questo è pseudo codice di ciò che ho. Il Modello ha alcune proprietà definite con vincoli come quello che descrivo sopra table.Column<bool>(nullable: false, defaultValueSql: "1")
Userò la colonna MyBooleanProperty come esempio. Ho un servizio:
var newModel = new GEAddress();
newModel = someEntity.MyBooleanProperty; //it is false,but ends up as 1 in the database
Sto usando l'unità di lavoro e i depositi così ho
_unitOfWork.MyModelRepository.Add(newModel);
_unitOfWork.SaveChanges();
Nella finestra di output di VS, vedo come invia tutte le proprietà in una query INSERT INTO
e quindi esegue una SELECT
sulle proprietà con valori predefiniti. Il risultato è il nuovo modello nel database con tutti i valori che ho inviato, ad eccezione delle colonne con valori predefiniti. Non riesco a modificare la configurazione per queste tabelle poiché è utilizzata da un altro sistema che ha bisogno di tali regole.
Mi piacerebbe sapere una spiegazione sul perché questo sta accadendo più di una soluzione. Posso aggirare questo problema, ma mi piacerebbe sapere perché questo comportamento sta accadendo
Lo definirei un bug.
Ho messo insieme un semplice test, solo una classe con alcune impostazioni predefinite:
public class Test
{
public int ID { get; set; }
public int IntDef { get; set; }
public bool BoolDef { get; set; }
public DateTime? DateDef { get; set; }
}
(Si noti che DateTime è annullabile)
La mappatura:
modelBuilder.Entity<Test>().HasKey(a => a.ID);
modelBuilder.Entity<Test>().Property(s => s.DateDef).HasDefaultValueSql("GETDATE()");
modelBuilder.Entity<Test>().Property(s => s.IntDef).HasDefaultValueSql("1");
modelBuilder.Entity<Test>().Property(s => s.BoolDef).HasDefaultValue(true);
// Equivalent:
// modelBuilder.Entity<Test>().Property(s => s.BoolDef).HasDefaultValueSql("1");
Istruzione SQL che crea la tabella:
CREATE TABLE [Tests] (
[ID] int NOT NULL IDENTITY,
[BoolDef] bit NOT NULL DEFAULT 1,
[DateDef] datetime2 DEFAULT (GETDATE()),
[IntDef] int NOT NULL DEFAULT (1),
CONSTRAINT [PK_Tests] PRIMARY KEY ([ID])
);
Quando inserisco un nuovo Test
senza impostare alcun valore, l'istruzione insert è:
INSERT INTO [Tests]
DEFAULT VALUES;
SELECT [ID], [BoolDef], [DateDef], [IntDef]
FROM [Tests]
WHERE @@ROWCOUNT = 1 AND [ID] = scope_identity();
Si vede che i tre valori predefiniti (e il valore di identità generato) vengono letti dal database dopo l'inserimento. [A proposito, questo è nuovo in EF-Core. In EF6, solo i valori di identità e i valori delle colonne contrassegnati come DatabaseGeneratedOption.Computed
venivano letti dal database dopo l'inserimento (e l'aggiornamento)].
Questo è l'oggetto Test
creato:
ID IntDef BoolDef DateDef
1 1 True 21-11-16 19:52:56
Ora inserisco un nuovo Test
e assegna tutti i valori, ma, solo per divertimento, utilizzo i valori predefiniti per i tipi non annullabili:
var item = new Test
{
IntDef = default(int), // 0
BoolDef = default(bool), // false
DateDef = default(DateTime), // 01-01-01 0:00:00
};
Ecco la dichiarazione SQL:
exec sp_executesql N'SET NOCOUNT ON;
INSERT INTO [Tests] ([DateDef])
VALUES (@p0);
SELECT [ID], [BoolDef], [IntDef]
FROM [Tests]
WHERE @@ROWCOUNT = 1 AND [ID] = scope_identity();
',N'@p0 datetime2(7)',@p0='0001-01-01 00:00:00'
Ovviamente, EF non ha modo di inferire che i valori predefiniti sono stati assegnati deliberatamente. Quindi, come vedi, solo per la colonna DateTime
nullable viene inserito il valore assegnato, non per le colonne non annullabili. Ora il valore per DateDef
non viene letto dal database dopo l'inserimento.
I valori dell'entità sono:
ID IntDef BoolDef DateDef
1 1 True 01-01-01 0:00:00
Non è quello che ci si aspetterebbe dopo aver salvato l'entità - non del tutto!
Che significa:
Quando si configura una proprietà con un valore predefinito in EF-Core e questo valore predefinito è diverso dal valore predefinito .Net, non è possibile inserire un'entità con valori predefiniti per il tipo .Net (come false
per un valore booleano).
Penso che questo sia un bug serio, forse squalifica anche il nuovo comportamento di EF-Core riguardante i default.
aggiunta
Come detto nel commento di Ivan, puoi impedire a EF di impostare i valori predefiniti aggiungendo ValueGeneratedNever()
, ad esempio:
modelBuilder.Entity<Test>().Property(s => s.IntDef)
.HasDefaultValueSql("1").ValueGeneratedNever();
Ora il valore verrà salvato così com'è e EF non lo leggerà dopo gli inserimenti e gli aggiornamenti. Tutto sommato, penso che definire i valori predefiniti per le proprietà non annullabili non sia utile.
Testato con anteprima EF Core 1.1.0.
Se non si contrassegna la proprietà come calcolato con l'attributo corretto
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
ef genererà l'istruzione insert con il valore della proprietà.
Durante l'aggiornamento EF genera la clausola SET dell'istruzione UPDATE inserendo solo i valori modificati.
Avete già una soluzione alternativa, se la proprietà è generata solo dal DBMS è possibile utilizzare l'attributo precedente altrimenti è necessario inserire il valore predefinito nel costruttore della classe che rappresenta l'entità.