Loading presentation...

Present Remotely

Send the link below via email or IM

Copy

Present to your audience

Start remote presentation

  • Invited audience members will follow you as you navigate and present
  • People invited to a presentation do not need a Prezi account
  • This link expires 10 minutes after you close the presentation
  • A maximum of 30 users can follow your presentation
  • Learn more about this feature in our knowledge base article

Do you really want to delete this prezi?

Neither you, nor the coeditors you shared it with will be able to recover it again.

DeleteCancel

Make your likes visible on Facebook?

Connect your Facebook account to Prezi and let your likes appear on your timeline.
You can change this under Settings & Account at any time.

No, thanks

Entity Framework Code First: Working With Disconnected Entities

Working with disconnected entities in Entity Framework Code First
by

Ian Lovell

on 16 November 2012

Comments (0)

Please log in to add your comment.

Report abuse

Transcript of Entity Framework Code First: Working With Disconnected Entities

Working With Disconnected Entities Entity Framework Code First Disconnected Entities Entities that are not being tracked by a context are known as disconnected entities.
For most single-tier applications, where the user interface and database access layers run in the same application process, you will most likely perform operations on entities that are being tracked by the context.
In N-tier applications, operations on disconnected entities is far more common.
This involves fetching some data on a server, returning it over a network to a client machine, manipulating the data on the client, and then persisting it back to the server Change Tracking States Added
The entity is tracked by the context, but doesn't exist in the database - INSERT
Unchanged
Exists in the database and hasn't been changed locally - not processed
Modified
Already exists in database been modified locally - UPDATE
Deleted
Exists in the database, been marked for deletion locally - DELETE
Detached
The entity is not being tracked by the context Painting the state - example private void SaveDepartmentAndEmployees(Department department, List<Employee> deletedEmployees)
{
using (var context = new MyContext())
{
context.Departments.Add(department);
}

if (department.Id > 0)
{
context.Entry(department).State = EntityState.Modified;
}

foreach (var employee in department.Employees)
{
if (employee.Id > 0)
{
context.Entry(employee).State = EntityState.Modified;
}
}

foreach (var employee in deletedEmployees)
{
context.Entry(employee).State = EntityState.Deleted;
}

context.SaveChanges();
} Tracking individual properties If you wish to track changes at the property level rather than at the entity level, then you will also need to develop a generic solution to handle this.
The options include:
Keeping track of modified properties on the client side and passing that list to the server along with the entities
Storing the original properties into the entities when they are retrieved from the database before passing them onto the client
Re-querying the database when the entities have been returned to the server from the client Graph of Entities When it comes to persisting data on the server, you are typically working with a graph of entities.
This is a number of entities that reference each other.
In N-tier scenarios this graph is usually disconnected from the context
The entity that you perform the operation on is known as the root of the graph.
Performing an operation on the root of a disconnected graph can have side effects on the rest of the graph too. Registering an entity with the context When an entity is registered with the context, it means that the context becomes aware of the entity and starts tracking it.
You can get access to change tracking information that Entity Framework has by using the DbContext.Entry method
One of the properties of the change tracking information is the State property which gives you the current state of the entity (Added, Deleted, Unchanged etc)
Entity Framework uses this state information to determine which SQL command to fire for each entity and its relationships Registering an entity with the context When you add on a disconnected entity, EF will also recursively add all other disconnected entities that are reachable from the root entity.
If a reference is found to an entity that is already tracked by the context, that entity's current state is left alone
You can see the problems that may arise in an N-tier application when EF hasn't been tracking the changes.
You need to tell EF of all the change tracking information yourself
There are a number of different approaches to solving the challenges associated with building N-Tier applications N-Tier options Use an existing framework
WCF Data Services
Microsoft's solution to the N-Tier challenge
Uses OData protocol
CSLA.NET
Business object framework developed by Rockford Lhotka supports
Has the concept of 'mobile objects'
These maintain persistence state (IsNew, IsDirty)
Self-Tracking Entities
This was a template for Model First and Database First which produced self tracking entities
Now obsolete N-Tier options Use explicit operations on the server side
Expose separate methods for adding each type of entity, rather than graphs of entities
Increases the quantity of code
Makes server side code easier write, test, and debug, but client side code more complex
Replay changes on the server
Pass an object graph to the server, but replay the changes, this is called 'painting the state' of the object graph
The server needs to know how to get the state information out of the object graph passed to it from the client Marking an entity as added var employee = new Employee { Name = "Fred" };

using (var context = new MyContext())
{
context.Employees.Add(employee);
context.SaveChanges();
}

