微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

无法将自定义组件中的自定义属性保存到 DTSX 文件中

如何解决无法将自定义组件中的自定义属性保存到 DTSX 文件中

我正在尝试创建我的第一个 SSIS 自定义源组件,但我无法将自定义属性保存到 .dtsx 文件中。

根据 https://docs.microsoft.com/en-us/sql/integration-services/extending-packages-custom-objects/persisting-custom-objects ,我需要的只是实现 IDTSComponentPersist 接口,但这不起作用,LoadFromXML 和 SavetoXML 永远不会被调用。无论是在我保存文件还是加载包时。

但是,如果您的对象具有使用复杂数据类型的属性,或者 如果您想对属性值执行自定义处理,因为它们 被加载和保存,你可以实现 IDTSComponentPersist 接口及其 LoadFromXML 和 SavetoXML 方法在这方法中 从包的 XML 定义加载(或保存到)一个 XML 包含对象属性及其当前属性的片段 值。这个 XML 片段的格式没有定义;它必须只 是格式良好的 XML。

当我保存 SSIS 包并查看 XML 时,我得到了这个,没有定义数据类型,也没有值:

enter image description here

我是不是错过了一些设置?

为了简化,我创建了一个小型测试项目。原始项目尝试保存一个包含 2 个字符串和 1 个整数的结构列表,但两者都具有相同的“不正确”行为,从不调用 SavetoXML 和 LoadFromXML。

这是我的代码

using System;
using System.Collections.Generic;
using Microsoft.sqlServer.Dts.Pipeline.Wrapper;
using Microsoft.sqlServer.Dts.Pipeline;
using Microsoft.sqlServer.Dts.Runtime;
using System.Xml;
using System.ComponentModel;
using System.Globalization;
using System.Drawing.Design;
using System.Windows.Forms.Design;
using System.Windows.Forms;

namespace TestCase
{
    public class MyConverter : TypeConverter
    {
        public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
        {
            return false;
        }
        public override object ConvertTo(ITypeDescriptorContext context,CultureInfo culture,object value,Type destinationType)
        {
            if (destinationType.Name.toupper() == "STRING")
                return string.Join(",",((List<string>)value).ToArray());
            else
                return ((string)value).Split(',');
        }

        public override object ConvertFrom(ITypeDescriptorContext context,object value)
        {
            if (value.GetType().Name.toupper() == "STRING")
                return ((string)value).Split(',');
            else
                return string.Join(",((List<string>)value).ToArray());
        }
    }

    class FancyStringEditor : UITypeEditor
    {
        public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
        {
            return UITypeEditorEditStyle.Modal;
        }
        public override object EditValue(ITypeDescriptorContext context,IServiceProvider provider,object value)
        {
            var svc = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
            List<string> vals = (List<string>)value;
            string valsstr = string.Join("\r\n",vals.ToArray());
            if (svc != null)
            {
                using (var frm = new Form { Text = "Your editor here" })
                using (var txt = new TextBox { Text = valsstr,Dock = DockStyle.Fill,Multiline = true })
                using (var ok = new Button { Text = "OK",Dock = DockStyle.Bottom })
                {
                    frm.Controls.Add(txt);
                    frm.Controls.Add(ok);
                    frm.AcceptButton = ok;
                    ok.DialogResult = DialogResult.OK;
                    if (svc.ShowDialog(frm) == DialogResult.OK)
                    {
                        vals = new List<string>();
                        vals.AddRange(txt.Text.Split(new string[] { "\r\n" },StringSplitOptions.RemoveEmptyEntries));
                        value = vals;
                    }
                }
            }
            return value;
        }
    }

    [DtsPipelineComponent(ComponentType = ComponentType.sourceAdapter,CurrentVersion = 0,Description = "Test class for saving",displayName = "Test class",IconResource = "None",NoEditor = false,requiredProductLevel = Microsoft.sqlServer.Dts.Runtime.Wrapper.DTSProductLevel.DTSPL_NONE,SupportsBackPressure = false,UITypeName = "None")]
    public class TestSave : PipelineComponent,IDTSComponentPersist
    {
        private string _NbBadWordProperty = "NbBadWord";
        private string _ListBadWordsProperty = "ListBadWords";
        private List<string> _badWords;

        public IDTSCustomProperty100 _nb;
        public IDTSCustomProperty100 _list;

        public TestSave()
        {
            _badWords = new List<string>();
            _badWords.Add("Word1");
            _badWords.Add("Word2");
            _badWords.Add("Word3");
        }

        public void LoadFromXML(System.Xml.XmlElement node,IDTSInfoEvents infoEvents)
        {
            System.Windows.Forms.MessageBox.Show("Oh god! we're inside LoadFromXML!!");
        }

        public void SavetoXML(System.Xml.XmlDocument doc,IDTSInfoEvents infoEvents)
        {
            System.Windows.Forms.MessageBox.Show("Oh god! we're inside SavetoXML!!");
            XmlElement elementRoot;
            XmlNode propertyNode;

            // Create a new node to persist the object and its properties.  
            elementRoot = doc.CreateElement(String.Empty,"NBElement",String.Empty);
            XmlAttribute nbEl = doc.CreateAttribute("Nbelement");
            nbEl.Value = _badWords.Count.ToString();
            elementRoot.Attributes.Append(nbEl);

            // Save the three properties of the object from variables into XML.  
            foreach (string s in _badWords)
            {
                propertyNode = doc.CreateNode(XmlNodeType.Element,"BadWord",String.Empty);
                propertyNode.InnerText = s;
                elementRoot.AppendChild(propertyNode);
            }

            doc.AppendChild(elementRoot);
        }

