繁体   English   中英

首先使用实体​​框架代码时将外键设置为空

[英]Setting a foreign key to null when using entity framework code first

我使用 Entity Framework Code First 的数据库优先实现作为项目的数据层,但我遇到了一个问题。

我需要能够将外键设置为 null 以删除数据库中的关联。

我有 2 个对象。 一种叫做项目。

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;}
}

这与我在数据库中的内容相匹配:

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
)

我可以为一个项目分配一个员工。 但是,我希望能够从项目中删除一名员工,并让 Employee 字段为空。 在我的用户界面中,这将显示为“未分配员工”。

但是,缺少直接的 sql 查询,我似乎无法在实体框架 4.1 中找到执行此操作的方法。

我试过了:

public void RemoveEmployeeFromProject(int projectId)
{
    var project = Context.Projects.FirstOrDefault(x => x.ProjectId == projectId);
    project.Employee = null;
    Context.SaveChanges();
}

但这没有任何作用。

有没有人有任何想法?

我认为问题在于,就上下文而言,您实际上并没有改变任何东西。

您可以使用之前通过使用virtual建议的延迟加载方法,但由于您尚未请求加载 Employee ,它仍然为空。 你可以试试这个:

var forceLoad = project.Employee;
project.Employee = null; // Now EF knows something has changed
Context.SaveChanges();

或者,明确将其包含在您的原始请求中:

var project = Context.Projects.Include(x => x.Employee).FirstOrDefault(x => x.ProjectId == projectId);
project.Employee = null;
Context.SaveChanges();

附带说明一下,如果没有Project与给定的 ID 匹配,则FirstOrDefault将返回null 如果您知道该项目存在,则可以使用First 您甚至可以使用Single ,它会断言只有一个这样的项目。 如果您继续使用FirstOrDefault ,我建议在使用project之前检查null

您可以这样做,这意味着您不必加载相关实体。

context.Entry(Project).Reference(r => r.Employee).CurrentValue = null;

答案很简单。 鉴于您提供的信息,EF 无法推断类型。

只需这样做:

public void RemoveEmployeeFromProject(int projectId)
{
    var project = Context.Projects.FirstOrDefault(x => x.ProjectId == projectId);
    project.EmployeeId = (int?)null;
    Context.SaveChanges();
}

它会起作用。

您需要在 linq 查询中包含要分配的属性,使用与 Project 类中相同的名称:

var project = Context.Projects.Include("Employee").FirstOrDefault(x => x.ProjectId == projectId);

作为另一种解决方法,我将两个方法编译为一个扩展方法:

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;
}

这允许您提供一个 DbContext(如果有),在这种情况下它将使用最有效的方法并将条目引用的 CurrentValue 设置为 null。

entity.SetToNull(e => e.ReferenceProperty, dbContext);

如果没有提供 DBContext,它将首先延迟加载。

entity.SetToNull(e => e.ReferenceProperty);

如果您通过将员工属性设为虚拟来启用延迟加载,它会起作用吗?

public class Project
{
    public int ProjectId {get; set;}
    public virtual Employee Employee {get;set;}
}

我还建议将 remove 方法封装为 poco 类的一部分,以使含义更清晰。 看到这个文章,对更多的细节。

public class Project
{
    public int ProjectId {get; set;}
    public virtual Employee Employee {get;set;}
    public void RemoveEmployee()
    {
        Employee = null;
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM