演练:创建聚合标准生成器的自定义数据生成器

更新:2007 年 11 月

在 Visual Studio Team System Database Edition 中,您可以在自定义数据生成器类中创建标准数据生成器类的实例。使用此方法,您可以轻松地减少自定义数据生成器中必需包括的逻辑的数量。例如,您可能希望创建一个可以生成与多个复杂模式相匹配的随机字符串数据的生成器。您可以创建一个包含处理多个模式的逻辑的自定义数据生成器,还可以使用标准 RegularExpression 生成器来处理复杂的模式匹配。

在本演练中,您将创建一个聚合标准 DateTime 生成器的自定义数据生成器。该生成器所生成的数据将位于两个不同的日期范围中的一个范围之内。该生成器接受两个不同的范围作为其输入,并生成一个位于其中一个范围内的随机日期。

说明:

有关该自定义数据生成器的目标的更多信息以及如何使用常规扩展性来实现同样的目标,请参见演练:为 CHECK 约束创建自定义数据生成器

在本演练中,您将执行下列任务:

  • 创建从 Generator 继承的类。

  • 创建输入属性,以便用户可以指定两个日期范围。

  • 创建要用作生成器输出的输出属性。

  • 创建两个标准 DateTime 生成器实例,它们分别代表两个可能的范围中的一个。

  • 重写 Generator 方法,并在这些方法内将工作委托给标准生成器。

  • 用强名称对生成器进行签名。

先决条件

若要完成此演练,需要以下组件:

  • Database Edition

创建自定义数据生成器类

创建自定义数据生成器类

  1. 在 Visual Studio 中,用所选语言创建一个类库项目并将它命名为“GeneratorDateRanges2”。

  2. 在“项目”菜单上单击“添加引用”。

    随即出现“添加引用”对话框。

  3. 单击“.NET”选项卡。在“组件名称”列表中,单击“Microsoft.VisualStudio.TeamSystem.Data”,再单击“确定”。

  4. 在“项目”菜单上单击“添加引用”。

    随即出现“添加引用”对话框。

  5. 单击“浏览”选项卡,然后浏览至如下位置:...\Program Files\Microsoft Visual Studio 9.0\DBPro\Extensions。

  6. 单击“Microsoft.VisualStudio.TeamSystem.Data.Generators.dll”,再单击“确定”。

  7. (可选,仅限 Visual Basic)在“解决方案资源管理器”中,单击“显示所有文件”,再展开“引用”节点以验证新引用。

  8. 在“代码”窗口顶部的类声明之前,添加下面的代码行:

    Imports Microsoft.VisualStudio.TeamSystem.Data.DataGenerator
    Imports Microsoft.VisualStudio.TeamSystem.Data.Generators
    Imports System.Data.SqlTypes
    
    using Microsoft.VisualStudio.TeamSystem.Data.DataGenerator;
    using Microsoft.VisualStudio.TeamSystem.Data.Generators;
    using System.Data.SqlTypes;
    
  9. 将该类从 Class1 重命名为 GeneratorDateRanges2,并指定该类从 Generator 继承,如下面的示例所示。

    警告:

    默认情况下,为该类指定的名称将显示在“列详细信息”窗口上的“生成器”列中。所指定的名称不应当与标准生成器或其他自定义生成器的名称冲突。

    Public Class GeneratorDateRanges2
        Inherits Generator
    
    End Class
    
    public class GeneratorDateRanges2: Generator
    {
    }
    
  10. 在“文件”菜单上,单击“全部保存”。

添加输入属性

这个自定义数据生成器接受两个日期范围作为其输入。若要指定每个范围,用户需要分别为每个范围指定最小日期和最大日期。因此,共需创建四个输入属性:两个最小日期和两个最大日期。

