I'm using for the first time Entity Framework in a complex grid databinding situation and i need some strategies to accomplish this task. I've got these POCO classes autogenerated by "EF POCO reverse code first" (the same for mappings) and for each one there's also a repository that i use to query or do simple CRUD operations:
public class Order
{
public int IdOrder { get; set; }
public int IdCustomer { get; set; }
public int IdOrderType {get; set;}
public string ChOrder { get; set; }
public decimal Total { get; set; }
public Customer Customer { get; set; }
public OrderType OrderType {get; set;}
}
public class OrderType
{
public int IdOrderType {get; set;}
public string ChOrderType {get; set;}
public string Description { get; set; }
}
public class Customer
{
public int IdCustomer { get; set; }
public string ChCustomer { get; set;}
public string Name { get; set; }
public string City { get; set; }
public CustomerType CustomerType { get; set;}
}
public class CustomerType
{
public int IdCustomerType { get; set; }
public string ChCustomerType { get; set; }
}
Now i'd like to query OrderRepository and show all results in a DataGridView (in real case, its the DevExpress XtraGrid). This DataGridView must show "direct" Order informations (ChOrder, Total) but also "indirect" informations such the ones reachable by using navigation properties (ex. Customer.Name, Customer.City, Customer.CustomerType.ChCustomerType). What i want is a thing like that:
In a "Readonly DataGridView" scenario, this task is fairly simple. You only have to create a linq query that flattern the resultset of the repository and bind it to the grid like that:
this.OrderRepository.AsQueryable().Select(x => new { .ChOrder = x.ChOrder,
.Total = x.Total,
.ChOrderType = x.OrderType.ChOrderType,
.ChCustomer = x.ChCustomer,
.Name = x.Customer.Name,
.City = x.Customer.City,
.CustomerType = x.Customer.CustomerType}).ToList();
But the grid must have to be editable. In particular i'd like to add new orders, modify ChOrderType (and consequently its description), modify the ChCustomer (and consequently his Name, City and Type) and delete orders. I cannot bind LINQ result because:
- LINQ anonymous type result are immutable (so the grid would be readonly)
- LINQ generates a list where items can change (user can delete items, add new one, modify etc..) and, because of it is a list, it can't track wheter items has been deleted, added and modified (even if i use a better suitable BindingList or ObservableCollection). So i won't know which entities delete, update or insert.
- LINQ result item does not implement INotifyPropertyChanged, so, if i change for example "ChCustomer" its relative data (Name, City ec..) won't be updated on the screen.
Due to those problems, i tought to bind directly the POCO OrderClass in this way:
this.OrderRepository.AsQueryable().Select()
And set "indirect" fields "DataPropertyName" like that:
Column "Name" -> Customer.Name
Column "City" -> Customer.City
But again, it doesn't fit because:
My POCO classes does not implement INotifyPropertyChanged, so, if i change for example "ChCustomer" its relative data (Name, City ec..) won't be updated on the screen.
When i edit for example ChCustomer, obviously databinding doesn't know how to get a Customer instance from its repository according on the ChCustomer the user has inserted and assign it to the current Order "Customer" property.
Sometimes editable fields (as ChCustomers) are part of composite keys and actually the edit of a composite key of an entity is not allowed in EF. This introduces more problems.
So, at the end of the story, it turns out that it is a difficult task to manage when EF is involved with databinding (and in our software there are thousand case like this).
I'd like to know if there is a good strategy to accomplish this task.
I tought about how to handle this by using a ViewModel (that also implement INotifyPropertyChanged) object to bind to the gridView like that:
public class OrderViewModel : INotifyPropertyChanged{
public event PropertyChangedEventHandler PropertyChanged;
//Order info
public string ChOrder { get; set; }
public decimal Total {get; set;}
public string ChOrderType {get; set;}
public string Description { get; set; }
//Customer info
public string ChCustomer { get; set; }
public string Name { get; set;}
public string City { get; set;}
public string ChCustomerType {get; set;}
}
It works very well, but i don't know how to sync data with repositories. In particular how to track deleted items and delete them using repository? how to track added items? And the edit changes? And also...how to set all those properties that comes from navigation properties (such as name, city and so on)? When i call SaveChanges() of my UnitOfWork i'd like to have all my chages correctly persisted to DB.
Sorry if i talk lenght, but i tried to be the clearest i can. Thanks in advance.