c# - una - llave primaria
Establecer una clave foránea para nulo cuando se usa primero el código de entidad de la estructura (6)
Como otra solución, compilé dos métodos en un método de extensión:
public static void SetToNull<TEntity, TProperty>(this TEntity entity, Expression<Func<TEntity, TProperty>> navigationProperty, DbContext context = null) where TEntity : class where TProperty : class { var pi = GetPropertyInfo(entity, navigationProperty); if (context != null) { //If DB Context is supplied, use Entry/Reference method to null out current value context.Entry(entity).Reference(navigationProperty).CurrentValue = null; } else { //If no DB Context, then lazy load first var prevValue = (TProperty)pi.GetValue(entity); } pi.SetValue(entity, null); } static PropertyInfo GetPropertyInfo<TSource, TProperty>( TSource source, Expression<Func<TSource, TProperty>> propertyLambda) { Type type = typeof(TSource); MemberExpression member = propertyLambda.Body as MemberExpression; if (member == null) throw new ArgumentException(string.Format( "Expression ''{0}'' refers to a method, not a property.", propertyLambda.ToString())); PropertyInfo propInfo = member.Member as PropertyInfo; if (propInfo == null) throw new ArgumentException(string.Format( "Expression ''{0}'' refers to a field, not a property.", propertyLambda.ToString())); if (type != propInfo.ReflectedType && !type.IsSubclassOf(propInfo.ReflectedType)) throw new ArgumentException(string.Format( "Expression ''{0}'' refers to a property that is not from type {1}.", propertyLambda.ToString(), type)); return propInfo; }
Esto le permite suministrar un DbContext si tiene uno, en cuyo caso usará el método más eficiente y establecerá CurrentValue de Entry Reference nulo.
entity.SetToNull(e => e.ReferenceProperty, dbContext);
Si no se proporciona DBContext, primero se cargará de forma diferida.
entity.SetToNull(e => e.ReferenceProperty);
Estoy utilizando la primera implementación de la base de datos de Entity Framework Code First como la capa de datos para un proyecto, pero me he encontrado con un problema.
Necesito poder establecer una clave foránea en nulo para eliminar una asociación en la base de datos.
Tengo 2 objetos. Uno se llama Proyecto.
public class Project
{
public int ProjectId {get; set;}
public Employee Employee {get;set;}
}
public class Employee
{
public int EmployeeId {get; set;}
public string EmployeeName {get;set;}
}
Esto coincide con lo que tengo en la base de datos:
CREATE TABLE Project(
ProjectId int IDENTITY(1,1) NOT NULL,
EmployeeId int NULL
)
CREATE TABLE Project(
EmployeeId int IDENTITY(1,1) NOT NULL,
EmployeeName varchar(100) NULL
)
Puedo asignar un empleado a un proyecto. Sin embargo, quiero poder eliminar un empleado de un proyecto y hacer que el campo Empleado sea nulo. En mi UI, esto se mostrará como ''No Empleado Asignado''.
Sin embargo, a falta de una consulta SQL directa, parece que no puedo encontrar una forma de hacerlo en el marco de la entidad 4.1.
He intentado:
public void RemoveEmployeeFromProject(int projectId)
{
var project = Context.Projects.FirstOrDefault(x => x.ProjectId == projectId);
project.Employee = null;
Context.SaveChanges();
}
Pero esto no hace nada.
¿Alguien tiene alguna idea?
Creo que el problema es que, en lo que respecta al contexto, en realidad no ha cambiado nada.
Puede utilizar el enfoque de carga diferida sugerido anteriormente mediante el uso de virtual
, pero dado que no ha solicitado que el empleado se cargue todavía, sigue siendo nulo. Puedes intentar esto:
var forceLoad = project.Employee;
project.Employee = null; // Now EF knows something has changed
Context.SaveChanges();
Alternativamente, explícitamente incluirlo en su solicitud original:
var project = Context.Projects.Include(x => x.Employee).FirstOrDefault(x => x.ProjectId == projectId);
project.Employee = null;
Context.SaveChanges();
En una nota lateral, FirstOrDefault
devolverá null
si ningún Project
coincide con el id. Si sabe que el proyecto existe, puede usar First
. Incluso podría usar Single
que afirmará que solo hay un proyecto de este tipo. Si continúa utilizando FirstOrDefault
, le recomendaría buscar null
antes de trabajar con el project
.
Debe incluir en la consulta linq, la propiedad para asignar, usando el mismo nombre que tiene en la clase Project:
var project = Context.Projects.Include("Employee").FirstOrDefault(x => x.ProjectId == projectId);
La respuesta a esto es bastante simple. EF no puede inferir el tipo dada la información que ha proporcionado.
Solo haz esto en su lugar:
public void RemoveEmployeeFromProject(int projectId)
{
var project = Context.Projects.FirstOrDefault(x => x.ProjectId == projectId);
project.EmployeeId = (int?)null;
Context.SaveChanges();
}
y funcionará
Puedes hacerlo de esta manera, lo que significa que no tienes que cargar la entidad relacionada.
context.Entry(Project).Reference(r => r.Employee).CurrentValue = null;
Si habilita la carga diferida haciendo que la propiedad del empleado sea virtual, ¿funciona?
public class Project
{
public int ProjectId {get; set;}
public virtual Employee Employee {get;set;}
}
También sugiero encapsular el método remove como parte de tu clase poco para hacer que el significado sea más claro. vea this artículo para más detalles sobre eso.
public class Project
{
public int ProjectId {get; set;}
public virtual Employee Employee {get;set;}
public void RemoveEmployee()
{
Employee = null;
}
}