Sunday, July 19, 2009

Disconnected Update with Entity Framework

In the first demo, we get disconnected entity, make change of it, and get copy of original copy from the database, and apply the changed entity to the original entity by using context.ApplyPropertyChanges method, and save it back to the database.

public void DemoDisconnectedUpdate1() { //using NoTracking to simulate the disconnected enviorment //or you can use context.Detach() to simulate that context.Contacts.MergeOption = MergeOption.NoTracking; var pendingContact = context.Contacts.Where(c => c.ContactID == 709).First(); //change pendingContact.FirstName = "somebody"; // ApplyChange1(pendingContact); } public void ApplyChange1(EntityObject pendingEntity) { context = new PEF(); context.GetObjectByKey(pendingEntity.EntityKey); context.ApplyPropertyChanges(pendingEntity.EntityKey.EntitySetName, pendingEntity); context.SaveChanges(); }

Unlike the first demo, in the second demo, we use a anonymous typed object to represent the change of the entity, and apply the change directly to the original version directly using reflection.

public void DemoDisconnectUpdate2() { EntityKey key = new EntityKey("PEF.Contacts", "ContactID", 709); var changes = new { FirstName = "xyz" }; UpdateEntity(key, changes); } public void UpdateEntity(EntityKey key, object changes) { var original = context.GetObjectByKey(key); ApplyChange(changes, original); context.SaveChanges(); } public void ApplyChange(object changes, object original) { Type newType = changes.GetType(); Type oldType = original.GetType(); var newProperties = newType.GetProperties(); foreach (var newProperty in newProperties) { var oldProperty = oldType.GetProperty(newProperty.Name); if (oldProperty != null) { oldProperty.SetValue(original, newProperty.GetValue(changes, null), null); } } }

Thursday, July 16, 2009

Reference and EntityKey

When add an entity to your objectContext, if the entity reference an other existing entity, but that entity is not in memory, you need to create a EntityKey like the following.

var address = new Address(); address.City = "SomeCity"; address.AddressType = "Home"; address.ModifiedDate = DateTime.Now; address.ContactReference.EntityKey = new EntityKey("PEF.Contacts", "ContactID", 709); context.AddToAddresses(address); context.SaveChanges();

Sunday, July 12, 2009

Naming in Entity Framework

When using the entity framework designer, you create you entity model with a naming convention. For example, a table "Customer" will map to a entity type "Customer" and entity set "CustomerSet" . It is very tempting to change the name of "CustomerSet" to to Customers. But what about Criterion, its plural forms is Criteria, what about Equipment, it is plural forms is also Equipment. I feel that the default naming convention is good enough, because it tells you it is a set and also my configuration is kept to minimum, isn't this the spirit of convention over configuraiton?

How ObjectContext manage entities

Those objects were created by an internal process called object materialization, which takes the returned data and builds the relevant objects for you. Depending on the query, these could be EntityObjects, anonymous types, or DbDataRecords. By default, for any EntityObjects that are materialized, the ObjectContext creates an extra object behind the scenes, called an ObjectStateEntry. It will use these ObjectStateEntry objects to keep track of any changes to their related entities. If you execute an additional query using the same context, more ObjectStateEntry objects will be created for any newly returned entities and the context will manage all of these as well. The context will keep track of its entries as long as it remains in memory. The ObjectContext can track only entities. It cannot keep track of anonymous types or nonentity data that is returned in a DbDataRecord.

ObjectStateEntry takes a snapshot of an entity's values as it is first created, and then stores the original values and the current values as two separate sets. ObjectStateEntry also has an EntityState property whose value reflects the state of the entity (Unchanged, Modified, Added, Deleted). As the user modifies the objects, the ObjectContext updates the current values of the related ObjectStateEntry as well as its EntityState.

The object itself also has an EntityState property. As long as the object is being managed by the context, its EntityState will always match the EntityState of the ObjectStateEntry. If the object is not being managed by the context, its state is Detached.

ObjectContext has a single method, SaveChanges, which persists back to the database all of the changes made to the entities. A call to SaveChanges will check for any ObjectStateEntry objects being managed by that context whose EntityState is not Unchanged, and then will use its details to build separate Insert, Update, and Delete commands to send to the database. ObjectContext can monitor the change of both entity and entity reference.

Pros and Cons of Load and Include

You have some things to consider when choosing between the Load and Include methods. Although the Load method may require additional round trips to the server, the Include method may result in a large amount of data being streamed back to the client application and then processed as the data is materialized into objects. This would be especially problematic if you are doing all of this work to retrieve related data that may never even be used. As is true with many choices in programming, this is a balancing act that you need to work out based on your particular scenario. The documentation also warns that using query paths with Include could result in very complex queries at the data store because of the possible need to use numerous joins. The more complex the model, the more potential there is for trouble.

You could certainly balance the pros and cons by combining the two methods. For example, you can load the customers and orders with Include and then pull in the order details on an as-needed basis with Load. The correct choice will most likely change on a case-by-case basis.

public static void DeferredLoadingEntityReference() { var addresses = from a in context.Addresses select a; foreach (var address in addresses) { if (address.CountryRegion == "UK") address.ContactReference.Load(); } } public static void EagerLoadWithInclude() { var test = from c in context.Contacts.Include("Addresses") where c.LastName == "Smith" select c; test.OuputTrace(); }

Debuging ObjectQuery

When you write query with ObjectQuery, it use IQueryable interface. Following extension function help your to debug ObjectQuery more easily.

public static class IQueryableExtenstion { public static ObjectQuery ToObjectQuery(this IQueryable query) { return query as ObjectQuery; } public static ObjectQuery<T> ToObjectQuery<T>(this IQueryable<T> query) { return query as ObjectQuery<T>; } public static string ToDatabaseSql(this IQueryable query) { try { return query.ToObjectQuery().ToTraceString(); } catch { return null; } } public static string ToEntitySql(this IQueryable query) { try { return query.ToObjectQuery().CommandText; } catch { return null; } } public static void OuputTrace(this IQueryable query) { Console.WriteLine(query.ToDatabaseSql()); Console.WriteLine(query.ToEntitySql()); } } //to use the extension function you can write the following code var test = from a in context.Addresses let c = new { a.Contact.FirstName, a.Contact.LastName, a.CountryRegion } group c by c.CountryRegion into mygroup where (mygroup.Count() > 150) select mygroup; test.OuputTrace();

Saturday, July 11, 2009

Seperate your EMD to 3 files

When you generate your EMDX, you medata data is embedded in the output assembly as resource files. So you you connection string looks like "metadata=res://*/Model1.csdl|res://*/Model1.ssdl|res://*/Model1.msl" . But you have an option to save your metadata in loose file, so that your connection string will be something like "metadata=.\Model1.csdl|.\Model1.ssdl|.\Model1.msl"

Object Service (ObjectContext, ObjectQuery and EntityObject)

The core objects of object service is ObjectContext and ObjectQuery and EntityObject. You can think of ObjectContext as entity repository. The repository is responsible to Insert/Update/Delete/Select entity.

To select entity, ObjectContext actually create ObjectQuery, which implement IQueryable. The object return from ObjectQuery, is not normal object, but EntityObject. Entity has EntityKey and EntityState.

Thursday, July 9, 2009

Domain-Driven Development with EF v1

Entity Framework version 1 is data-centric in the features it implements. Domain-driven development begins with the model, not the database. Many developers who embrace the tenets of domain-driven design will find the Entity Framework to be too restrictive. However, some of the advocates of this point of view are working with the Entity Framework team to enable version 2 to expand its capabilities so that you can use it with this approach.

Challenges with Change Tracking Distributed Applications

To put it mildly, using the Entity Framework in distributed applications can be challenging when it comes to the change tracking performed by Object Services, because the change-tracking information is not stored in the entities and instead is maintained by a separate set of Object Services objects. When an entity is transferred across a process, it is disconnected from the object that contains its change-tracking information. Those objects that own the tracking data are not serializable, so they can't easily be shipped across to the new process along with the entities. Therefore, when the entities arrive at the new process, they have no idea whether they are new or preexisting, or whether they have been edited or marked for deletion. There's no way to simply use the ObjectContext's default method for saving changes to the database without doing additional work.

Wednesday, July 8, 2009

Limitation of Entity Data Model Designer

The disigner does not support all the features of the EDM. With some of less frequently used EDM features, you will have to work with the XML after all.

  • Stored procedures

    The Designer supports a narrow use of stored procedures. Using the Designer, you can override the Entity Framework's automatic generation of Insert, Update, and Delete commands by mapping an entity to a set of stored procedures with two important rules. The first is that the stored procedure must line up with the entity. For inserts and updates, that means the values for the stored procedure parameters must come from an entity's property. The second rule is that you have to override the Insert, Update, and Delete commands, or no commands at all, so you'll need to map all three functions.

    In addition, the Designer supports read queries as long as the query results map directly to an entity. If you have a query that returns random data, you will need to manually create an entity for it to map to. That's not too hard in the Designer, but there's another requirement that will necessitate doing some work in the XML.

  • Unsupported EDM types

    The EDM has a very rich set of modeling capabilities. But the Designer does not support all of these advanced modeling techniques, requiring you to handcode some of them in the XML. In most cases, you can continue to work with the model in the Designer even though you won't see these particular model types, though you can leverage them in your code. However, there are a few model types, such as the very useful complex type, that, when included in the XML, will make it impossible to open the model in the Designer. The Designer is well aware of these limitations, and at least provides an alternative view that displays a message explaining why the model can't be opened.

  • Generating a database from the model

    The EDM is based on a data-driven design with the assumption that there is an existing database for the model to map back to. This makes a lot of sense if you are building an application for an existing database. Domain-driven developers prefer to create their object model first and have a database generated from that. The current designer does not support this capability. However, model first development will be possible in the next version of the Entity Framework tools, which will ship in Visual Studio 2010. In the meantime, developers in the community and at Microsoft are playing with a code generator called T4 Templates (Text Template Transformation Toolkit) to read the model and generate SQL script files to generate database objects for you.

Entity in Entity Framework

Entities are not the same as objects. Entities define the schema of an object, but not its behavior. So, an entity is something like the schema of a table in your database, except that it describes the schema of your business objects. Entity Framework is to build a conceptual model, entity data model from database schema, but not only database schema.

An EDM is a client-side data model and it is the core of the Entity Framework. It is not the same as the database model, that belongs to the database. EDM describes the structure of your business objects. It's as though you were given permission to restructure the database tables and views in your enterprise's database so that the tables and relationships look more like your business domain rather than the normalized schema that is designed by database administrators. Below compare a database model and a entity data model.

The entity data model doesn't have any knowledge of the data store, what type of database it is, much less what the schema is. And it doesn't need to. The database you choose as your backend will have no impact on your model or your code.

The Entity Framework communicates with the same ADO.NET data providers that ADO.NET already uses, but with a caveat. The provider must be updated to support the Entity Framework. The provider takes care of reshaping the Entity Framework's queries and commands into native queries and commands. All you need to do is identify the provider and a database connection string so that the Entity Framework can get to the database.

This means that if you need to write applications against a number of different databases, you won't have to learn the ins and outs of each database. You can write queries with the Entity Framework's syntax (either LINQ to Entities or Entity SQL) and never have to worry about the differences between the databases. If you need to take advantage of functions or operators that are particular to a database, Entity SQL allows you to do that as well.

Although the Entity Framework is designed to let you work directly with the classes from the EDM, it still needs to interact with the database. The conceptual data model that the EDM describes is stored in an XML file whose schema identifies the entities and their properties. Behind the conceptual schema described in the EDM is another pair of schema files that map your data model back to the database. One is an XML file that describes your database and the other is a file that provides the mapping between your conceptual model and the database.

During query execution and command execution (for updates), the Entity Framework figures out how to turn a query or command that is expressed in terms of the data model into one that is expressed in terms of your database.

When data is returned from the database, it does the job of shaping the database results into the entities and further materializing objects from those results.

Tuesday, July 7, 2009

WF runtime

public class WorkflowRuntime { public WorkflowRuntime(); public void AddService(object service); public void RemoveService(object service); public void StartRuntime(); public void StopRuntime(); public WorkflowInstance CreateWorkflow(XmlReader reader); public WorkflowInstance GetWorkflow(Guid instanceId); /* *** other members *** */ } public sealed class WorkflowInstance { public Guid InstanceId { get; } public void Start(); public void Load(); public void Unload(); public void EnqueueItem(IComparable queueName, object item, IPendingWork pendingWork, object workItem); /* *** other members *** */ } class Program { static void Main() { using(WorkflowRuntime runtime = new WorkflowRuntime()) { TypeProvider typeProvider = new TypeProvider(runtime); typeProvider.AddAssemblyReference("EssentialWF.dll"); runtime.AddService(typeProvider); runtime.StartRuntime(); WorkflowInstance instance = null; using (XmlTextReader reader = new XmlTextReader("OpenSesame.xoml")) { instance = runtime.CreateWorkflow(reader); instance.Start(); } string s = Console.ReadLine(); instance.EnqueueItem("r1", s, null, null); // Prevent Main from exiting before // the WF program instance completes Console.ReadLine(); runtime.StopRuntime(); } } }

When the Start method is called on the WorkflowInstance, the WF runtime runs the WF program asynchronously. But other threading models are supported by the WF runtime. When a ReadLine activity executes, it creates a WF program queue. When our console application (which is playing the role of a listener) reads a string from the console, it resumes the execution of the bookmark established by the ReadLine by enqueuing the string. The name of the WF program queue is the same name, "r1", that we gave to the ReadLine activity (per the execution logic of ReadLine).

In order to illustrate the mechanics of passivation, we can write two different console applications. The first one begins the execution of an instance of the Open, Sesame program.

class FirstProgram { static string ConnectionString = "Initial Catalog=SqlPersistenceService;Data Source=localhost;Integrated Security=SSPI;"; static void Main() { using (WorkflowRuntime runtime = new WorkflowRuntime()) { SqlWorkflowPersistenceService persistenceService = new SqlWorkflowPersistenceService(ConnectionString); runtime.AddService(persistenceService); TypeProvider typeProvider = new TypeProvider(runtime); typeProvider.AddAssemblyReference("EssentialWF.dll"); runtime.AddService(typeProvider); runtime.StartRuntime(); WorkflowInstance instance = null; using (XmlTextReader reader = new XmlTextReader("OpenSesame.xoml")) { instance = runtime.CreateWorkflow(reader); instance.Start(); } Guid durableHandle = instance.InstanceId; // save the Guid... instance.Unload(); runtime.StopRuntime(); } } }

The WF program instance never completes because it is expecting to receive a string after it prints the key, and we do not provide it with any input. When the WorkflowInstance.Unload method is called,[2] the instance is passivated. Inspection of the SQL Server database table that holds passivated WF program instances will show us a row representing the idle Open, Sesame program instance.

In order to resume the passivated instance in another CLR application domain, we need to have some way of identifying the instance. That is precisely the purpose of the InstanceId property of WorkflowInstance. This globally unique identifier can be saved and then later passed as a parameter to the WorkflowRuntime.GetWorkflow method in order to obtain a fresh WorkflowInstance for the WF program instance carrying that identifier.

class SecondProgram { static string ConnectionString = "Initial Catalog=SqlPersistenceService;Data Source=localhost;Integrated Security=SSPI;"; static void Main() { using (WorkflowRuntime runtime = new WorkflowRuntime()) { SqlWorkflowPersistenceService persistenceService = new SqlWorkflowPersistenceService(ConnectionString); runtime.AddService(persistenceService); TypeProvider typeProvider = new TypeProvider(runtime); typeProvider.AddAssemblyReference("EssentialWF.dll"); runtime.AddService(typeProvider); runtime.StartRuntime(); // get the identifier we had saved Guid id = "saveed from first program"; WorkflowInstance instance = runtime.GetWorkflow(id); // user must enter the key that was printed // during the execution of the first part of // the Open, Sesame program string s = Console.ReadLine(); instance.EnqueueItem("r1", s, null, null); // Prevent Main from exiting before // the WF program instance completes Console.ReadLine(); runtime.StopRuntime(); } } }

The passivated (bookmarked) WF program instance picks up where it left off, and writes its result to the console after we provide the second string.

WF Programming Model

Workflow is queue and scheduler.

public class ReadLine : Activity { private string text; public string Text { get { return text; } } protected override ActivityExecutionStatus Execute( ActivityExecutionContext context) { WorkflowQueuingService qService = context.GetService<WorkflowQueuingService>(); WorkflowQueue queue = qService.CreateWorkflowQueue(this.Name, true); queue.QueueItemAvailable += this.ContinueAt; return ActivityExecutionStatus.Executing; } void ContinueAt(object sender, QueueEventArgs e) { ActivityExecutionContext context = sender as ActivityExecutionContext; WorkflowQueuingService qService = context.GetService<WorkflowQueuingService>(); WorkflowQueue queue = qService.GetWorkflowQueue(this.Name); text = (string) queue.Dequeue(); qService.DeleteWorkflowQueue(this.Name); context.CloseActivity(); } }

In WF, the data structure chosen to represent a bookmark's capacity to hold data is queue. This queue, which we shall call a WF program queue is created by ReadLine using WorkflowQueuingService.

namespace System.Workflow.Runtime { public class WorkflowQueuingService { // queueName is the bookmark name public WorkflowQueue CreateWorkflowQueue( IComparable queueName, bool transactional); public bool Exists(IComparable queueName); public WorkflowQueue GetWorkflowQueue(IComparable queueName); public void DeleteWorkflowQueue(IComparable queueName); /* *** other members *** */ } }

The WorkflowQueue object that is returned by the CreateWorkflowQueue method offers an event, QueueItemAvailable. Despite the syntactic sugar of the C# event, this event represents the asynchronous delivery of stimulus from an external entity to an activity, and is exactly the same pattern of bookmark resumption. The more refined WF version of the programming model for bookmarks allows a bookmark's payload (a WF program queue) to hold an ordered list of inputs that await processing (instead of a single object as did the bookmark in Chapter 1). The physical resumption point of the bookmark is still just a delegate (ContinueAt) even though in the WF programming model the delegate is indicated using the += event subscription syntax of C#.

namespace System.Workflow.Runtime { public class WorkflowQueue { public event EventHandler<QueueEventArgs> QueueItemAvailable; public object Dequeue(); public int Count { get; } public IComparable QueueName { get; } /* *** other members *** */ } }

The return value of the ReadLine activity's Execute method indicates that, at that point in time, the ReadLine has pending bookmarks; its execution is not complete. When an item is enqueued in its WF program queue, perhaps days after the ReadLine began its execution, the bookmark is resumed and, as a result, the ContinueAt method is invoked. After obtaining the item from its queue and setting the value of its text field, the ReadLine activity reports its completion.

public class Sequence : CompositeActivity { protected override ActivityExecutionStatus Execute( ActivityExecutionContext context) { if (this.EnabledActivities.Count == 0) return ActivityExecutionStatus.Closed; Activity child = this.EnabledActivities[0]; child.Closed += this.ContinueAt; context.ExecuteActivity(child); return ActivityExecutionStatus.Executing; } void ContinueAt(object sender, ActivityExecutionStatusChangedEventArgs e) { ActivityExecutionContext context = sender as ActivityExecutionContext; e.Activity.Closed -= this.ContinueAt; int index = this.EnabledActivities.IndexOf(e.Activity); if ((index + 1) == this.EnabledActivities.Count) context.CloseActivity(); else { Activity child = this.EnabledActivities[index + 1]; child.Closed += this.ContinueAt; context.ExecuteActivity(child); } } }

Sequence cannot directly execute its child activities since the Activity.Execute method has accessibility of protected internal. Instead, Sequence requests the execution of a child activity via ActivityExecutionContext.

Sequence subscribes to the Activity.Closed event before it requests the execution of a child activity. When the child activity completes its execution, the execution of the Sequence is resumed at the ContinueAt method. The Sequence activity's subscription to the Closed event of a child activity is syntactic sugar for the creation of a bookmark that is managed internally, on behalf of Sequence, by the WF runtime.

The ActivityExecutionContext type is effectively an activity-facing abstraction on top of the WF runtime.

namespace System.Workflow.ComponentModel { public class ActivityExecutionContext : System.IServiceProvider { public void ExecuteActivity(Activity activity); public void CloseActivity(); public T GetService<T>(); public object GetService(Type serviceType); /* *** other members *** */ } }

Sunday, July 5, 2009

When workflow is persisted.

If a persistence service is loaded, the state of the workflow is persisted in the following situations:

  1. When a workflow becomes idle. For example, when a workflow is waiting for an external event or executes a DelayActivity. To persist and unload a workflow when it becomes idle, the service must return true from the UnloadOnIdle method. With the standard, SqlWorkflowPersistenceService, you can affect this behavior by setting an UnloadOnIdle parameter during the construction of the service.
  2. When a workflow completes or terminates.
  3. When a TransactionScopeActivity (or CompensatableTransactionScopeActivity) completes. A TransactionScopeScopeActivity identifies a logical unit of work that is ended when the activity completes.
  4. When a CompenstableSequneceActivity completes. A CompensatableSequenceActivity identifies a set of child activities that a compensatable. Compensation is the ability to undo the actions of a completed activity.
  5. When a custom activity that is decorated with the PersistOnCloseAttribute completes.
  6. When you manually invoke one of the methods on a WorkflowInstance that cause a persistence operation. Examples are Unload and TryUnload. The Load method results in a previously unloaded and persisted workflow being retrieved and loaded back into memory.

It is important to make a distinction between saving a workflow and saving the state of a workflow. Not all persistence operations result in a new serialized copy of a workflow being saved. For instance, when a workflow completes or terminates, the standard SQL Server persistence service (SqlWorkflowPersistenceService) actually removes the persisted copy of the workflow. It persisted the workflow in the sense that it updated the durable store with the state of the workflow. If you implement your own persistence service, you may choose to do something else when a workflow completes.

Why ManualWorkflowSchedulerService should be used in asp.net enviroment

Why ManualWorkflowSchedulerService should be used in asp.net enviroment