Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
.NET Framework 4.6.1, iş akışı verilerini kalıcı hale getirmek için SQL Server kullanan bir örnek deposu içerir SqlWorkflowInstanceStore. Uygulamanızın iş akışı verilerini farklı bir veritabanı veya dosya sistemi gibi başka bir ortamda kalıcı hale getirmek için gerekliyse, özel bir örnek deposu uygulayabilirsiniz. Özel örnek deposu, soyut InstanceStore sınıfı genişleterek ve uygulama için gerekli yöntemleri uygulayarak oluşturulur. Özel örnek deposunun eksiksiz bir uygulaması için Bkz . Kurumsal Satın Alma İşlemi örneği.
BeginTryCommand yöntemini uygulama
BeginTryCommand, kalıcılık altyapısı tarafından örnek deposuna gönderilir. Parametresinin command
türü, hangi komutun yürütülmekte olduğunu gösterir; bu parametre aşağıdaki türlerden olabilir:
SaveWorkflowCommand: Bir iş akışı depolama ortamına kalıcı hale geldiğinde, kalıcılık altyapısı bu komutu örnek deposuna gönderir. İş akışı kalıcılık verileri, parametresinin üyesindeki InstanceData yöntemine
command
sağlanır.LoadWorkflowCommand: Bir iş akışı depolama alanından yüklenecek olduğunda, kalıcılık altyapısı bu komutu örnek deposuna gönderir. Yüklenecek iş akışının örnek kimliği, parametresinin özelliğinin
instanceId
InstanceView parametresinde yönteminecontext
sağlanır.CreateWorkflowOwnerCommand: Kalıcılık altyapısı, bir WorkflowServiceHost kilit sahibi olarak kaydedilmesi gerektiğinde bu komutu örnek deposuna gönderir. Geçerli iş akışının örnek kimliği, parametresinin yöntemi kullanılarak BindInstanceOwner örnek deposuna
context
sağlanmalıdır.Aşağıdaki kod parçacığı, açık bir kilit sahibi atamak için CreateWorkflowOwner komutunun nasıl uygulanacağını gösterir.
XName WFInstanceScopeName = XName.Get(scopeName, "<namespace>"); ... CreateWorkflowOwnerCommand createCommand = new CreateWorkflowOwnerCommand() { InstanceOwnerMetadata = { { WorkflowHostTypeName, new InstanceValue(WFInstanceScopeName) }, } }; InstanceHandle ownerHandle = store.CreateInstanceHandle(); store.DefaultInstanceOwner = store.Execute( ownerHandle, createCommand, TimeSpan.FromSeconds(30)).InstanceOwner; childInstance.AddInitialInstanceValues(new Dictionary<XName, object>() { { WorkflowHostTypeName, WFInstanceScopeName } });
DeleteWorkflowOwnerCommand: Bir kilit sahibinin örnek kimliği örnek deposundan kaldırılabildiğinde, kalıcılık altyapısı bu komutu örnek deposuna gönderir. gibi CreateWorkflowOwnerCommand, kilit sahibinin kimliği uygulama tarafından sağlanmalıdır.
Aşağıdaki kod parçacığı kullanılarak kilidin DeleteWorkflowOwnerCommandnasıl serbest bırakılmasını gösterir.
static void FreeHandleAndDeleteOwner(InstanceStore store, InstanceHandle handle) { handle.Free(); handle = store.CreateInstanceHandle(store.DefaultInstanceOwner); try { // We need this sleep so that we dont prematurely delete the owner, we need time to update the database. Thread.Sleep(10000); store.Execute(handle, new DeleteWorkflowOwnerCommand(), TimeSpan.FromSeconds(10)); } catch (InstancePersistenceCommandException ex) { Log.Inform(ex.Message); } catch (InstanceOwnerException ex) { Log.Inform(ex.Message); } catch (OperationCanceledException ex) { Log.Inform(ex.Message); } catch (Exception ex) { Log.Inform(ex.Message); } finally { handle.Free(); store.DefaultInstanceOwner = null; } }
Bir alt örnek çalıştırıldığında yukarıdaki yöntem Try/Catch bloğunda çağrılmalıdır.
try { childInstance.Run(); } catch (Exception) { throw ; } finally { FreeHandleAndDeleteOwner(store, ownerHandle); }
LoadWorkflowByInstanceKeyCommand: Bir iş akışı örneği iş akışının örnek anahtarı kullanılarak yüklenecekse, kalıcılık altyapısı bu komutu örnek deposuna gönderir. Örnek anahtarının kimliği, komutun LookupInstanceKey parametresi kullanılarak belirlenebilir.
QueryActivatableWorkflowsCommand: Kalıcılık altyapısı, daha sonra iş akışlarını yükleyebilen bir iş akışı konağı oluşturmak üzere kalıcı iş akışları için etkinleştirme parametrelerini almak üzere bu komutu örnek deposuna gönderir. Bu komut, etkinleştirilebilen bir örneği bulurken ana bilgisayara yükselten HasActivatableWorkflowEvent örnek deposuna yanıt olarak altyapı tarafından gönderilir. Etkinleştirilebilen iş akışları olup olmadığını belirlemek için örnek deposu yoklanmalıdır.
TryLoadRunnableWorkflowCommand: Kalıcılık altyapısı, çalıştırılabilir iş akışı örneklerini yüklemek için bu komutu örnek deposuna gönderir. Bu komut, çalıştırılabilir bir örneği bulurken ana bilgisayara yükselten HasRunnableWorkflowEvent örnek deposuna yanıt olarak altyapı tarafından gönderilir. Örnek deposu çalıştırılabilir iş akışlarını yoklamalıdır. Aşağıdaki kod parçacığı, çalıştırılabilir veya etkinleştirilebilen iş akışları için örnek deposunu yoklamayı gösterir.
public void PollForEvents() { InstanceOwner[] storeOwners = this.GetInstanceOwners(); foreach (InstanceOwner owner in storeOwners) { foreach (InstancePersistenceEvent ppEvent in this.GetEvents(owner)) { if (ppEvent.Equals(HasRunnableWorkflowEvent.Value)) { bool hasRunnable = GetRunnableEvents(this.StoreId, owner.InstanceOwnerId, isActivatable); if (hasRunnable) { this.SignalEvent(ppEvent, owner); } else { this.ResetEvent(ppEvent, owner); } } else if(ppEvent.Equals(HasActivatableWorkflowEvent.Value)) { bool hasActivatable = GetActivatableEvents(this.StoreId, owner.InstanceOwnerId); if (hasActivatable) { this.SignalEvent(ppEvent, owner); } else { this.ResetEvent(ppEvent, owner); } } } } }
Yukarıdaki kod parçacığında, örnek deposu kullanılabilir olayları sorgular ve olay olup olmadığını HasRunnableWorkflowEvent belirlemek için her birini inceler. Bulunursa, SignalEvent ana bilgisayara örnek deposuna bir komut göndermesi için sinyal vermek için çağrılır. Aşağıdaki kod parçacığı, bu komut için bir işleyicinin uygulamasını gösterir.
If (command is TryLoadRunnableWorkflowCommand) { Owner owner; CheckOwner(context, command.Name, out owner); // Checking instance.Owner is like an InstanceLockQueryResult. context.QueriedInstanceStore(new InstanceLockQueryResult(context.InstanceView.InstanceId, context.InstanceView.InstanceOwner.InstanceOwnerId)); XName ownerService = null; InstanceValue value; Instance runnableInstance = default(Instance); bool foundRunnableInstance = false; value = QueryPropertyBag(WorkflowNamespace.WorkflowHostType, owner.Data); if (value != null && value.Value is XName) { ownerService = (XName)value.Value; } foreach (KeyValuePair<Guid, Instance> instance in MemoryStore.instances) { if (instance.Value.Owner != Guid.Empty || instance.Value.Completed) { continue; } if (ownerService != null) { value = QueryPropertyBag(WorkflowNamespace.WorkflowHostType, instance.Value.Metadata); if (value == null || ((XName)value.Value) != ownerService) { continue; } } value = QueryPropertyBag(WorkflowServiceNamespace.SuspendReason, instance.Value.Metadata); if (value != null && value.Value != null && value.Value is string) { continue; } value = QueryPropertyBag(WorkflowNamespace.Status, instance.Value.Data); if (value != null && value.Value is string && ((string)value.Value) == "Executing") { runnableInstance = instance.Value; foundRunnableInstance = true; } if (!foundRunnableInstance) { value = QueryPropertyBag(XNamespace.Get("urn:schemas-microsoft-com:System.Activities/4.0/properties").GetName("TimerExpirationTime"), instance.Value.Data); if (value != null && value.Value is DateTime && ((DateTime)value.Value) <= DateTime.UtcNow) { runnableInstance = instance.Value; foundRunnableInstance = true; } } if (foundRunnableInstance) { runnableInstance.LockVersion++; runnableInstance.Owner = context.InstanceView.InstanceOwner.InstanceOwnerId; MemoryStore.instances[instance.Key] = runnableInstance; context.BindInstance(instance.Key); context.BindAcquiredLock(runnableInstance.LockVersion); Dictionary<Guid, IDictionary<XName, InstanceValue>> associatedKeys = new Dictionary<Guid, IDictionary<XName, InstanceValue>>(); Dictionary<Guid, IDictionary<XName, InstanceValue>> completedKeys = new Dictionary<Guid, IDictionary<XName, InstanceValue>>(); foreach (KeyValuePair<Guid, Key> keyEntry in MemoryStore.keys) { if (keyEntry.Value.Instance == context.InstanceView.InstanceId) { if (keyEntry.Value.Completed) { completedKeys.Add(keyEntry.Key, DeserializePropertyBag(keyEntry.Value.Metadata)); } else { associatedKeys.Add(keyEntry.Key, DeserializePropertyBag(keyEntry.Value.Metadata)); } } } context.LoadedInstance(InstanceState.Initialized, DeserializePropertyBag(runnableInstance.Data), DeserializePropertyBag(runnableInstance.Metadata), associatedKeys, completedKeys); break; } }
Yukarıdaki kod parçacığında örnek deposu çalıştırılabilir örnekleri arar. Bir örnek bulunursa yürütme bağlamı ile ilişkilidir ve yüklenir.
Özel örnek deposu kullanma
Özel örnek deposu uygulamak için, öğesine örnek deposunun bir örneğini atayın InstanceStorePersistableIdle ve yöntemini uygulayın. Ayrıntılar için Bkz. Nasıl yapılır: Uzun Süre Çalışan İş Akışı Oluşturma ve Çalıştırma öğreticisi.
Örnek örnek deposu
Aşağıdaki kod örneği, Kurumsal Satın Alma İşlemi örneğinden alınan eksiksiz bir örnek deposu uygulamasıdır. Bu örnek deposu, XML kullanarak iş akışı verilerini bir dosyada kalıcı hale getirmekte.
using System;
using System.Activities.DurableInstancing;
using System.Collections.Generic;
using System.IO;
using System.Runtime.DurableInstancing;
using System.Runtime.Serialization;
using System.ServiceModel.Persistence;
using System.Xml;
using System.Xml.Linq;
namespace Microsoft.Samples.WF.PurchaseProcess
{
public class XmlWorkflowInstanceStore : InstanceStore
{
Guid ownerInstanceID;
public XmlWorkflowInstanceStore() : this(Guid.NewGuid())
{
}
public XmlWorkflowInstanceStore(Guid id)
{
ownerInstanceID = id;
}
//Synchronous version of the Begin/EndTryCommand functions
protected override bool TryCommand(InstancePersistenceContext context, InstancePersistenceCommand command, TimeSpan timeout)
{
return EndTryCommand(BeginTryCommand(context, command, timeout, null, null));
}
//The persistence engine will send a variety of commands to the configured InstanceStore,
//such as CreateWorkflowOwnerCommand, SaveWorkflowCommand, and LoadWorkflowCommand.
//This method is where we will handle those commands
protected override IAsyncResult BeginTryCommand(InstancePersistenceContext context, InstancePersistenceCommand command, TimeSpan timeout, AsyncCallback callback, object state)
{
IDictionary<XName, InstanceValue> data = null;
//The CreateWorkflowOwner command instructs the instance store to create a new instance owner bound to the instanace handle
if (command is CreateWorkflowOwnerCommand)
{
context.BindInstanceOwner(ownerInstanceID, Guid.NewGuid());
}
//The SaveWorkflow command instructs the instance store to modify the instance bound to the instance handle or an instance key
else if (command is SaveWorkflowCommand)
{
SaveWorkflowCommand saveCommand = (SaveWorkflowCommand)command;
data = saveCommand.InstanceData;
Save(data);
}
//The LoadWorkflow command instructs the instance store to lock and load the instance bound to the identifier in the instance handle
else if (command is LoadWorkflowCommand)
{
string fileName = IOHelper.GetFileName(this.ownerInstanceID);
try
{
using (FileStream inputStream = new FileStream(fileName, FileMode.Open))
{
data = LoadInstanceDataFromFile(inputStream);
//load the data into the persistence Context
context.LoadedInstance(InstanceState.Initialized, data, null, null, null);
}
}
catch (Exception exception)
{
throw new PersistenceException(exception.Message);
}
}
return new CompletedAsyncResult<bool>(true, callback, state);
}
protected override bool EndTryCommand(IAsyncResult result)
{
return CompletedAsyncResult<bool>.End(result);
}
//Reads data from xml file and creates a dictionary based off of that.
IDictionary<XName, InstanceValue> LoadInstanceDataFromFile(Stream inputStream)
{
IDictionary<XName, InstanceValue> data = new Dictionary<XName, InstanceValue>();
NetDataContractSerializer s = new NetDataContractSerializer();
XmlReader rdr = XmlReader.Create(inputStream);
XmlDocument doc = new XmlDocument();
doc.Load(rdr);
XmlNodeList instances = doc.GetElementsByTagName("InstanceValue");
foreach (XmlElement instanceElement in instances)
{
XmlElement keyElement = (XmlElement)instanceElement.SelectSingleNode("descendant::key");
XName key = (XName)DeserializeObject(s, keyElement);
XmlElement valueElement = (XmlElement)instanceElement.SelectSingleNode("descendant::value");
object value = DeserializeObject(s, valueElement);
InstanceValue instVal = new InstanceValue(value);
data.Add(key, instVal);
}
return data;
}
object DeserializeObject(NetDataContractSerializer serializer, XmlElement element)
{
object deserializedObject = null;
MemoryStream stm = new MemoryStream();
XmlDictionaryWriter wtr = XmlDictionaryWriter.CreateTextWriter(stm);
element.WriteContentTo(wtr);
wtr.Flush();
stm.Position = 0;
deserializedObject = serializer.Deserialize(stm);
return deserializedObject;
}
//Saves the persistence data to an xml file.
void Save(IDictionary<XName, InstanceValue> instanceData)
{
string fileName = IOHelper.GetFileName(this.ownerInstanceID);
XmlDocument doc = new XmlDocument();
doc.LoadXml("<InstanceValues/>");
foreach (KeyValuePair<XName,InstanceValue> valPair in instanceData)
{
XmlElement newInstance = doc.CreateElement("InstanceValue");
XmlElement newKey = SerializeObject("key", valPair.Key, doc);
newInstance.AppendChild(newKey);
XmlElement newValue = SerializeObject("value", valPair.Value.Value, doc);
newInstance.AppendChild(newValue);
doc.DocumentElement.AppendChild(newInstance);
}
doc.Save(fileName);
}
XmlElement SerializeObject(string elementName, object o, XmlDocument doc)
{
NetDataContractSerializer s = new NetDataContractSerializer();
XmlElement newElement = doc.CreateElement(elementName);
MemoryStream stm = new MemoryStream();
s.Serialize(stm, o);
stm.Position = 0;
StreamReader rdr = new StreamReader(stm);
newElement.InnerXml = rdr.ReadToEnd();
return newElement;
}
}
}