viciado tipos semejanzas romano nulos nulidad entre ejemplos derecho anulabilidad administrativo actos acto absoluta c# entity-framework ef-code-first graphdiff

c# - tipos - La relación no se pudo cambiar porque una o más de las propiedades de clave externa no son anulables



semejanzas entre nulidad y anulabilidad (1)

En Entity Framework puedes trabajar con asociaciones de claves extranjeras . Es decir, una clave foránea para otro objeto se expresa como un par de dos propiedades: una propiedad de clave foránea primitiva (por ejemplo, NominalRouting.OrderItemId ) y una referencia de objeto ( NominalRouting.OrderItem ).

Esto significa que puede establecer un valor primitivo o una referencia de objeto para establecer una asociación de clave externa. Si configura uno de ellos, EF intenta mantener el otro sincronizado, si es posible. Desafortunadamente, esto también puede dar lugar a conflictos entre los valores de clave foránea primitivos y sus referencias acompañantes.

Es difícil decir qué sucede exactamente en tu caso. Sin embargo, sé que su enfoque de "copiar" objetos de un padre a otro no es ... ideal. Primero, nunca es una buena idea cambiar los valores de la clave principal. Al establecerlos en 0 , hace que el objeto se vea como nuevo, pero no lo son. En segundo lugar, asigna los mismos objetos secundarios a otros objetos principales muchas veces. Creo que, como consecuencia, terminas con un gran número de objetos que tienen un valor de clave externa pero no una referencia .

Dije "copiar", porque eso es lo que aparentemente intentas lograr. Si es así, debería clonar correctamente los objetos y targetOrderItem a cada targetOrderItem . Al mismo tiempo, me pregunto por qué (aparentemente) clona todos estos objetos. Parece que las asociaciones de muchos a muchos son más apropiadas aquí. Pero ese es un tema diferente.

Ahora tu pregunta actual: ¿cómo encontrar las asociaciones conflictivas?

Eso es muy, muy duro. Requeriría un código para buscar a través del modelo conceptual y encontrar propiedades involucradas en asociaciones de clave externa. Entonces tendrías que encontrar sus valores y encontrar desajustes. Lo suficientemente difícil, pero trivial en comparación con determinar cuándo un conflicto posible es un conflicto real . Permítanme aclarar esto con dos ejemplos. Aquí, una clase OrderItem tiene una asociación de clave foránea requerida que consiste en las propiedades Order y OrderId .

var item = new OrderItem { OrderId = 1, ... }; db.OrderItems.Add(item); db.SaveChanges();

Así que hay un artículo con OrderId asignado y Order = null, y EF está contento.

var item = db.OrderItems.Include(x => x.Order).Find(10); // returns an OrderItem with OrderId = 1 item.Order = null; db.SaveChanges();

Nuevamente, un elemento con OrderId asignado y Order = null, pero EF lanza la excepción "La relación no se pudo cambiar ...".

(Y hay más situaciones de conflicto posibles)

Por lo tanto, no es suficiente buscar valores no OrderId/Order en pares OrderId/Order , también tendría que inspeccionar los estados de las entidades y saber exactamente en qué combinación de estados no se permite una falta de coincidencia. Mi consejo: olvídalo, arregla tu código.

Sin embargo, hay un truco sucio. Cuando EF intenta hacer coincidir valores y referencias de clave externa, en algún lugar en el fondo de un árbol anidado, if es así, recopila los conflictos de los que estamos hablando en una variable miembro del ObjectStateManager , llamada _entriesWithConceptualNulls . Es posible obtener su valor haciendo una reflexión:

#if DEBUG db.ChangeTracker.DetectChanges(); // Force EF to match associations. var objectContext = ((IObjectContextAdapter)db).ObjectContext; var objectStateManager = objectContext.ObjectStateManager; var fieldInfo = objectStateManager.GetType().GetField("_entriesWithConceptualNulls", BindingFlags.Instance | BindingFlags.NonPublic); var conceptualNulls = fieldInfo.GetValue(objectStateManager); #endif

conceptualNulls es un HashSet<EntityEntry> , EntityEntry es una clase interna, por lo que solo puede inspeccionar la colección en el depurador para tener una idea de las entidades en conflicto. Sólo para fines de diagnóstico!

Recibo el siguiente error durante la actualización con EF:

La operación falló: la relación no se pudo cambiar porque una o más de las propiedades de clave foránea no son anulables. Cuando se realiza un cambio en una relación, la propiedad de clave foránea relacionada se establece en un valor nulo. Si la clave foránea no admite valores nulos, se debe definir una nueva relación, la propiedad de clave foránea debe tener asignado otro valor no nulo o el objeto no relacionado debe eliminarse.

¿Hay alguna forma general de encontrar qué propiedades de clave externa causan el error anterior?

[Actualizar]

Para un caso, el siguiente código causa un error (trabajé en un entorno desconectado, por lo que utilicé graphdiff para actualizar el gráfico de mis objetos), cuando quiere ejecutar _uow.Commit(); :

public void CopyTechnicalInfos(int sourceOrderItemId, List<int> targetOrderItemIds) { _uow = new MyDbContext(); var sourceOrderItem = _uow.OrderItems .Include(x => x.NominalBoms) .Include("NominalRoutings.NominalSizeTests") .AsNoTracking() .FirstOrDefault(x => x.Id == sourceOrderItemId); var criteria = PredicateBuilder.False<OrderItem>(); foreach (var targetOrderItemId in orderItemIds) { int id = targetOrderItemId; criteria = criteria.OR(x => x.Id == id); } var targetOrderItems = _uow.OrderItems .AsNoTracking() .AsExpandable() .Where(criteria) .ToList(); foreach (var targetOrderItem in targetOrderItems) { //delete old datas and insert new datas targetOrderItem.NominalBoms = sourceOrderItem.NominalBoms; targetOrderItem.NominalBoms.ForEach(x => x.Id = 0); targetOrderItem.NominalRoutings = sourceOrderItem.NominalRoutings; targetOrderItem.NominalRoutings.ForEach(x => x.Id = 0); targetOrderItem.NominalRoutings .ForEach(x => x.NominalTests.ForEach(y => y.Id = 0)); targetOrderItem.NominalRoutings .ForEach(x => x.NominalSizeTests.ForEach(y => y.Id = 0)); _uow.OrderItems.UpdateGraph(targetOrderItem, x => x.OwnedCollection(y => y.NominalBoms) .OwnedCollection(y => y.NominalRoutings, with => with .OwnedCollection(t => t.NominalTests))); } _uow.Commit(); }