这是一个非常模糊/主观的问题。我想知道这是否是使用ajax调用向/从浏览器发送/检索数据的最佳方式。在后端webservice上,我想使用实体框架。以下是两个示例函数。
“最佳”的标准是编写代码的速度,可读代码和强大的架构。
感谢您提供反馈意见和建议。
获取功能
[WebMethod]
public AjaxEmployee EmployeeGetById(int employeeID, bool getTimeOff)
{
using (Time_TrackerEntities ctx = new Time_TrackerEntities())
{
var results = from item in ctx.Employees
where item.ID == employeeID
orderby item.Last_Name
select new AjaxEmployee
{
ID = item.ID,
Employee_ID = item.Employee_ID,
First_Name = item.First_Name,
Middle_Name = item.Middle_Name,
Last_Name = item.Last_Name,
Supervisor_ID = item.Supervisor_ID,
Active = item.Active,
Is_Supervisor = item.Is_Supervisor
};
var emp = results.FirstOrDefault();
if (getTimeOff)
{
var results2 = from item2 in ctx.Time_Off
where item2.Employee_ID == emp.Employee_ID
select new AjaxTime_Off
{
ID = item2.ID,
Employee_ID = item2.Employee_ID,
Date_Off = item2.Date_Off,
Hours = item2.Hours
};
emp.Time_Off = results2.ToList<AjaxTime_Off>();
}
return emp;
}
}
保存功能
[WebMethod]
public bool EmployeeSave(AjaxEmployee emp)
{
using (Time_TrackerEntities ctx = new Time_TrackerEntities())
{
var results = from item in ctx.Employees
where item.ID == emp.ID
select item;
var myEmp = results.FirstOrDefault();
if (myEmp == null)
{
myEmp = new Employee();
ctx.Employees.AddObject(myEmp);
}
myEmp.Employee_ID = emp.Employee_ID;
myEmp.First_Name = emp.First_Name;
myEmp.Middle_Name = emp.Middle_Name;
myEmp.Last_Name = emp.Last_Name;
myEmp.Supervisor_ID = emp.Supervisor_ID;
myEmp.Active = emp.Active;
myEmp.Is_Supervisor = emp.Is_Supervisor;
return ctx.SaveChanges() > 0;
}
}
答案 0 :(得分:9)
有一些改进。
保存()方法 - 不要从左到右复制,使用EF内置逻辑
而不是:
myEmp.Employee_ID = emp.Employee_ID;
myEmp.First_Name = emp.First_Name;
myEmp.Middle_Name = emp.Middle_Name;
myEmp.Last_Name = emp.Last_Name;
myEmp.Supervisor_ID = emp.Supervisor_ID;
myEmp.Active = emp.Active;
myEmp.Is_Supervisor = emp.Is_Supervisor;
你可以这样做:
ctx.Employees.ApplyCurrentValues(emp)
。
这样做是在图中查找具有相同键的实体(因为您刚刚使用FirstOrDefault()
检索它),并使用您传入的实体覆盖标量值 - 这正是你的所作所为。
因此,如果添加任何额外的标量属性,您的7行将变为1,您将不必重构代码。请记住 - 仅适用于标量属性,而不适用于导航属性。
为什么要构建主键检索查询?只需将谓词用于SingleOrDefault()
而不是:
var results = from item in ctx.Employees
where item.ID == emp.ID
select item;
var myEmp = results.FirstOrDefault();
这样做:
var myEmp = ctx.Employees.SingleOrDefault(x => x.ID == emp.Id);
甚至更好,使用管道/过滤器技术:
var myEmp = ctx.Employees.WithId(emp.Id).SingleOrDefault();
其中WithId
是IQueryable<Employee>
扩展方法,它根据提供的员工ID过滤查询。这允许从存储库/ DAL中分离过滤/业务逻辑。它应该放在你的域模型中,这样你就可以有一个很好的流畅API来通过你的ORM查询你的域实体。
当您通过主键检索实体时,您应始终使用SingleOrDefault()
或Single()
,永远不要FirstOrDefault()
或First()
。如果它是主键 - 应该只有其中一个,所以如果存在多个,则应该抛出异常,这就是SingleOrDefault()
所做的。正如@Shiraz所提到的那样 - 你的FirstOrDefault()
会使下面的查询崩溃。使用<First/Single>OrDefault()
时,始终需要进行空检查。
您可以对Get方法进行相同的改进。
总的来说,您的代码没有任何功能上的错误 - 它只需要微妙的改进,空检查和异常处理。
我强烈推荐的唯一功能改进是将您的Web服务代码重构为通用存储库。因为代码非常简单,可以在任何实体中重复使用。 Web服务不应该关注事务,主键或EF逻辑。它甚至不应该引用EF DLL。将此逻辑封装在存储库后面并将持久性逻辑委托给那里(当然通过接口)。
在我上面提到的更改后,您的Web服务方法每行不应超过5-7行代码。
你的网络服务中有太多的智慧 - 它应该是愚蠢而持久的无知。
答案 1 :(得分:3)
我发现在数据协定上直接尝试使用我的实体通常是一个非常糟糕的主意。这是可能的,并且在某些情况下工作正常,但是只要我的对象模型变得有点复杂,我就会开始不得不担心对象图形。我不想这样做。
相反,这与客户端无关,但它同样适用于JS客户端,我尝试将数据合同类视为纯数据卡车(DTO),而EF根本没有映射。那些课程只是他来回传递的文件,如果你愿意,那就是信息正文。它们可能会转换为我的模型上的命令,或者它们可能用于填充查询等等,但它们本身并不是实体。
我发现,这简化了很多事情。当你第一次编写一个简单的服务时,它可能会感觉更像代码,但在一生中它使事情变得更加可维护。
作为旁注,你还应该考虑更好地分担你的责任。 Web服务类不应该负责直接创建adn处理数据上下文,它应该依赖于DAO或存储库接口(或域服务)为您处理所有这些东西(并根据需要应用事务等)。
答案 2 :(得分:0)
你的get方法可能会崩溃。
如果此行返回null:
var emp = results.FirstOrDefault();
然后这一行会因空引用异常而崩溃:
where item2.Employee_ID == emp.Employee_ID
我还会尝试使用catch块来记录错误。