添加输入属性

  1. 创建四个成员变量来存放这两个日期范围的最小日期和最大日期,如下面的示例所示。

    Dim range1MinValue As SqlDateTime
    Dim range1MaxValue As SqlDateTime
    
    Dim range2MinValue As SqlDateTime
    Dim range2MaxValue As SqlDateTime
    
    SqlDateTime range1MinValue;
    SqlDateTime range1MaxValue;
    
    SqlDateTime range2MinValue;
    SqlDateTime range2MaxValue;
    
  2. 创建四个属性来设置这两个日期范围的最小日期和最大日期,如下面的示例所示。这些属性必须具有 InputAttribute 才能被识别为输入属性。

    <Input(Visible:= true, TypeConverter:= GetType(SqlDateTimeConverter))> _
    Public Property Range1Min() As SqlDateTime
        Set(ByVal value As SqlDateTime)
            range1MinValue = value
        End Set
        Get
            Return range1MinValue
        End Get
    End Property
    
    <Input(Visible:= true, TypeConverter:= GetType(SqlDateTimeConverter))> _
    Public Property Range1Max() As SqlDateTime
        Set(ByVal value As SqlDateTime)
            range1MaxValue = value
        End Set
        Get
            Return range1MaxValue
        End Get
    End Property
    
    <Input(Visible:= true, TypeConverter:= GetType(SqlDateTimeConverter))> _
    Public Property Range2Min() As SqlDateTime
        Set(ByVal value As SqlDateTime)
            range2MinValue = value
        End Set
        Get
            Return range2MinValue
        End Get
    End Property
    
    <Input(Visible:= true, TypeConverter:= GetType(SqlDateTimeConverter))> _
    Public Property Range2Max() As SqlDateTime
        Set(ByVal value As SqlDateTime)
            range2MaxValue = value
        End Set
        Get
            Return range2MaxValue
        End Get
    End Property
    
    [Input(Visible = true, TypeConverter = typeof(SqlDateTimeConverter))]
    public SqlDateTime Range1Min
    {
        set {range1MinValue = value;}
        get {return range1MinValue;}
    }
    
    [Input(Visible = true, TypeConverter = typeof(SqlDateTimeConverter))]
    public SqlDateTime Range1Max
    {
        set {range1MaxValue = value;}
        get {return range1MaxValue;}
    }
    
    [Input(Visible = true, TypeConverter = typeof(SqlDateTimeConverter))]
    public SqlDateTime Range2Min
    {
        set {range2MinValue = value;}
        get {return range2MinValue;}
    }
    
    [Input(Visible = true, TypeConverter = typeof(SqlDateTimeConverter))]
    public SqlDateTime Range2Max
    {
        set {range2MaxValue = value;}
        get {return range2MaxValue;}
    }
    
  3. 在“文件”菜单上,单击“全部保存”。

添加输出属性

这个自定义数据生成器会返回一个随机日期作为其输出。因此,必须创建一个输出属性。

添加输出属性

  1. 创建一个成员变量来存放作为输出的随机日期,如下面的示例所示。

    Dim randomDateValue As SqlDateTime
    
    SqlDateTime randomDateValue;
    
  2. 创建一个属性来返回作为输出的随机日期,如下面的示例所示。该属性必须具有 OutputAttribute 才能被识别为输出属性。

    <Output()> _
    Public ReadOnly Property RandomDate() As SqlDateTime
        Get
            Return randomDateValue
        End Get
    End Property
    
    [Output]
    public SqlDateTime RandomDate
    {
        get {return randomDateValue;}
    }
    
  3. 在“文件”菜单上单击“全部保存”。

重写 OnInitialize 方法

重写 OnInitialize 方法

  1. 创建一个成员变量来生成随机数,如下面的示例所示。该变量用来在两个可能的日期范围之间随机选择。

    Dim randomRange As Random
    
    Random randomRange;
    
  2. 创建并实例化作为标准的 DateTime 生成器的两个成员变量,如下面的示例所示。

    Dim range1 As DatabaseDateTime = New DatabaseDateTime()
    Dim range2 As DatabaseDateTime = New DatabaseDateTime()
    
    DatabaseDateTime range1 = new DatabaseDateTime();
    DatabaseDateTime range2 = new DatabaseDateTime();
    
  3. 重写 OnInitialize 方法,如下面的示例所示。在该方法中,为 Random 对象提供种子并使生成器具有确定性。还可以调用标准生成器的 Initialize 方法。

    Protected Overrides Sub OnInitialize(ByVal initInfo As GeneratorInit)
    
        randomRange = New Random(Me.Seed)  'deterministic
    
        range1.Initialize(initInfo)
        range2.Initialize(initInfo)
    
        MyBase.OnInitialize(initInfo)
    End Sub
    
    protected override void OnInitialize(GeneratorInit initInfo)
    {
        randomRange = new Random(this.Seed);  //deterministic
    
        range1.Initialize(initInfo);
        range2.Initialize(initInfo);
    
        base.OnInitialize(initInfo);
    }
    
  4. 在“文件”菜单上,单击“全部保存”。

重写其他方法

