Ado Entity Framework添加数据

时间:2009-10-21 08:23:36

标签: c# entity-framework ado.net

我的实体添加功能有点麻烦。

设置

为简单起见,我在DB中有4个表,Customer,Address,Area,Location。

  • 客户存储基本客户详细信息,即。 ID,姓名,用户名,密码等
  • 地址存储街道名称,街道号等
  • 区域存储区域名称和区域代码。
  • 位置存储位置名称和位置代码。

关系如下 客户* ---> 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()无济于事。我应该采取什么方法来解决这个问题?

1 个答案:

答案 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时不要这样做。