Wednesday, July 8, 2009

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

Monday, June 29, 2009

What is a fake

A fake is a generic term that can be used to describe either a stub or a mock object (handwritten or otherwise), because they both look like the real object. Whether a fake is a stub or a mock depends how it's used in the current test. If it's used to check an interaction(asserted against), it's a mock object. Otherwise, it is a stub.

What is mock

A mock object is a fake object in the system that decides whether the unit test has passed or failed. It does so by verifying whether the object under test interacted as expected with the fake object.