重写其他方法

  1. 重写 OnSetInputValues,如下面的示例所示。此方法的 inputs 参数是由用户设置的所有标准生成器属性(如“种子”和“Null 百分比”)的 IDictionary。您调用标准生成器的 SetInputValues 方法,以便将这些值传递给它们。然后,使用在该数据生成器中创建的自定义输入属性来设置每个标准生成器的 Min 和 Max 属性。

    Protected Overrides Sub OnSetInputValues(ByVal inputs As IDictionary(Of String, Object))
    
        'It is important to call MyBase.OnSetInputValues first to get the inputs
        'from the Properties window first.
        '--------------------------------------------------------------------------
        MyBase.OnSetInputValues(inputs)
    
        range1.SetInputValues(inputs)
        range2.SetInputValues(inputs)
    
        range1.Min = range1MinValue
        range1.Max = range1MaxValue
        range2.Min = range2MinValue
        range2.Max = range2MaxValue
    
        range1.Distribution = New Uniform()
        range2.Distribution = New Uniform()
    End Sub
    
    protected override void OnSetInputValues(IDictionary<string, object> inputs)
    {
        //It is important to call base.OnSetInputValues first to get the inputs
        //from the Properties window first.
        //-------------------------------------------------------------------------
        base.OnSetInputValues(inputs);
    
        range1.SetInputValues(inputs);
        range2.SetInputValues(inputs);
    
        range1.Min = range1MinValue;
        range1.Max = range1MaxValue;
        range2.Min = range2MinValue;
        range2.Max = range2MaxValue;
    
        range1.Distribution = new Uniform();
        range2.Distribution = new Uniform();
    }
    
  2. 重写 OnValidateInputs 以验证输入,如下面的示例所示。

    Protected Overrides Sub OnValidateInputs()
    
        range1.ValidateInputs()
        range2.ValidateInputs()
    
        MyBase.OnValidateInputs()
    End Sub
    
    protected override void OnValidateInputs()
    {
        range1.ValidateInputs();
        range2.ValidateInputs();
    
        base.OnValidateInputs();
    }
    
  3. 重写 Dispose(Boolean) 方法以清理标准生成器,如下面的示例所示。

    Protected Overrides Sub Dispose(ByVal disposing As Boolean)
    
        range1.Dispose()
        range2.Dispose()
    
        MyBase.Dispose(disposing)
    End Sub
    
    protected override void Dispose(bool disposing)
    {
        range1.Dispose();
        range2.Dispose();
    
        base.Dispose(disposing);
    }
    
  4. 在“文件”菜单上单击“全部保存”。

重写 OnGenerateNextValues 方法

Database Edition 调用生成器的 OnGenerateNextValues 方法来创建它需要的数据。必须重写此方法来提供为输出属性生成随机日期的逻辑。在本演练中,您将生成随机日期的职责委托给标准 DateTime 生成器。

重写 OnGenerateNextValues 方法

  1. 重写 OnGenerateNextValues 方法,如下面的示例所示。

    Protected Overrides Sub OnGenerateNextValues()
    
        'Generate a random date from either range 1 or range 2.
        'Randomly select either range 1 or range 2 by randomly 
        'generating an odd or an even random number.
        '------------------------------------------------------------
        If (randomRange.Next() Mod 2 = 0) Then  'check for odd or even
    
            'the standard generator does the work
            range1.GenerateNextValues()
            randomDateValue = range1.Result.Value
        Else
            'the standard generator does the work
            range2.GenerateNextValues()
            randomDateValue = range2.Result.Value
        End If
    
        MyBase.OnGenerateNextValues()
    End Sub
    
    protected override void OnGenerateNextValues()
    {
        //Generate a random date from either range 1 or range 2.
        //Randomly select either range 1 or range 2 by randomly 
        //generating an odd or an even random number.
        //------------------------------------------------------------
        if (randomRange.Next() % 2 == 0)  //check for odd or even
        {
            //the standard generator does the work
            range1.GenerateNextValues();
            randomDateValue = range1.Result.Value;
        }
        else
        {
            //the standard generator does the work
            range2.GenerateNextValues();
            randomDateValue = range2.Result.Value;
        }
    
        base.OnGenerateNextValues();
    }
    
  2. 在“文件”菜单上单击“全部保存”。

定义类型转换器

若要在“属性”窗口中为该数据生成器指定输入属性,必须提供一个用来在输入值和 SqlDateTime 类型之间来回转换的类型转换器。