using (var context = new MyContext())
{
context.Entry(employee).State = EntityState.Added;
context.SaveChanges();
} Marking an entity You can also use the Entry method to set the State property to other state values
If you set it to Modified, then EF will fire an UPDATE statement on SaveChanges, updating every column for that entity (as it doesn't know which properties were changed)
If you set the state to Unchanged, then EF will not process the entity on SaveChanges
You can achieve the same effect by using the Attach method on DbSet
To mark an entity for deletion, you can use the DbSet.Remove method. If you attempt to Remove an entity that isn't tracked, then an InvalidOperation exception will occur, so use the Attach method first so that the entity is tracked by the context.
Alternatively, you can set the state of the entty to EntityState.Deleted Using a stub to mark for deletion private void DeleteEmployee(int employeeId)
{
using (var context = new MyContext())
{
var employee = new Employee { Id = employeeId };
context.Entry(employee).State = EntityState.Deleted;
context.SaveChanges();
}
} Setting the State for Multiple Entities in an Entity Graph Add Root
Every entity in graph will be change tracked and marked with Added state
SaveChanges will attempt to insert data that may already exist in the database
Attach Root
Every entity in graph will be change tracked and marked with Unchanged state (i.e. an existing entity)
New entities will not get inserted into database and have EF will ensure that all entities have a unique key, therefore a conflict will occur
Add or Attach Root, then paint state throughout graph
Entities will have correct state when painting is complete
It is recommended to use Add to avoid key conflicts A generic approach to tracking state locally To be able to paint the state for any object graph, you need to develop a generic approach to track changes in your entities
One technique is to have all your entities implement a common interface which includes the entities current state: public interface IObjectWithState
{
State State { get; set; }
}

public enum State
{
Added,
Unchanged,
Modified,
Deleted
} Defaulting entities to the Unchanged state Any entries retrieved from the database should have their state set to 'Unchanged' by default.
Remember in an N-tier environment, the entities will be retrieved on the server, and sent back to the client
To be able to mark all entries automatically on retrieval, you can listen to the ObjectMaterialized event on the underlying ObjectContext. public MyContext()
{
((IObjectContextAdapter)this).ObjectContext
.ObjectMaterialized += (sender, args) =>
{
var entity = args.Entity as IObjectWithState;
if (entity != null)
{
entity.State = State.Unchanged;
}
};
} Painting the state generically private void ApplyChanges<TEntity>(TEntity root)
where TEntity : class, IObjectWithState
{
using (var context = new MyContext())
{
context.Set<TEntity>().Add(root);
foreach (var entry in context.ChangeTracker.Entries<IObjectWithState>())
{
IObjectWithState stateInfo = entity.Entity;
entry.State = ConvertState(stateInfo.State);
}

context.SaveChanges();
}
} Maintain a list of changed properties We only need to know if a property value has been modified or not
One option would be to maintain a list of changed property names on your common entity interface
It's the responsibility of the client to keep this list up to date
On the server, the Property method can be used to get information about an entity's property, and it's IsModified boolean property can be set if (stateInfo.State == State.Modified)
{
entry.State = EntityState.Unchanged;
foreach (var property in stateInfo.ModifiedProperties)
{
entry.Property(property).IsModified = true;
}
} Recording original values An alternative option is rather than have the client track modified properties, the server stores the original values within the entity before sending it to the client
This makes the client code much simpler and less error prone
This could be part of the common entity interface, for example a Dictionary<string, object> OriginalValues property.
The ObjectMaterialized event handler can be updated to populate the OriginalValues property automatically on retrieve
When the server comes to paint the state of the object graph, it can copy the OriginalValues on the common entity interface over the top of the entry's original values collection.
Entity Framework will compare this OriginalValues collection on the entry (a DbPropertyValues) with the CurrentValues on the entry, and if any changes are found it will just send an UPDATE command for those properties.
You have to deal with recursion as the entity may have complex values Querying and Applying Changes Another approach is to calculate the modified properties by querying the database at the point the server tries to apply the changes
The incoming values are then copied over the current entity, where Entity Framework will then take care of detecting the differences
Because this approach requires another query to select the entity, it's usually slower than just modifying the entity as modified and updating every column
Entity Framework makes it easy to copy the values from one object to another Querying and Applying Changes Example public void UpdateDepartment(Department department)
{
using (var context = new MyContext())
{
if (department.Id > 0)
{
var existingDepartment = context.Departments.Find(department.Id);
context.Entry(existingDepartment)
.CurrentValues
.SetValues(department);
}
else
{
context.Departments.Add(department);
}

context.SaveChanges();
}
} Problems with this Approach This approach falls down when working with object graphs
When an entity is added to the context, any entities that it references are also placed in the added state
This means that Entity Framework will attempt to add entities that may already exist in the database
Rather than getting the existing entity from the database and copying the values to it, you can add the root entity, paint the state of the object graph, and for each entity, if the state is unchanged (i.e. hasn't been added or removed, but potentially modified) then set its original values to the values from the database entry.OriginalValues.SetValues(entry.GetDatabaseValues()); The End
Full transcript