zoukankan      html  css  js  c++  java
  • WF 4.0 之持久化操作二:Xml方式的存储

    在上一篇中提到了InstanceStore和PersistenceParticipant,这两个是实现自定义存储的关键,也是我们两个自定义类分别继承的基类。InstanceStore为持久化的基类,PersistenceParticipant为进行持久化准备数据过程提供了方便。

    在这个实例中一共有两个主要的类,一个是XmlPersistenceParticipant用于收集要持久化的信息,一个是XmlWorkflowInstanceStore用于保存到指定的文件去。

    Xml方式持久化实现之自定义持久化类:

     public class XmlWorkflowInstanceStore : InstanceStore
        {
            Guid ownerInstanceID;
            
            public XmlWorkflowInstanceStore() : this(Guid.NewGuid())
            {
    
            }
    
            public XmlWorkflowInstanceStore(Guid id)
            {
                ownerInstanceID = id;
            }
    
            /// <summary>
            /// 确定是否进行异步进行持久化操作
            /// </summary>
            /// <param name="context"></param>
            /// <param name="command"></param>
            /// <param name="timeout"></param>
            /// <returns></returns>
            protected override bool TryCommand(InstancePersistenceContext context, InstancePersistenceCommand command, TimeSpan timeout)
            {
                return EndTryCommand(BeginTryCommand(context, command, timeout, null, null));
            }
    
           /// <summary>
           /// 异步进行持久化操作
           /// </summary>
           /// <param name="context"></param>
           /// <param name="command"></param>
           /// <param name="timeout"></param>
           /// <param name="callback"></param>
           /// <param name="state"></param>
           /// <returns></returns>
            protected override IAsyncResult BeginTryCommand(InstancePersistenceContext context, InstancePersistenceCommand command, TimeSpan timeout, AsyncCallback callback, object state)
            {
                IDictionary<System.Xml.Linq.XName, InstanceValue> data = null;
    
               //创建持久化工作流
                if (command is CreateWorkflowOwnerCommand)
                {
                    context.BindInstanceOwner(ownerInstanceID, Guid.NewGuid());
                }
                //修改持久化的工作流
                else if (command is SaveWorkflowCommand)
                {
                    SaveWorkflowCommand saveCommand = (SaveWorkflowCommand)command;
                    data = saveCommand.InstanceData;
                    Save(data);
                }
                //加载持久化的工作流
                else if (command is LoadWorkflowCommand)
                {
                    string fileName = IOHelper.GetFileName(this.ownerInstanceID);
                    try
                    {
                        using (FileStream inputStream = new FileStream(fileName, FileMode.Open))
                        {
                            data = LoadInstanceDataFromFile(inputStream);
                            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);
            }
    
            /// <summary>
            /// 从指定的文件中读取工作流信息和数据
            /// </summary>
            /// <param name="inputStream"></param>
            /// <returns></returns>
            IDictionary<System.Xml.Linq.XName, InstanceValue> LoadInstanceDataFromFile(Stream inputStream)
            {
                IDictionary<System.Xml.Linq.XName, InstanceValue> data = new Dictionary<System.Xml.Linq.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");
                    System.Xml.Linq.XName key = (System.Xml.Linq.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;
            }
    
            /// <summary>
            /// 反序列化
            /// </summary>
            /// <param name="serializer"></param>
            /// <param name="element"></param>
            /// <returns></returns>
            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;
            }
    
            /// <summary>
            /// 保存数据到xml
            /// </summary>
            /// <param name="instanceData"></param>
            void Save(IDictionary<System.Xml.Linq.XName, InstanceValue> instanceData)
            {
                string fileName = IOHelper.GetFileName(this.ownerInstanceID);
                XmlDocument doc = new XmlDocument();
                doc.LoadXml("<InstanceValues/>");
                
                foreach (KeyValuePair<System.Xml.Linq.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);
           }
    
            /// <summary>
            /// 序列化
            /// </summary>
            /// <param name="elementName"></param>
            /// <param name="o"></param>
            /// <param name="doc"></param>
            /// <returns></returns>
            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;
            }
        }
     class XmlPersistenceParticipant : PersistenceParticipant
        {
            const string propertiesNamespace = "urn:schemas-microsoft-com:System.Activities/4.0/properties";
            private Guid Id;
    
            public XmlPersistenceParticipant(Guid id)
            {
                Id = id;
            }
    
           /// <summary>
           /// 收集保存的信息
           /// </summary>
           /// <param name="readWriteValues"></param>
           /// <param name="writeOnlyValues"></param>
            protected override void CollectValues(out IDictionary<XName, object> readWriteValues, out IDictionary<XName, object> writeOnlyValues)
            {
                base.CollectValues(out readWriteValues, out writeOnlyValues);
            }
    
            /// <summary>
            /// 对xml中进行name和value的映射
            /// </summary>
            /// <param name="readWriteValues"></param>
            /// <param name="writeOnlyValues"></param>
            /// <returns></returns>
            protected override IDictionary<XName, object> MapValues(IDictionary<XName, object> readWriteValues, IDictionary<XName, object> writeOnlyValues)
            {
                XName StatusXname = XName.Get("Status", propertiesNamespace);
    
                IDictionary<XName, object> mappedValues = base.MapValues(readWriteValues, writeOnlyValues);
    
                RequestForExpert requestForExpert = null;
                string Status = string.Empty;
                object value = null;
    
                //得到xml中Status的值(Status属性主要用来判断是否全部投票完毕,可以自行删除修改)
                if (writeOnlyValues.TryGetValue(StatusXname, out value))
                {
                    Status = (string)value;
                }
    
                //遍历workflow中所有的数据,直到找到保存的实体对象
                foreach (KeyValuePair<System.Xml.Linq.XName, object> item in writeOnlyValues)
                {
                    if (item.Value is LocationInfo)
                    {
                        LocationInfo li = (LocationInfo)item.Value;
                        if (li.Value is RequestForExpert)
                        {
                            requestForExpert = (RequestForExpert)li.Value;
                        }
                    }
                }
    
                //确保存放数据信息的xml和存放文件的文件夹都存在
                IOHelper.EnsureAllrfeFileExists();
    
                // 加载存放数据信息的xml
                XElement doc = XElement.Load(IOHelper.GetAllrfesFileName());
    
                //根据传递的参数Id找到xml节点
                IEnumerable<XElement> current =
                                        from r in doc.Elements("requestForExpert")
                                        where r.Attribute("Id").Value.Equals(Id.ToString())
                                        select r;
    
                //如果状态为关闭则删除掉xml中对应数据的节点
                if (Status == "Closed")
                {
                   
                    foreach (XElement xe in current)
                    {
                        xe.Attribute("Status").Value = "finished";
                    }
                }
                else
                {
                 
                    foreach (XElement xe in current)
                    {
                        xe.Remove();
                    }
    
                    if (requestForExpert != null)
                    {
                        XElement e = Serializerfe(requestForExpert);
                        doc.Add(e);
                    }
                }
                doc.Save(IOHelper.GetAllrfesFileName());
                return mappedValues;
            }
    
           /// <summary>
           /// 序列化
           /// </summary>
           /// <param name="rfe"></param>
           /// <returns></returns>
            XElement Serializerfe(RequestForExpert rfe)
            {
                
                XElement ret =
                   new XElement("requestForExpert",
                   new XAttribute("Id", rfe.ProjectId), new XAttribute("Status", rfe.Status));
    
                //所有的评委列表节点
                XElement expertList = new XElement("ExpertList");
                foreach (ExpertInfo expert in rfe.ExpertList)
                {
                    expertList.Add(
                        new XElement("Expert",
                            new XAttribute("UserId", expert.UserId),
                            new XAttribute("UserName", expert.UserName),
                            new XAttribute("IsConfirmed", expert.IsConfirmed.ToString()))
                        );
                }
                ret.Add(expertList);
    
                //推荐评委列表节点
                XElement CandidateList = new XElement("CandidateList");
                foreach (ExpertInfo expert in rfe.CandidateList)
                {
                    CandidateList.Add(
                        new XElement("Expert",
                            new XAttribute("UserId", expert.UserId),
                            new XAttribute("UserName", expert.UserName),
                            new XAttribute("IsConfirmed", expert.IsConfirmed.ToString()))
                        );
                }
                ret.Add(CandidateList);
    
                //可选择评委列表节点
                XElement OptionalList = new XElement("OptionalList");
                foreach (ExpertInfo expert in rfe.OptionalList)
                {
                    OptionalList.Add(
                        new XElement("Expert",
                            new XAttribute("UserId", expert.UserId),
                            new XAttribute("UserName", expert.UserName),
                            new XAttribute("IsConfirmed", expert.IsConfirmed.ToString()))
                        );
                }
    
                ret.Add(OptionalList);
    
                return ret;
            }
    
            /// <summary>
            ///传递所有加载的值(由 LoadWorkflowCommand 或 LoadWorkflowByInstanceKeyCommand 填充)作为字典参数
            /// </summary>
            /// <param name="readWriteValues"></param>
            protected override void PublishValues(IDictionary<XName, object> readWriteValues)
            {
                base.PublishValues(readWriteValues);
            }
        } 

    XmlWorkflowInstanceStore 类主要重写了BeginCommand方法,通过判断命令来确定要执行的操作,分为创建 保存 加载三个类型,同时在保存的时候使用了Xml的方式,节点的名字可以自定义,但是要保证保存和加载时候使用相同的节点名称和属性。

    XmlPersistenceParticipant 类主要是用来收集要保存的数据,往往是自定义的数据,比如自定义的实体或者自定义的其他复杂类型的数据,主要是MapValue映射方法,将要保存的实体都映射为一个个xml的节点和属性,这样就可以在下次Load的时候根据自己设置的id找到持久化的WorkFlow.

    其中Serializerfe方法中的xml的节点大家是要自定义的,因为要根据自己要保存的对象的和属性来决定要保存哪些信息的,同时要考虑是否有集合属性等其他自己的复杂属性,这个在进行将Xml加载为对象的时候也要一一对应处理。

    XML持久化实现之帮助类:

     public static class RfeRepository
        {
            // id为实体类中的一个属性,也可以自定义
            public static RequestForExpert Retrieve(Guid id)
            {
                XElement doc = XElement.Load(IOHelper.GetAllrfesFileName());
                IEnumerable<RequestForExpert> current =
                                        from r in doc.Elements("requestForExpert")
                                        where r.Attribute("Id").Value.Equals(id.ToString())
                                        select MapFrom(r);
    
                return current.First<RequestForExpert>();
            }
    
            /// <summary>
            /// 获得当前项目(激活状态)
            /// </summary>
            /// <param name="id"></param>
            /// <returns></returns>
            public static RequestForExpert RetrieveActive(Guid id)
            {
                if (!File.Exists(IOHelper.GetAllrfesFileName()))
                    return new RequestForExpert();
    
                XElement doc = XElement.Load(IOHelper.GetAllrfesFileName());
                IEnumerable<RequestForExpert> current = from rfe in doc.Descendants("requestForExpert")
                                                        where (rfe.Attribute("Status").Value.Equals("active")) && (rfe.Attribute("Id").Value.Equals(id.ToString()))
                                                        select MapFrom(rfe);
    
                return current.First<RequestForExpert>();
            }
    
    
            /// <summary>
            /// 根据一个根节点,得到节点下的所有属性对应的节点
            /// </summary>
            /// <param name="elem"></param>
            /// <returns></returns>
            static RequestForExpert MapFrom(XElement elem)
            {
                RequestForExpert rfe = new RequestForExpert();
                rfe.ProjectId = new Guid(elem.Attribute("Id").Value);
                rfe.Status = elem.Attribute("Status").Value;
    
                //所有的评委
                IEnumerable < ExpertInfo > expertList= elem.Descendants("ExpertList").
                                                                                        Descendants("Expert").
                                                                                        Select(x => 
                                                                                            new ExpertInfo { UserId = x.Attribute("UserId").Value, 
                                                                                            IsConfirmed = Convert.ToBoolean(x.Attribute("IsConfirmed").Value), 
                                                                                            UserName = x.Attribute("UserName").Value });
                rfe.ExpertList = expertList.ToList();
    
                //选中的评委
                expertList = elem.Descendants("CandidateList").
                                                                                        Descendants("Expert").
                                                                                        Select(x =>
                                                                                            new ExpertInfo
                                                                                            {
                                                                                                UserId = x.Attribute("UserId").Value,
                                                                                                IsConfirmed = Convert.ToBoolean(x.Attribute("IsConfirmed").Value),
                                                                                                UserName = x.Attribute("UserName").Value
                                                                                            });
    
                rfe.CandidateList = expertList.ToList();
    
                //可选择的评委
                expertList = elem.Descendants("OptionalList").
                                                                                        Descendants("Expert").
                                                                                        Select(x =>
                                                                                            new ExpertInfo
                                                                                            {
                                                                                                UserId = x.Attribute("UserId").Value,
                                                                                                IsConfirmed = Convert.ToBoolean(x.Attribute("IsConfirmed").Value),
                                                                                                UserName = x.Attribute("UserName").Value
                                                                                            });
                rfe.OptionalList = expertList.ToList();
    
                return rfe;
            }
    
        }

    此类用于根据唯一标识去加载Xml中对应的实体对象。

      public static class IOHelper
        {
    
            public static readonly string InstanceFormatString = "{0}.xml";
            public static readonly string PersistenceDirectory = Path.Combine(Path.GetTempPath(), "FilePersistenceProvider");
    
            public static string GetFileName(Guid id)
            {
                EnsurePersistenceFolderExists();
                return Path.Combine(PersistenceDirectory, string.Format(CultureInfo.InvariantCulture, InstanceFormatString, id));
            }
    
            public static string GetAllrfesFileName()
            {
                EnsurePersistenceFolderExists();
                return Path.Combine(PersistenceDirectory, "rfes.xml");
            }
    
            public static string GetTrackingFilePath(Guid instanceId)
            {
                EnsurePersistenceFolderExists();
                return Path.Combine(PersistenceDirectory, instanceId.ToString() + ".tracking");
            }
    
            public static void EnsurePersistenceFolderExists()
            {
                if (!Directory.Exists(PersistenceDirectory))
                {
                    Directory.CreateDirectory(PersistenceDirectory);
                }
            }
    
            public static void EnsureAllrfeFileExists()
            {
                string fileName = IOHelper.GetAllrfesFileName();
                if (!File.Exists(fileName))
                {
                    XElement root = new XElement("requestForExperts");
                    root.Save(fileName);
                }
            }
        }

    此类提供了Xml的路径查询以及确定Xml等文件存放的位置,默认存放在Temp临时文件夹下边,可以自定义修改,默认路径应该为 C:\Users\用户名\AppData\Local\Temp\FilePersistenceProvider下,每一个工作流有
    2个xml,一个为业务数据对象的xml,一个为工作流对象的xml。

    最后要修改一下,就是宿主的地方,看到了在调用持久化设置的时候传递的是Xml枚举值:

            public WorkflowApplication CreateAndRun(RequestForExpert rfe)
            {
               
                IDictionary<string, object> inputs = new Dictionary<string, object>();
                inputs.Add("InRequestForExpert", rfe);
                // 实例化工作流对象
                Activity wf = new ExpertWorkFlow();
                WorkflowApplication instance = new WorkflowApplication(wf, inputs);
                instance.PersistableIdle += OnIdleAndPersistable;
                instance.Completed += OnWorkflowCompleted;
                instance.Idle += OnIdle;
                //持久化设置
                GetSqlInstanceStore(instance, StoreType.Xml);
                instance.Run();
                return instance;
            }
            public WorkflowApplication LoadInstance(Guid instanceId)
            {
                WorkflowApplication instance = new WorkflowApplication(new ExpertWorkFlow());
                instance.Completed += OnWorkflowCompleted;
                instance.PersistableIdle += OnIdleAndPersistable;
                instance.Idle += OnIdle;
                //持久化设置
                GetSqlInstanceStore(instance, StoreType.Xml);
                instance.Load(instanceId);
                return instance;
            }

    好了,说了这么多,可能大家还不是太理解,大家可以对比源码来分析代码的用处,多提意见

    下载源码:WF持久化操作

     
  • 相关阅读:
    react 编写日历组件
    腾讯地图打开地图选取位置 withMap
    react 动态修改 document.title
    axios 中断请求
    react 使用 react-loadable分包
    http Authorization
    @media screen媒体查询
    CSS规范
    布局和WEB性能
    CSS 标签的分类,及显示模式
  • 原文地址:https://www.cnblogs.com/ListenFly/p/2754727.html
Copyright © 2011-2022 走看看