前文我们尝试添加了SqlTrackingService服务,运行后观察了数据库的变化。本节将这个例子丰富并完成。
检索来自于工作流中的跟踪记录
打开上节创建的WorkflowTracker工程的Program.cs文件,声明以下空间:
UsingSystem.Configuration;
UsingSystem.Workflow.Runtime.Tracking;
在Main方法的 waitHandle.WaintOne();后添加:
ShowWorkflowTrackingEvents(instance.InstanceId);
ShowActivityTrackingEvents(instance.InstanceId);
这是两个不存在的方法,我们需要添加如下代码。
static voidShowActivityTrackingEvents(Guid instanceId)
{
SqlTrackingQuery sqlTrackingQuery = newSqlTrackingQuery(ConfigurationManager.ConnectionStrings["TrackingDatabase"].ConnectionString);
SqlTrackingWorkflowInstance sqlTrackingWorkflowInstance = null;
sqlTrackingQuery.TryGetWorkflow(instanceId, outsqlTrackingWorkflowInstance);
if (sqlTrackingWorkflowInstance != null)
{
Console.WriteLine("\nActivity Tracking Events:\n");
Console.WriteLine(" Status :: Date/Time :: Qualified ID");
foreach(ActivityTrackingRecord atr in sqlTrackingWorkflowInstance.ActivityEvents)
{
Console.WriteLine(" {0} ::{1} :: {2}", atr.ExecutionStatus, atr.EventDateTime, atr.QualifiedName);
} // foreach
} // if
}
static void ShowWorkflowTrackingEvents(Guid instanceId)
{
SqlTrackingQuery sqlTrackingQuery = newSqlTrackingQuery(ConfigurationManager.ConnectionStrings["TrackingDatabase"].ConnectionString);
SqlTrackingWorkflowInstance sqlTrackingWorkflowInstance = null;
sqlTrackingQuery.TryGetWorkflow(instanceId, outsqlTrackingWorkflowInstance);
if (sqlTrackingWorkflowInstance != null)
{
Console.WriteLine("\nWorkflowInstance Events:\n");
Console.WriteLine(" Description :: Date/Time");
foreach (WorkflowTrackingRecordworkflowTrackingRecord in sqlTrackingWorkflowInstance.WorkflowEvents)
{
Console.WriteLine(" {0} :: {1}",workflowTrackingRecord.TrackingWorkflowEvent,workflowTrackingRecord.EventDateTime);
} // foreach
}
}
上述代码并不复杂,我们首先创建了一SqlTrackingQuery的实例,为它提供数据库连接字符串。然后我们通过当前工作流实例ID,从数据库中查询该实例的跟踪信息。查询由SqlTrackingService.TryGetWorkflow执行。假如数据库中有指定工作流的信息,我们循环获取跟踪记录。查询返回给我们的是一个workflowTrackingRecord对象的集合,从中提取需要的信息。编译,运行,在控制台查看输出。
跟踪用户事件
SqlTrackingService是WF的一部分,它可以跟踪活动和工作流激发的标准事件。
Activity活动支持一个名叫TrackData的方法,执行它并为跟踪传入通常是字符串类型的数据,将作为用户事件数据存入跟踪数据库。
在Workflow1.cs,找到PreDelayMessage方法,添加
this.TrackData(“Delaycommencing”);
找到PostDelayMessage方法,添加
this.TrackData(“Delaycomplete”);
编译执行。打开WorkflowTracking数据表中的UserEvent表。我们在工作流中每调用一次TrackData就产生一条记录。
创建自定义配置文件
通过定义跟踪配置文件来限制存储到跟踪数据库中的信息,它规定一个给定的工作流的跟踪将包含和排除的东西。使用TrackingProfile对象及其他可用对象来创建这个XML文档。
在”事件跟踪对象“表中带有location或point的对象,location指在你的工作流中活动、工作流或用户事件发生时一个指定的位置。Locations描述了要跟跟踪的事件,使用它能精确地指定你想跟踪或排除的事件。你可把跟踪点当成一个感兴趣的点,能在你的工作流代码中跨越不同的位置。
当我们建立跟踪配置文件时,就是要把跟踪点和位置添加到profile对象中作为跟踪事件的过滤器使用。
创建一个新的跟踪配置文件
打开WorkflowTracker的Program.cs,加入如下空间声明:
usingSystem.Workflow.ComponentModel;
usingSystem.Data;
usingSystem.Data.SqlClient;
usingSystem.Globalization;
usingSystem.IO;
在Main方法中创建工作流的代码前,添加如下代码:
TrackingProfile profile = CreateProfile();
StoreProfile(profile, ConfigurationManager.ConnectionStrings["TrackingDatabase"].ConnectionString);
定义上述两个函数:
static TrackingProfile CreateProfile()
{
//Create the basic profile
TrackingProfile profile = new TrackingProfile();
//Create the activity location, meaning the events we're interested in
ActivityTrackingLocation actLoc = newActivityTrackingLocation(typeof(Activity));
actLoc.MatchDerivedTypes = true;
actLoc.ExecutionStatusEvents.Add(ActivityExecutionStatus.Executing);
//Create the activity track point and add the location we just created
ActivityTrackPoint actPt = new ActivityTrackPoint();
actPt.MatchingLocations.Add(actLoc);
profile.ActivityTrackPoints.Add(actPt);
//Create the workflow location
WorkflowTrackingLocation wfLoc = new WorkflowTrackingLocation();
wfLoc.Events.Add(TrackingWorkflowEvent.Started);
wfLoc.Events.Add(TrackingWorkflowEvent.Idle);
//Create the workflow track point
WorkflowTrackPoint wfPt = new WorkflowTrackPoint();
wfPt.MatchingLocation = wfLoc;
profile.WorkflowTrackPoints.Add(wfPt);
//Set the version of the profile...this version must not already exist
//in the database.
profile.Version = new Version("1.0.0.0");
return profile;
}
staticvoid StoreProfile(TrackingProfile profile, string connString)
{
//First, serialize the profile into an XML string
TrackingProfileSerializer serializer = new TrackingProfileSerializer();
StringWriter writer = new StringWriter(new StringBuilder(),CultureInfo.InvariantCulture);
serializer.Serialize(writer, profile);
//Then, write the XML string to the database
SqlConnection conn = null;
try
{
if (!String.IsNullOrEmpty(connString))
{
// Create a connection object
conn = new SqlConnection(connString);
// Create a dummy for the stored proc name
string storedProc ="dbo.UpdateTrackingProfile";
// Create the command
SqlCommand cmd = new SqlCommand(storedProc, conn);
cmd.CommandType = CommandType.StoredProcedure;
// Add the parameters
SqlParameter parm = new SqlParameter("@TypeFullName",SqlDbType.NVarChar, 128);
parm.Direction = ParameterDirection.Input;
parm.Value = typeof(TrackedWorkflow.Workflow1).ToString();
cmd.Parameters.Add(parm);
parm = new SqlParameter("@AssemblyFullName",SqlDbType.NVarChar, 256);
parm.Direction = ParameterDirection.Input;
parm.Value = typeof(TrackedWorkflow.Workflow1).Assembly.FullName;
cmd.Parameters.Add(parm);
parm = new SqlParameter("@Version", SqlDbType.VarChar, 32);
parm.Direction = ParameterDirection.Input;
parm.Value = "1.0.0.0";
cmd.Parameters.Add(parm);
parm = new SqlParameter("@TrackingProfileXml",SqlDbType.NText);
parm.Direction = ParameterDirection.Input;
parm.Value = writer.ToString();
cmd.Parameters.Add(parm);
// Open the connection
conn.Open();
// Write the XML data
cmd.ExecuteNonQuery();
} // if
}// try
catch (Exception ex)
{
// If the exception is telling us we've already written
// this profile to the database, just pop up an informational
// message.
if (ex is SqlException)
{
// Check to see if it's a version error
if (ex.Message.Contains("已经存在大于或等于新版本的版本"))
{
// Version alreadyexists...
Console.WriteLine("NOTE: a profile with the same version alreadyexists in the database.");
} // if
else
{
// Write error message
Console.WriteLine("Error writing profile to database: {0}",ex.ToString());
} // else
} // if
else
{
// Write error message
Console.WriteLine("Error writing profile to database: {0}",ex.ToString());
} // else
}// catch
finally
{
// Close the connection
if (conn != null)
{
conn.Close();
} // if
}// finally
}
我们用CreateProfile方法创建了一个新的TrackingProfile并添加了一个活动跟踪点和工作流跟踪点。每个跟踪点都有一个单一的位置,它只定义了要跟踪来自活动Executing事件和来自工作流实例的Started事件和Idle事件。
用StoreProfile方法,把跟踪配置文件序列化成XML形式,然后用ADO.NET技术把这个XML存入跟踪数据库。