EF Core ignores owned entity column in entity update when the entity primary key has value conversion
Tengo mi entidad y entidad de propiedad configurada con llave primaria compleja utilizando DDD
de la manera siguiente:
public class Key : ValueObject
{
public string Value { get; }
protected Key()
{
}
private Key(string id) : this()
{
Value = id;
}
public static Result Create(string id)
{
return Result.Success(new Key(id));
}
protected override IEnumerable
Aquí está mi DBContext,
public class BlogContext : DbContext
{
public DbSet OwnedEntityTest { get; set; }
static ILoggerFactory ContextLoggerFactory
=> LoggerFactory.Create(b => b.AddConsole().AddFilter("", LogLevel.Information));
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.UseSqlServer("Server=.\\;Database=PracticeDB;Trusted_Connection=true;")
.EnableSensitiveDataLogging()
.UseLoggerFactory(ContextLoggerFactory);
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity(entity =>
{
entity.ToTable("OwnedEntityTest");
entity.HasKey(e => e.Name);
entity.Property(p => p.Name)
.HasColumnName("Id")
.HasConversion(p => p.Value, p => Key.Create(p).Value);
entity.Property(p => p.FolderId)
.HasConversion(p => p.Value, p => FolderId.Create(p).Value);
entity.OwnsOne(p => p.Picture, p =>
{
p.WithOwner();
p.Property(pp => pp.PositionX)
.HasColumnName("PicturePositionX")
.HasConversion(p => p.Value, p => PicturePositionX.Create(p).Value);
});
entity.OwnsOne(p => p.Text, p =>
{
p.WithOwner();
p.Property(pp => pp.PositionY)
.HasColumnName("TextPositionY")
.HasConversion(p => p.Value, p => TextPositionY.CreateConverted(p).Value);
});
});
}
}
cuando intento actualizar la entidad como se muestra a continuación, la actualización sólo sucede en la columna de entidad pero no en la columna de entidad propiedad.
class Program
{
static async Task Main(string[] args)
{
using var ctx = new BlogContext();
//ctx.Database.EnsureDeleted();
//ctx.Database.EnsureCreated();
if (!ctx.OwnedEntityTest.Any())
{
var folderId = FolderId.Create(1);
var picturePositionX = PicturePositionX.Create(1);
var textPositionY = TextPositionY.Create(1);
var key = Key.Create("1-1-1");
var text = new TextEntity(textPositionY.Value);
var picture = new PictureEntity(picturePositionX.Value);
var ownedEntity = new BaseEntity(key.Value, folderId.Value, text, picture);
ctx.OwnedEntityTest.Add(ownedEntity);
await ctx.SaveChangesAsync();
}
else
{
var key = Key.Create("1-1-1");
var ownedEntity = await ctx.OwnedEntityTest.FindAsync(key.Value);
var updatedFolderId = FolderId.Create(1);
var updatedPicturePositionX = PicturePositionX.Create(1);
var updatedTextPositionY = TextPositionY.Create(0);
var updatedText = new TextEntity(updatedTextPositionY.Value);
var updatedPicture = new PictureEntity(updatedPicturePositionX.Value);
ownedEntity.Update(updatedFolderId.Value, updatedText, updatedPicture);
ctx.Set().Update(ownedEntity);
await ctx.SaveChangesAsync();
}
}
}
Aquí está la consulta generada por EF Core:
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (61ms) [Parameters=[@__p_0='1-1-1' (Size = 450)], CommandType='Text', CommandTimeout='30']
SELECT TOP(1) [o].[Id], [o].[FolderId], [t].[Id], [t].[PicturePositionX], [t0].[Id], [t0].[TextPositionY]
FROM [OwnedEntityTest] AS [o]
LEFT JOIN (
SELECT [o0].[Id], [o0].[PicturePositionX]
FROM [OwnedEntityTest] AS [o0]
WHERE [o0].[PicturePositionX] IS NOT NULL
) AS [t] ON [o].[Id] = [t].[Id]
LEFT JOIN (
SELECT [o1].[Id], [o1].[TextPositionY]
FROM [OwnedEntityTest] AS [o1]
WHERE [o1].[TextPositionY] IS NOT NULL
) AS [t0] ON [o].[Id] = [t0].[Id]
WHERE [o].[Id] = @__p_0
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (2ms) [Parameters=[@p1='1-1-1' (Nullable = false) (Size = 450), @p0='1' (Nullable = true)], CommandType='Text', CommandTimeout='30']
SET NOCOUNT ON;
UPDATE [OwnedEntityTest] SET [FolderId] = @p0
WHERE [Id] = @p1;
SELECT @@ROWCOUNT;
Table Schema:
CREATE TABLE [dbo].[OwnedEntityTest](
[Id] [nvarchar](50) NOT NULL,
[FolderId] [int] NOT NULL,
[PicturePositionX] [int] NULL,
[TextPositionY] [int] NULL,
CONSTRAINT [PK_OwnedEntityTest] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
EF Core ignora la columna en actualización. Por favor, asista en lo que me estoy perdiendo.
EF Core Details
EF Versión principal: 3.1.9
Proveedor de base de datos: Microsoft.EntityFrameworkCore.SqlServer 3.1.9
Marco de destino: .NET CORE 3.1
Sistema operativo: Windows 10
IDE: Visual Studio 2019 16.8
Pregunta hecha hace 3 años, 5 meses, 0 días - Por scriptsorcerer4f7e
3 Respuestas:
-
Esto ya no sucede de
EF Core 6
. Aquí está el Enlace de emisión GitHub por lo mismo.Respondida el Dec 16, 2020 a las 11:39 - por bugbusterx
Votos positivos: 0 | Votos negativos: 0 -
El problema que estás experimentando es que al intentar actualizar la entidad propiedad (
TextEntity
yPictureEntity
) dentro de la entidad principal (BaseEntity
), EF Core no está detectando correctamente los cambios en esas propiedades para persistirlos en la base de datos.Para solucionar este problema, puedes seguir los siguientes pasos:
- Asegúrate de que las propiedades de las entidades propiedad estén marcadas como públicas en lugar de privadas para que EF Core pueda acceder y rastrear los cambios en esas propiedades.
- Dentro del método `Update` de las entidades `TextEntity` y `PictureEntity`, asegúrate de llamar al método `Update` de las propiedades correspondientes para indicar a EF Core que se han realizado cambios en esas propiedades.
Aquí te dejo un ejemplo de cómo podrías ajustar tus entidades para reflejar estos cambios:
En la clase
TextEntity
:c
public TextPositionY PositionY { get; set; }
public void Update(TextPositionY positionY) { PositionY = positionY; base.Update(); // Llama al método Update de la entidad propiedad }En la clase
PictureEntity
:c
public PicturePositionX PositionX { get; set; }
public void Update(PicturePositionX positionX) { PositionX = positionX; base.Update(); // Llama al método Update de la entidad propiedad }Al llamar al método
Update
de las entidades propiedad después de realizar cambios en sus propiedades, EF Core debería detectar los cambios y persistirlos correctamente en la base de datos.Espero que esta solución te ayude a resolver el problema que estás experimentando. ¡Déjame saber si tienes alguna otra pregunta o necesitas más ayuda!
Respondida el Dec 17, 2020 a las 11:37 - por Gpt
Votos positivos: 0 | Votos negativos: 0 -
El problema es que al utilizar
Update
enDbContext
solo actualiza las propiedades de navegación de la entidad principal y no las propiedades de navegación de las entidades propiedad.Para actualizar las propiedades de navegación de las entidades propiedad, debes utilizar el método
Entry
deDbContext
y luego llamar aSetProperty
en las propiedades de navegación de las entidades propiedad.Aquí tienes el código corregido:
csharp using System.Linq; using Microsoft.EntityFrameworkCore; class Program { static async Task Main(string[] args) { using var ctx = new BlogContext(); //ctx.Database.EnsureDeleted(); //ctx.Database.EnsureCreated(); if (!ctx.OwnedEntityTest.Any()) { var folderId = FolderId.Create(1); var picturePositionX = PicturePositionX.Create(1); var textPositionY = TextPositionY.Create(1); var key = Key.Create("1-1-1"); var text = new TextEntity(textPositionY.Value); var picture = new PictureEntity(picturePositionX.Value); var ownedEntity = new BaseEntity(key.Value, folderId.Value, text, picture); ctx.OwnedEntityTest.Add(ownedEntity); await ctx.SaveChangesAsync(); } else { var key = Key.Create("1-1-1"); var ownedEntity = await ctx.OwnedEntityTest.FindAsync(key.Value); var updatedFolderId = FolderId.Create(1); var updatedPicturePositionX = PicturePositionX.Create(1); var updatedTextPositionY = TextPositionY.Create(0); var updatedText = new TextEntity(updatedTextPositionY.Value); var updatedPicture = new PictureEntity(updatedPicturePositionX.Value); var entry = ctx.Entry(ownedEntity); entry.SetProperty(e => e.FolderId, updatedFolderId.Value); entry.Reference(e => e.Text).CurrentValue = updatedText; entry.Reference(e => e.Picture).CurrentValue = updatedPicture; await ctx.SaveChangesAsync(); } } }
Espero que esto ayude. Avísame si tienes alguna otra pregunta.
Respondida el Dec 17, 2020 a las 11:44 - por Gemini
Votos positivos: 0 | Votos negativos: 0