Entity Framework 6 Recipes Chinese translation series (44), entityframework
For the original intention of translation and why I chose Entity Framework 6 Recipes, see the beginning of this series.
8-4 Use the value object (Complex Type -- Also called composite Type) attribute in POCO
Problem
You want to use value objects in POCO.
Solution
Suppose you have a model 8-5. In the model, attribute Name is a value object.
Figure 8-5. A model with an employee ID. The attribute Name is a value object, which is composed of FirstName and LastName.
POCO supports value objects. When you reconstruct two or more object attributes to a value object, a new class is generated by default. This class is the type of the value object. An attribute of the type of this value object type is also added to the primary object. Only classes are supported, because the Entity Framework generates them when saving value objects. Code List 8-6 demonstrates that the Name attribute of the value object type is used to represent the employee's surname and Name.
Code List 8-6.Use Value objects in POCO
class Program { static void Main(string[] args) { RunExample(); } static void RunExample() { using (var context = new EFRecipesEntities()) { context.Employees.Add(new Employee { Name = new Name { FirstName = "Annie", LastName = "Oakley" }, Email = "aoakley@wildwestshow.com" }); context.Employees.Add(new Employee { Name = new Name { FirstName = "Bill", LastName = "Jordan" }, Email = "BJordan@wildwestshow.com" }); context.SaveChanges(); } using (var context = new EFRecipesEntities()) { foreach (var employee in context.Employees.OrderBy(e => e.Name.LastName)) { Console.WriteLine("{0}, {1} email: {2}", employee.Name.LastName, employee.Name.FirstName, employee.Email); } } Console.WriteLine("Enter input:"); string line = Console.ReadLine(); if (line == "exit") { return; }; } } public partial class Employee { public Employee() { this.Name = new Name(); } public int EmployeeId { get; set; } public string Email { get; set; } public Name Name { get; set; } } public partial class Name { public string FirstName { get; set; } public string LastName { get; set; } } public partial class EFRecipesEntities : DbContext { public EFRecipesEntities() : base("name=EFRecipesEntities") { } protected override void OnModelCreating(DbModelBuilder modelBuilder) { throw new UnintentionalCodeFirstException(); } public DbSet<Employee> Employees { get; set; } }
The output of code listing 8-6 is as follows:
Jordan, Bill email: BJordan@wildwestshow.comOakley, Annie email: aoakley@wildwestshow.com
Principle
When you use a value object in POCO, remember the following two items:
1. The value object must be a class;
2. Inheritance cannot be used for value objects;
In the object framework, change tracking cannot be used for value objects. Modification to the value object cannot be reflected in the change tracking. Note that if you mark a value object as virtual in its attributes, it will not be supported by the change tracking proxy. All change tracking is based on snapshots.
When you use a value object to delete an object that has not been loaded from the database, you need to create an instance of a value object. In an object framework, a value object instance is a part of an object and does not support null values. Code listing 8-7 demonstrates a method for processing the deletion.
Code List 8-7. delete an object with a value
int id = 0; using (var context = new EFRecipesEntities()) { var emp = context.Employees.Where(e => e.Name.FirstName.StartsWith("Bill")).FirstOrDefault(); id = emp.EmployeeId; } using (var context = new EFRecipesEntities()) { var empDelete = new Employee { EmployeeId = id, Name = new Name { FirstName = string.Empty, LastName = string.Empty } }; context.Employees.Attach(empDelete); context.Employees.Remove(empDelete); context.SaveChanges(); } using (var context = new EFRecipesEntities()) { foreach (var employee in context.Employees.OrderBy(e => e.Name.LastName)) { Console.WriteLine("{0}, {1} email: {2}", employee.Name.LastName, employee.Name.FirstName, employee.Email); } }
In code listing 8-7, we first find the employee ID of Bill Jordan. Because we want to demonstrate how to delete the Bill that is not previously loaded into the context object, we have created a new context object. This example deletes the Bill by specifying the employee ID of the Bill. We need to create an instance of the Employee entity. This is because the Name attribute cannot be blank. It does not matter what value is set for FirstName and LastName. We can meet this requirement by assigning a Name-type instance (Dummy) to the value object property. After the methods Attach (), Remove (), and SaveChanges () are called, the object is deleted.
8-5 Notification of object change
Problem
You are using POCO. When your object changes, you want to get notifications about the object framework and object state management.
Solution
Suppose you have a model 8-6.
Figure 8-6. A model containing entity donor and donation
This model represents the donors and their donations. Because some donations are anonymous, the relationship between donor and donation is 0 .. 1 *.
We want to modify entities. For example, we want to move a donation from one donor to another and get notifications about these changes from the Entity Framework and Object Manager. In addition, we want the Entity Framework to use these notifications to correct the relationships affected by changes. In the example, if the donor corresponding to the donation item is modified, we hope that the physical framework can correct the relationship between the two sides. Code List 8-8 demonstrates this.
The key part of code listing 8-8 is to mark all attributes as virtual and set the type of each set to ICollection <T>. In this way, the Entity Framework is allowed to create a proxy for each POCO object to implement change tracking in the proxy class. When we create a POCO object instance, the object framework dynamically creates a class derived from the object, which acts as the proxy of the object. This proxy overrides the attribute marked as virtual by the entity and adds some hooks. When an attribute is accessed, these hooks are automatically executed. This technology is used to implement delayed loading and object change tracking. Note that the Entity Framework does not generate a proxy for any entity that has no proxy. You can set an object to sealed or an attribute that does not contain virtual tags to avoid proxy generation.
Code List 8-8.Mark all attributes as virtual and set the type of each set to ICollection <T> to obtain the change tracking function of the proxy class.
1 class Program 2 {3 static void Main (string [] args) 4 {5 RunExample (); 6} 7 8 static void RunExample () 9 {10 using (var context = new EFRecipesEntities () 11 {12 var donation = context. donations. create (); 13 donation. amount = 5000 M; 14 15 var donor1 = context. donors. create (); 16 donor1.Name = "Jill Rosenberg"; 17 var donor2 = context. donors. create (); 18 donor2.Name = "Robert Hewitt"; 19 20 // donate to jill and save 21 donor1.Donations. add (donation); 22 context. donors. add (donor1); 23 context. donors. add (donor2); 24 context. saveChanges (); 25 26 // now donate to Rebert27 donation. donor = donor2; 28 29 // Report 30 foreach (var donor in context. donors) 31 {32 Console. writeLine ("{0} has given {1} donation (s)", donor. name, 33 donor. donations. count (). toString (); 34} 35 Console. writeLine ("Original Donor Id: {0}", 36 context. entry (donation ). originalValues ["DonorId"]); 37 Console. writeLine ("Current Donor Id: {0}", 38 context. entry (donation ). currentValues ["DonorId"]); 39} 40} 41} 42 public partial class Donation43 {44 public int DonationId {get; set;} 45 public Nullable <int> DonorId {get; set ;}46 public decimal Amount {get; set ;}47 48 public virtual Donor {get; set ;}49} 50 public partial class Donor51 {52 public Donor () 53 {54 this. donations = new HashSet <Donation> (); 55} 56 57 public int DonorId {get; set;} 58 public string Name {get; set ;} 59 60 public virtual ICollection <Donation> Donations {get; set;} 61} 62 public partial class EFRecipesEntities: DbContext63 {64 public EFRecipesEntities () 65: base ("name = EFRecipesEntities ") 66 {67} 68 69 protected override void OnModelCreating (DbModelBuilder modelBuilder) 70 {71 throw new UnintentionalCodeFirstException (); 72} 73 74 public DbSet <Donation> Donations {get; set ;} 75 public DbSet <Donor> Donors {get; set;} 76}
The output from Code List 8-8 is as follows:
Jill Rosenberg has given 0 donation(s)Robert Hewitt has given 1 donation(s)Original Donor Id: 1Current Donor Id: 2
Principle
By default, the Entity Framework uses a snapshot-based method to detect changes to POCO entities. If you change the code in the POCO object. The change tracking agent created by the object framework can synchronize the context.
Change tracking brings us two benefits: one is that the Entity Framework is notified of changes, which can keep the state information of the object graph synchronized with your POCO object. It means that the snapshot-based method does not need to take time to check for changes.
Another point is that when the Entity Framework receives a change notification from one side of the entities on both sides of the relationship, it can be reflected on the other side of the relationship if necessary. In code list 8-8, we move a donation item from one donor to another, and the Entity Framework can correct the collection of donations from two donors.
The Entity Framework must meet the following conditions to generate a change tracking proxy for the POCO object class.
1. The class must be Public, not abstract, not sealed;
2. attributes to be persistent must be virtual, and getter and setter are implemented;
3. You must set the set-based navigation attributes to ICollection <T>. They cannot be specific implementation classes, it cannot be another interface derived from ICollection <T>;
Once your POCO entity meets these requirements, the Entity Framework returns a proxy instance for your POCO entity. To Create an instance, use the Create () method in DbContext as in code listing 8-8. This method creates a POCO object instance, And it initializes all the sets as instances of EntityCollection. The POCO object set is used as an instance of Entitycollection because it can correct the relationship.
Entity Framework exchange QQ group: 458326058. You are welcome to join us.
Thank you for your continued attention, my blog address: http://www.cnblogs.com/VolcanoCloud/