实体框架在创建父对象时创建新的子对象

时间:2017-10-27 08:55:02

标签: c# database entity-framework

我有2个班级,作为一对多的关系。这种关系的结构如下:

public partial class Department
    {
        public Department()
        {
            this.Employees = new HashSet<Employee>();
        }

        public int DepartmentId { get; set; }
        public string Name { get; set; }

        public virtual ICollection<Employee> Employees { get; set; }
    }


public partial class Employee
    {
        public int EmployeeId { get; set; }
        public int DepartmentId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }

        public virtual Department Department { get; set; }
    }

当我创建一个新的Employee对象并为其分配一个现有的部门时,EF会在我的数据库中创建一个新的Employee对象以及一个新的部门记录,但这不是我想要的。我希望创建员工记录,但要引用现有的部门对象。这是我的商业代码:

IEmployeeBiz employeeBiz = new EmployeeBiz();
IDepartmentBiz departmentBiz = new DepartmentBiz();

Department department = new Department();
department.Name = "Testing";
departmentBiz.Add(department); // add to db

Employee employee = new Employee();
employee.FirstName = "James";
employee.LastName = "Bond";
employee.Department = department; // add existing department to new employee
employeeBiz.Add(employee); // add to db

如您所见,我将Department对象分配给employee.Department,此创建的DAL代码如下,我正在使用通用DAL:

public virtual void Add(T[] items)
{
    using (var context = new DbConnect())
    {
        foreach (T item in items)
        {
            context.Entry(item).State = EntityState.Added;
        }
        context.SaveChanges();
    }
}

我知道我可以通过在我的员工对象上分配departmentId来解决这个问题,但我希望我的代码可以在我将子Department对象分配给父Employee对象的地方工作。

我怎样才能以我想要的方式工作?

2 个答案:

答案 0 :(得分:0)

“部门”有一个“员工”集合,“员工”有“部门”属性。这是一个递归设计,不知道,如果EF可以处理这个。这看起来像是一个设计错误。

顺便说一句:使用GraphDiff在EF中非常优雅。这是一个没有详细记录,但非常有效的EF扩展用于更新整个图形。 https://www.nuget.org/packages/RefactorThis.GraphDiff/

答案 1 :(得分:0)

您的一对多关系配置不正确。您的员工需要为其所属的部门指定外键

class Employee
{
     public int EmployeeId {get; set;}

     // every employee belongs to exactly one Department
     public int DepartmentId {get; set;}
     public virtual Department Department {get; set;}

     ... // other properties, not meaningful for this question
}

请参阅configure one-to-many

如果您想介绍员工以及新部门,您可以按照以下方式执行此操作:

var addedEmployee = myDbcontext.Employees.Add(new Employe()
{
     FirstName = ...;
     ...

     Department = new Department()
     {
         Name = "My beautiful launderette",
     },
});

如果您想将新员工引入现有部门,您必须先获取此部门,或者只是分配DepartmentId

int departmentId = ...
var addedEmployee = myDbcontext.Employees.Add(new Employe()
{
     FirstName = ...;
     ...

     DepartmentId = departmentId,
});

如果您以不同方式获取现有部门,那么您应该拥有可以使用的ID:

Department existingDepartment = ..
var addedEmployee = myDbcontext.Employees.Add(new Employe()
{
     FirstName = ...;
     ...

     DepartmentId = existingDepartmenr.DepartmentId,
});

或者,但不可取,因为您不会轻易看到您是使用现有部门还是创建新部门:

Department someDepartment = ..
var addedEmployee = myDbcontext.Employees.Add(new Employe()
{
     FirstName = ...;
     ...

     Department = someDepartment,
});

实体框架将使用someDepartment.DepartmentId检测someDepartment是否已存在。那么为什么不自己使用呢?它只会保护您和未来的读者不会意外地创建新的部门。

最后:在Department的构造函数中创建Hashset是没用的。使用它时会立即丢弃:

// fetch some departments:
var result = myDbContext.Departments
    .Where(department => department.Name = ...)
    .ToList();

这里将创建一个带有空哈希集的部门,它将立即被从数据库中提取的虚拟ICollection所取代

// Introduce a Department with several employees:
var introducedDepartment = myDbContext.Departments.Add(new Department()
{
    Name = ...
    Employees = new List<Employee>()
    {
        new Employee() {Name = ... etc},
        new Employee() {Name = ... etc},
        new Employee() {Name = ... etc},
    },
});

这里创建了一个具有HashSet的部门。此哈希集会立即被丢弃,并替换为新的员工列表。

那么为什么要构造HashSet呢?