演练:为 CHECK 约束创建自定义数据生成器

更新:2007 年 11 月

在 Visual Studio Team System Database Edition 中,可以使用标准数据生成器在列中填充数据。如果要填充的列已定义了一个 CHECK 约束,则填充该列的数据必须满足该 CHECK 约束。标准数据生成器可以生成满足许多 CHECK 约束的数据。例如,如果您有一个要求日期位于某个范围内的 CHECK 约束,则您可以使用标准 DateTime 生成器并设置 Min 和 Max 属性来满足该 CHECK 约束。

但是,标准数据生成器并不能满足所有的 CHECK 约束。例如,如果 CHECK 约束要求日期位于两个不同范围中的任一范围内,则不能使用标准 DateTime 生成器。在本演练中,您将创建一个能够满足这种约束的自定义数据生成器。该生成器接受两个范围作为其输入,并生成一个位于其中一个范围内的随机日期。

说明:

您可以通过其他方法来创建自定义数据生成器,并使用聚合扩展性来实现同样的目标。有关更多信息,请参见演练:创建聚合标准生成器的自定义数据生成器

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

  • 创建从 Generator 继承的类。

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

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

  • 重写 OnInitialize 方法,以便为 Random 对象提供种子并使生成器具有确定性。

  • 重写 OnGenerateNextValues 方法以生成数据。

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

先决条件

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

  • Database Edition

创建自定义数据生成器类

创建自定义数据生成器类

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

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

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

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

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

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

    Imports Microsoft.VisualStudio.TeamSystem.Data.DataGenerator
    Imports System.Data.SqlTypes
    
    using Microsoft.VisualStudio.TeamSystem.Data.DataGenerator;
    using System.Data.SqlTypes;
    
  6. 将该类从 Class1 重命名为 GeneratorDateRanges,并指定该类从 Generator 继承。

    警告:

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

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

添加输入属性

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

添加输入属性

  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(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(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(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(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(TypeConverter = typeof(SqlDateTimeConverter))]
    public SqlDateTime Range1Min
    {
        set {range1MinValue = value;}
        get {return range1MinValue;}
    }
    
    [Input(TypeConverter = typeof(SqlDateTimeConverter))]
    public SqlDateTime Range1Max
    {
        set {range1MaxValue = value;}
        get {return range1MaxValue;}
    }
    
    [Input(TypeConverter = typeof(SqlDateTimeConverter))]
    public SqlDateTime Range2Min
    {
        set {range2MinValue = value;}
        get {return range2MinValue;}
    }
    
    [Input(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 方法

生成随机数据时,数据可以是确定性的,也可以是非确定性的。每次从同一个种子生成的确定性数据都是相同的。所有的数据生成器都有一个可以由用户设置的 Seed 属性。可以重写 OnInitialize 方法,以便为 Random 对象提供种子并使生成器具有确定性。

重写 OnInitialize 方法

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

    Dim random As Random
    Dim randomRange As Random
    
    Random random;
    Random randomRange;
    
  2. 重写 OnInitialize 方法。

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

重写 OnGenerateNextValues 方法

Database Edition 调用生成器的 OnGenerateNextValues 方法来创建它需要的数据。您必须重写此方法,才能为输出属性提供用于生成随机日期的逻辑。

重写 OnGenerateNextValues 方法

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

    Protected Overrides Sub OnGenerateNextValues()
    
        Dim min As SqlDateTime
        Dim max As SqlDateTime
    
        '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
            min = range1MinValue
            max = range1MaxValue
        Else
            min = range2MinValue
            max = range2MaxValue
        End If
    
        'The formula for creating a random number in a specific range is:
        'start of range + (size of range * random number between 0 and 1)
    
        'size of range
        Dim range As TimeSpan = max.Value - min.Value
    
        '(size of range * random number between 0 and 1)
        Dim randomNumber As TimeSpan = New TimeSpan(CLng(range.Ticks * random.NextDouble()))
    
        'start of range + (size of range * random number between 0 and 1)
        randomDateValue = min + randomNumber
    
    End Sub
    
    protected override void OnGenerateNextValues()
    {
        SqlDateTime min;
        SqlDateTime max;
    
        //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
        {
            min = range1MinValue;
            max = range1MaxValue;
        }
        else
        {
            min = range2MinValue;
            max = range2MaxValue;
        }
    
        //The formula for creating a random number in a specific range is:
        //start of range + (size of range * random number between 0 and 1)
    
        //size of range
        TimeSpan range = max.Value - min.Value;
    
        //(size of range * random number between 0 and 1)
        TimeSpan randomNumber = new TimeSpan((long)(range.Ticks * random.NextDouble()));
    
        //start of range + (size of range * random number between 0 and 1)
        randomDateValue = min + randomNumber;
    }
    
  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 继承。

     [Visual Basic]
    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 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 override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        bool result = false;
        if (sourceType == typeof(string))
        {
            result = true;
        }
        else
        {
            result = base.CanConvertFrom(context, sourceType);
        }
        return result;
    }
    
    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
    {
        if (destinationType == typeof(string))
        {
            return true;
        }
        return base.CanConvertTo(context, destinationType);
    }
    
  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 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 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. 在“项目”菜单上,单击“GeneratorDateRanges 属性”打开项目属性。

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

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

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

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

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

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

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

安全性

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

后续步骤

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

请参见

任务

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

概念

数据生成器扩展性概述

参考

Microsoft.VisualStudio.TeamSystem.Data.DataGenerator

其他资源

创建自定义数据生成器

使用标准数据生成器

数据生成器演练