        private IDTSCustomProperty100 GetCustomPropertyByName(string name)
        {
            foreach (IDTSCustomProperty100 prop in this.ComponentMetaData.CustomPropertyCollection)
                if (prop.Name.toupper() == name)
                    return prop;
            return null;
        }

        public override DTSValidationStatus Validate()
        {
            return DTSValidationStatus.VS_ISVALID;
        }
        public override void ProvideComponentProperties()
        {
            try
            {
                base.ProvideComponentProperties();

                // reset the component
                this.ComponentMetaData.OutputCollection.RemoveAll();
                this.ComponentMetaData.InputCollection.RemoveAll();

                // Add custom properties
                if (GetCustomPropertyByName(_NbBadWordProperty) == null)
                {
                    _nb = this.ComponentMetaData.CustomPropertyCollection.New();

                    _nb.Name = _NbBadWordProperty;
                    _nb.Description = "Number of bad word to filter";
                    _nb.State = DTSPersistState.PS_DEFAULT;
                    _nb.Value = _badWords.Count;

                    _nb.ExpressionType = DTSCustomPropertyExpressionType.CPET_NOTIFY;
                }

                if (GetCustomPropertyByName(_ListBadWordsProperty) == null)
                {
                    IDTSCustomProperty100 _list = this.ComponentMetaData.CustomPropertyCollection.New();
                    _list.Name = _ListBadWordsProperty;
                    _list.Description = "List of bad words";
                    _list.State = DTSPersistState.PS_DEFAULT;

                    _list.TypeConverter = typeof(MyConverter).AssemblyQualifiedname;
                    _list.Value = _badWords;
                    _list.ExpressionType = DTSCustomPropertyExpressionType.CPET_NOTIFY;
                    
                    _list.UITypeEditor = typeof(FancyStringEditor).AssemblyQualifiedname;
                }

                // add input objects
                // none

                // add output objects

                IDTSOutput100 o2 = this.ComponentMetaData.OutputCollection.New();
                o2.Name = "Dummy output";
                o2.IsSorted = false;

                foreach (IDTSCustomProperty100 p in this.ComponentMetaData.CustomPropertyCollection)
                {
                    if (p.Name == _ListBadWordsProperty)
                    {
                        MyConverter c = new MyConverter();
                        List<string> l = (List<string>)p.Value;

                        foreach (string s in l)
                        {
                            IDTSOutputColumn100 col1 = o2.OutputColumnCollection.New();
                            col1.Name = s.Trim();
                            col1.Description = "Bad word";
                            col1.SetDataTypeProperties(Microsoft.sqlServer.Dts.Runtime.Wrapper.DataType.DT_WSTR,500,0);
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                System.Windows.Forms.MessageBox.Show("Critical error: " + ex.Message);
            }
        }

    }
}

更新 1:

添加 TypeConverter 和 UITypeEditor。仍然是相同的行为(不保存“复杂”数据类型)。

当我将源组件添加到数据流时,我得到了这个,一切看起来都很好:

enter image description here

我可以编辑属性,没问题

enter image description here

enter image description here

但是当我保存 SSIS 包并查看 xml 时,该属性仍未保存并且仍然具有 System.NULL 的数据类型:

enter image description here

谢谢!

解决方法

重要说明 - 基于在 Internet 上找到的 Microsoft definition of IDTSComponentPersist InterfaceSaveToXML 的代码示例,我怀疑自定义持久性只能在自定义 SSIS 任务、连接管理器和枚举器。

好吧,请自行选择是否真的需要实现自定义对象持久化。您的自定义属性似乎很适合标准数据类型 Int32String
来自微软的重要说明 -

在实现自定义持久化时,必须持久化对象的所有属性,包括继承的属性和添加的自定义属性。

因此,您确实需要做很多工作来保留组件的所有属性,包括示例中的 LocaleID - 以防有人需要更改它。我可能会将 ListBadWords 自定义属性存储为没有自定义 XML 持久性的字符串。

在您的代码中——System.Null 数据类型问题的最可能原因是在组件初始化时调用了 ProvideComponentProperties() 方法,当它被添加到数据流时。此时属性的数据类型是动态确定的,变量_badwords还没有初始化,是一个引用类型,所以定义为Null引用。 ProvideComponentProperties() 方法用于定义自定义属性并设置其默认值,以解决您的问题 - set

if (GetCustomPropertyByName(_ListBadWordsProperty) == null)
   {
   IDTSCustomProperty100 _list = this.ComponentMetaData.CustomPropertyCollection.New();
   _list.Name = _ListBadWordsProperty;
   _list.Description = "List of bad words";
   _list.State = DTSPersistState.PS_DEFAULT;

   _list.TypeConverter = typeof(MyConverter).AssemblyQualifiedName;
   // This is the change
   _list.Value = String.Empty;
   _list.ExpressionType = DTSCustomPropertyExpressionType.CPET_NOTIFY;
                    
   _list.UITypeEditor = typeof(FancyStringEditor).AssemblyQualifiedName;
   }    

如果您打算实现自定义 XML 持久性 - 请研究 Microsoft code sample 和其他来源。保存是通过其他方式完成的。主要区别在于在组件属性的 elementRoot 中,每个属性都在其自己的 XML 节点下创建。 Node的InnerText用于存储属性值,可选Node的属性可以存储附加信息。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。