我的实体添加功能有点麻烦。
设置
为简单起见,我在DB中有4个表,Customer,Address,Area,Location。
关系如下 客户* ---> 1地址* ---> 1区* ---> 1位置
在我的应用程序中,我将有一个创建新客户的功能。还有一些文本框可用于地址信息。区域和位置信息可以从我从数据库中预先获取并填充到ComboBox的可用区域和位置列表中选择。
我的应用程序不直接使用ADO.net EF,而是通过WCF数据服务调用。 该服务位于IIS托管的SQL服务器上。以下是在业务逻辑层中检索客户的服务调用
/// <summary>
/// The get all customers.
/// </summary>
/// <returns>
/// </returns>
public static List<Customer> GetAll()
{
using (var context = new CustomerDataEntities())
{
// I may be missing something here.. Possibly a call to Detach().
// I have been ommitting it because a call to Detach()
// tends to cut away any .Include() from the data. Maybe there is another way?
return context.CustomerSet.ToList();
}
}
如你所见。这将无限期地导致上下文的多个实例(如Craig Stuntz所述,这是一个错误)。
然后在WCF数据服务中。
// IDataService.cs
[OperationContract]
List<Customer> GetAllCustomers();
// DataService.svc.cs
public List<Customer> GetAllCustomers()
{
return CustomerBLL.GetAll();
}
应用程序继续使用上述检索中的Customers列表,但是可以像这样创建一个新客户:
private Location newLocation;
private Area newArea;
private Address newAddress;
private Customer newCustomer;
// Code omitted for clarity..
public void CreateCustomer()
{
newLocation = new Location();
newArea = new Area{Location = newLocation};
newAddress = new Address{Area = newArea};
newCustomer = new Customer{Address = newAddress};
}
位置,区域,地址和客户是ADO.net EF模型类的参考。当用户从ComboBox中的列表中选择一个位置或区域时,将修改newLocation和newArea,如下所示:
private Location _selectedLocation;
public Location SelectedLocation
{
get { return _selectedLocation; }
set {
_selectedLocation = value;
newLocation = _selectedLocation;
}
}
SelectedLocation是绑定到位置组合框的组合框SelectedItem的数据。类似的过程发生在newArea。
当用户点击保存时,会发生以下情况:
public bool SaveCustomer(Customer customer)
{
if(customer == null) return false;
var dataService = new DataService.DataServiceClient();
try
{
if (customer.ID > 0) dataService.UpdateCustomer(customer);
else dataService.CreateCustomer(customer);
}
catch (Exception e)
{
Trace.WriteLine(e.Message);
return false;
}
return true;
}
在数据服务中调用的方法就是这样。
/// <summary>
/// The create.
/// </summary>
/// <param name="customer">
/// The customer.
/// </param>
public static bool Create(Customer customer)
{
if (customer== null) return false;
// Once again, a new instance of the Context..
using (var context = new CustomerDataEntities())
{
try
{
context.AddToCustomerSet(customer);
context.SaveChanges();
}
catch (Exception e)
{
return false;
}
return true;
}
}
问题
这会引发错误:
无法将对象添加到 ObjectStateManager因为它已经存在 有一个EntityKey。使用 ObjectContext.Attach附加一个 具有现有密钥的对象。
这是因为Customer.Address.Area
已存在于上下文中,并要求我先使用context.Attach()
。我尝试过context.Attach()无济于事。我应该采取什么方法来解决这个问题?
答案 0 :(得分:1)
我不会尝试针对这一问题进行特定修复,而是敦促您为ObjectContexts的生命周期提出一般策略。我不能给你一个“最佳实践”,因为这取决于你的应用。例如,我主要做ASP.NET MVC应用程序,因此我的ObjectContexts的生命周期总是一个上下文,只持续一个请求。
这里的一般问题是实体框架将期望对象图中的任何对象(换句话说,直接或直接引用彼此的对象)将是单个ObjectContext的一部分。例如,如果你有一个名为Area的对象,它附加到某个ObjectContext,而一个名为Customer的对象没有附加到任何ObjectContext(换句话说,它是分离的),你更改了Customer,以便它间接引用区域通过地址,然后如果您查看客户的EntityState,您将看到它现在“已添加”。尝试通过AddToCustomerSet将其添加到不同的ObjectContext将抛出您报告的错误,因为实体不能存在于两个ObjectContexts中。另一方面,在前一个上下文中调用SaveChanges将导致Customer被添加到数据库中,而根本没有调用AddToCustomerSet,因为它已经隐式添加到集合中。
这可能听起来很复杂,或者难以管理。但是你会发现,如果你能找到一种方法来使用单个ObjectContext,那么几乎所有这些复杂功能都会消失。没有反对使用多个ObjectContexts的规则,但如果您是Entity Framework的新用户,我会建议您在以后更加熟悉EF时不要这样做。