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持久化操作

 
posted @ 2012-11-05 14:09  wangyafei_it  阅读(2358)  评论(7编辑  收藏  举报