C# 什么';这是EF的基本概念,即I';我失踪了?
我肯定我误解了EF5的一些基本原理 在[上一个问题]中,我询问了如何在ASP.NET MVC应用程序中的操作之间传递值,有人建议我可以使用TempData作为传递数据的机制(在我的例子中,我选择了在EF中表示我的数据模型的POCO) 我在MVC中的控制器不知道EF中有任何持久性机制。它们利用我称之为“管理器”的服务层在我的POCO上执行常见任务,并将它们读取/持久化到底层数据存储 我正在编写一个工作流,允许我的网站的“员工”取消“离职请求”。在控制器和操作方面,有一个HttpGet操作“CancelLeaveRequest”,它获取有问题的LeaveRequest的ID,通过服务层检索LeaveRequest,并显示一些详细信息、警告和确认按钮。在控制器返回相关视图之前,它将LeaveRequest实体提交到TempData中,以便在下一步中提取 确认按钮导致HttpPost“LeaveRequest”,然后使用来自TempData的LeaveRequest和对服务层的调用对LeaveRequest进行更改,并使用EF将其保存回数据库 在我的代码中,manager类的每个实例都有自己的EF DBContext。MVC中的控制器实例化一个管理器,并在页面生命周期内处理它。因此,使用DBContext的一个实例检索LeaveRequest,并通过另一个实例进行更改和提交 我的理解是,当第一个DBContext超出范围时,实体就会“分离”。因此,当我尝试提交对第二个DBContext的更改时,我必须使用DBContext.LeaveRequests.attach()将实体附加到上下文?还有一个更复杂的问题,我需要使用“Employee”实体来记录哪个员工取消了休假请求 我在服务层中用于取消请假请求的代码如下所示C# 什么';这是EF的基本概念,即I';我失踪了?,c#,asp.net-mvc,entity-framework,entity-framework-5,code-first,C#,Asp.net Mvc,Entity Framework,Entity Framework 5,Code First,我肯定我误解了EF5的一些基本原理 在[上一个问题]中,我询问了如何在ASP.NET MVC应用程序中的操作之间传递值,有人建议我可以使用TempData作为传递数据的机制(在我的例子中,我选择了在EF中表示我的数据模型的POCO) 我在MVC中的控制器不知道EF中有任何持久性机制。它们利用我称之为“管理器”的服务层在我的POCO上执行常见任务,并将它们读取/持久化到底层数据存储 我正在编写一个工作流,允许我的网站的“员工”取消“离职请求”。在控制器和操作方面,有一个HttpGet操作“Canc
public void CancelLeaveRequest(int employeeId, LeaveRequest request)
{
_DBContext.LeaveRequests.Attach(request);
request.State = LeaveRequestApprovalState.Cancelled;
request.ResponseDate = DateTime.Now;
using (var em = new EmployeesManager())
{
var employee = em.GetEmployeeById(employeeId);
request.Responder = employee;
_DBContext.Entry(request.Responder).State = System.Data.EntityState.Unchanged;
}
_CommitDatabaseChanges();
}
您可以看到,我从EmployeesManager中检索了一个雇员实体,并将该雇员指定为休假请求的响应者
在我的测试用例中,请假请求的“响应者”与请假请求的另一个属性“请求者”是同一个雇员。请假请求和提出请求的员工之间的关系是多对一的,请假请求和作出响应的员工之间的关系是多对一的
当我的代码在当前状态下运行时,出现以下错误:
AcceptChanges无法继续,因为对象的键值与ObjectStateManager中的另一个对象冲突。在调用AcceptChanges之前,请确保键值是唯一的
我怀疑这是因为英孚认为它已经知道有关员工的情况。失败的线路是:
_DBContext.Entry(request.Responder).State = System.Data.EntityState.Unchanged;
但是,如果我删除了这一行,并且不想通过告诉EF不要更改我的员工对象来表现得聪明,那么请假请求会像预期的那样被取消,但我的员工会发生一些非常奇怪的事情
首先,提出/响应请求的员工是重复的。然后,任何导航属性(如“经理”,员工和其他员工之间的多对一关系)似乎也会被复制。我可以理解,在Employee上复制Manager属性是因为我正在加载Manager对象图,作为GetEmployeeById
的一部分,并且我认为我可以理解原始员工正在被复制,因为就LeaveRequest DBContext而言,它只是突然出现(我通过另一个DBContext检索了该员工)。但是,假设这两点是正确的,我不知道如何才能a)防止该员工及其关联的对象图在数据库中重复,以及b)如何确保修改后的LeaveRequest正确持久化(在员工和请假申请中,似乎停止了附加、将状态更改为修改等各种组合)
有人能指出我的错误吗
我的请求实体:
public class LeaveRequest
{
public LeaveRequest()
{
HalfDays = new List<LeaveRequestHalfDay>();
}
public int CalculatedHalfDaysConsumed { get; set; }
public Employee Employee { get; set; }
public virtual ICollection<LeaveRequestHalfDay> HalfDays { get; set; }
public int LeaveRequestId { get; set; }
public DateTime RequestDate { get; set; }
public int ResponderId { get; set; }
public virtual Employee Responder { get; set; }
public DateTime? ResponseDate { get; set; }
public LeaveRequestApprovalState State { get; set; }
public LeaveRequestType Type { get; set; }
public ICollection<LeaveRequest> ChildRequests { get; set; }
public LeaveRequest ParentRequest { get; set; }
}
公共类请求
{
公众假期申请()
{
半天=新列表();
}
public int CalculatedHalfDaysConsumed{get;set;}
公共雇员雇员{get;set;}
公共虚拟ICollection半天{get;set;}
公共请求ID{get;set;}
公共日期时间请求日期{get;set;}
public int ResponderId{get;set;}
公共虚拟员工响应程序{get;set;}
公共日期时间?响应日期{get;set;}
public LeaveRequestApprovalState状态{get;set;}
public LeaveRequestType类型{get;set;}
公共ICollection子请求{get;set;}
public LeaveRequest ParentRequest{get;set;}
}
“Employee”字段(类型为Employee…)是提交请求的人。“Responder”可能是不同的,但可能是相同的Employee。您应该将导航属性更改为:
public int ResponderId {get;set;}
public virtual Employee Responder { get; set; }
此标量属性将通过EF自动映射到导航属性。接下来,您可以简单地执行以下操作(并且不需要未更改的状态):
另请参阅关于EF中的关系。我认为您的模型缺少外键。能否显示LeaveRequest
类?更新的问题。我是否需要LeaveRequest上的ResponderId int字段?我更改了LeaveRequest的实体,但EF希望添加:ALTER TABLE[dbo].[LeaveRequests]add[ResponderId][int]NOT NULL默认值0我还向映射中添加了以下内容:this.HasOptional(x=>x.Responder)。WithMany().Map(x=>x.MapKey(“ResponderID”);将测试并确认,谢谢!再次更新问题。我现在有了一个ResponderID int字段,Responder现在是虚拟的。代码已更改为只附加LeaveRequest,而不是我设置的Employee
var employee = em.GetEmployeeById(employeeId);
request.ResponderId = employee.Id;