Hi everyone,
I'm having a little trouble with my Entity add functionality.
Setup
For simplicity, I have in DB with 4 tables, Customer, Address, Area, Location.
- Customer stores basic customer details ie. Id, Name, Username, Password, etc.
- Address stores street name, street number, etc.
- Area stores area name and area code.
- Location stores location name and location code.
The relationship is as follows Customer *--->1 Address *--->1 Area *--->1 Location
In my application, I would have a function to create a new customer. There are also some textboxes available for the address information. The Area and Location information can be selected from a list of available Areas and Locations which I have pre-fetched from the database and populated into a ComboBox.
My application does not use the ADO.net EF directly, but via a WCF data service call. The service sits on the SQL server hosted by IIS. Here is the service call to retrieve Customers in the Business Logic Layer
/// <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();
}
}
As you can see. This will indefinitely lead to multiple instances of the Context (as mentioned by Craig Stuntz that this is a mistake).
Then in the WCF data service.
// IDataService.cs
[OperationContract]
List<Customer> GetAllCustomers();
// DataService.svc.cs
public List<Customer> GetAllCustomers()
{
return CustomerBLL.GetAll();
}
The application proceeds to use the list of Customers from the above retrieval, but ops to Create a new Customer like so:
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};
}
Where Location, Area, Address and Customer is the reference to the ADO.net EF model classes. The newLocation and newArea is then modified when the user selects a Location or Area from a list in the ComboBox like so:
private Location _selectedLocation;
public Location SelectedLocation
{
get { return _selectedLocation; }
set {
_selectedLocation = value;
newLocation = _selectedLocation;
}
}
SelectedLocation is data bound to the combo box SelectedItem of the locations combo box. A similar process happens to newArea.
When the User clicks save the following happens:
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;
}
The method that gets called in the data service is like so.
/// <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;
}
}
The Problem
This would throw an error:
The object cannot be added to the ObjectStateManager because it already has an EntityKey. Use ObjectContext.Attach to attach an object that has an existing key.
This is because the Customer.Address.Area
already exists in the context and its asking me to use context.Attach()
first. I've tried context.Attach() to no avail. What is the approach I should be taking to takle this problem?