创建 SqlDateTime 类型转换器类

  1. 在“项目”菜单上单击“添加类”。

    即会出现“添加新项”对话框。

  2. 在“名称”中键入 SqlDateTimeConverter。

  3. 在“代码”窗口顶部的类声明之前,添加下面的代码行:

    Imports System.ComponentModel
    Imports System.Data.SqlTypes
    Imports System.Globalization
    
    using System.ComponentModel;
    using System.Data.SqlTypes;
    using System.Globalization;
    
  4. 将该类从 Class1 重命名为 GeneratorDateRanges,并指定该类从 TypeConverter 继承。

    Public Class SqlDateTimeConverter
        Inherits TypeConverter
    
    End Class
    
    public class SqlDateTimeConverter: TypeConverter
    {
    }
    
  5. 在类声明内部添加类构造函数。如果是在 Visual Basic 中编写类型转换器类,请跳至步骤 6。

    public SqlDateTimeConverter()
    {
    }
    
  6. 在类构造函数之后,添加一个方法来验证该类型转换器是否可以实现特定的转换。

    Public Overrides Function CanConvertFrom(ByVal context As ITypeDescriptorContext, ByVal sourceType As Type) As Boolean
        Dim result As Boolean
        result = False
        If (sourceType Is GetType(System.String)) Then
            result = True
        Else
            result = MyBase.CanConvertFrom(context, sourceType)
        End If
        Return result
    End Function 
    
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        bool result = false;
        if (sourceType == typeof(string))
        {
            result = true;
        }
        else
        {
            result = base.CanConvertFrom(context, sourceType);
        }
        return result;
    }
    
  7. 最后添加转换器方法。

    Public Overrides Function ConvertFrom(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal culture As System.Globalization.CultureInfo, ByVal value As Object) As Object
        Dim dateTimeString As String
        dateTimeString = value.ToString
        If (dateTimeString.Length > 0) Then
            Dim dateTime As Date
            dateTime = Date.Parse(dateTimeString, culture)
            Return New SqlDateTime(dateTime)
        End If
        Return MyBase.ConvertFrom(context, culture, value)
    End Function
    
    Public Overrides Function CanConvertTo(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal destinationType As System.Type) As Boolean
        If (destinationType Is GetType(System.String)) Then
            Return True
        End If
        Return MyBase.CanConvertTo(context, destinationType)
    End Function
    
    Public Overrides Function ConvertTo(ByVal context As System.ComponentModel.ITypeDescriptorContext, ByVal culture As System.Globalization.CultureInfo, ByVal value As Object, ByVal destinationType As System.Type) As Object
        If (destinationType Is GetType(System.String)) Then
            Dim dateTime As Date
            dateTime = CType(value, SqlDateTime).Value
            dateTime.ToString(culture)
        End If
        Return MyBase.ConvertTo(context, culture, value, destinationType)
    End Function 
    
            public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
            {
                string dateTimeString = value as string;
                if (dateTimeString != null)
                {
                    DateTime dateTime = DateTime.Parse(dateTimeString, culture);
                    return new SqlDateTime(dateTime);
                }
                return base.ConvertFrom(context, culture, value);
            }
    
            public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
            {
                if (destinationType == typeof(string))
                {
                    return true;
                }
                return base.CanConvertTo(context, destinationType);
            }
    
            public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
            {
                if (destinationType == typeof(string))
                {
                    DateTime dateTime = ((SqlDateTime)value).Value;
                    dateTime.ToString(culture);
                }
                return base.ConvertTo(context, culture, value, destinationType);
            }
    
  8. 在“文件”菜单上单击“全部保存”。

对生成器进行签名

所有的自定义数据生成器都必须先使用强名称进行签名才能注册。

使用强名称对生成器进行签名

  1. 在“项目”菜单上,单击“GeneratorDateRanges2 属性”打开项目属性。

  2. 在“签名”选项卡上,选中“为程序集签名”复选框。

  3. 在“选择强名称密钥文件”框中,单击“<新建...>”。

  4. 在“密钥文件名称”框中,键入“GeneratorDateRanges2Key”,键入并确认密码,然后单击“确定”。

    在生成解决方案时,将使用密钥文件对程序集进行签名。

  5. 在“文件”菜单上单击“全部保存”。

  6. 在“生成”菜单上,单击“生成解决方案”。

    现在已经生成了数据生成器。接下来您必须在自己的计算机上注册它,以便可以在数据生成计划中使用它。

安全性

有关更多信息,请参见数据生成器的安全性

后续步骤

现在已经生成了数据生成器,接下来您必须在自己的计算机上注册它。有关更多信息,请参见下列主题之一:

请参见

任务

演练:部署自定义数据生成器

概念

数据生成器扩展性概述

参考

Microsoft.VisualStudio.TeamSystem.Data.DataGenerator

其他资源

创建自定义数据生成器

使用标准数据生成器

数据生成器演练