Compartir a través de


Tutorial: Extender la compilación del proyecto de base de datos para generar estadísticas del modelo

Puede crear un colaborador de compilación para realizar acciones personalizadas al compilar un proyecto de base de datos. En este tutorial creará un colaborador de compilación denominado ModelStatistics que genera estadísticas a partir del modelo de base de datos al compilar un proyecto de base de datos. Dado que este colaborador de compilación toma parámetros al compilar, se necesitan algunos pasos adicionales.

En este tutorial llevará a cabo las siguientes tareas principales:

  • Crear un colaborador de compilación

  • Instalar el colaborador de compilación

  • Probar su colaborador de compilación

Requisitos previos

Necesita los componentes siguientes para completar este tutorial:

  • Debe tener instalado Visual Studio 2010 Premium o Visual Studio 2010 Ultimate.

  • Debe tener un proyecto de base de datos que contenga objetos de base de datos.

Nota

Este tutorial está dirigido a los usuarios que ya están familiarizados con las características de base de datos de Visual Studio Premium. También se espera que esté familiarizado con los conceptos básicos de Visual Studio, como, por ejemplo, la forma de crear una biblioteca de clases y de usar el editor de código para agregar código a una clase.

Crear un colaborador de compilación

Para crear un colaborador de compilación, debe realizar las siguientes tareas:

  • Crear un proyecto de biblioteca de clases y agregar las referencias necesarias

  • Definir una clase denominada ModelStatistics que herede de BuildContributor

  • Reemplazar los métodos OnExecute y OnPopulateArguments

  • Agregar unos cuantos métodos auxiliares privados

  • Compilar el ensamblado resultante

Nota

Este colaborador solo se generará cuando se compile un proyecto de base de datos usando MSBuild. El informe está desactivado de forma predeterminada, pero puede invalidarlo proporcionando una propiedad en la línea de comandos de MSBuild. Para ver un ejemplo de cómo habilitar la salida en la Ventana de salida, vea Tutorial: Extender la implementación del proyecto de base de datos para analizar el plan de implementación.

Para crear un proyecto de biblioteca de clases

  1. Cree un proyecto de biblioteca de clases de Visual Basic o Visual C# denominado MyBuildContributor.

  2. En el Explorador de soluciones, haga clic con el botón secundario en el nodo del proyecto y, a continuación, haga clic en Agregar referencia.

  3. Haga clic en la ficha .NET.

  4. Resalte las entradas Microsoft.Data.Schema.Sql y Microsoft.Data.Schema y haga clic en Aceptar.

    A continuación, comience a agregar código a la clase.

Para definir la clase ModelStatistics

  1. En el editor de código, actualice el archivo class1.cs para que coincida con las siguientes instrucciones using o Imports:

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Xml.Linq;
    using Microsoft.Data.Schema;
    using Microsoft.Data.Schema.Build;
    using Microsoft.Data.Schema.Extensibility;
    using Microsoft.Data.Schema.SchemaModel;
    using Microsoft.Data.Schema.Sql;
    
    Imports System
    Imports System.Collections.Generic
    Imports System.IO
    Imports System.Linq
    Imports System.Xml.Linq
    Imports Microsoft.Data.Schema
    Imports Microsoft.Data.Schema.Build
    Imports Microsoft.Data.Schema.Extensibility
    Imports Microsoft.Data.Schema.SchemaModel
    Imports Microsoft.Data.Schema.Sql
    
  2. Actualice la definición de clase para que coincida con la siguiente:

    /// <summary>
    /// The ModelStatistics class demonstrates
    /// how you can create a class that inherits BuildContributor
    /// to perform actions when you build a database project.
    /// </summary>
    [DatabaseSchemaProviderCompatibility(typeof(SqlDatabaseSchemaProvider))]
        public sealed class ModelStatistics : BuildContributor
        {
        }
    
    ''' <summary>
    ''' The ModelStatistics class demonstrates
    ''' how you can create a class that inherits BuildContributor
    ''' to perform actions when you build a database project.
    ''' </summary>
    <DatabaseSchemaProviderCompatibility(GetType(SqlDatabaseSchemaProvider))> _
    Public NotInheritable Class ModelStatistics
        Inherits BuildContributor
    End Class
    

    Ahora ha definido su colaborador de compilación y ha utilizado el atributo para indicar que este colaborador es compatible con cualquier proveedor de esquemas de base de datos que herede de SqlDatabaseSchemaProvider.

  3. A continuación, agregue los siguientes miembros. Utilizará los miembros para permitir a este proveedor aceptar parámetros de compilación de línea de comandos:

            private const string GenerateModelStatistics = "GenerateModelStatistics";
            private const string SortModelStatisticsBy = "SortModelStatisticsBy";
            private const string GenerateModelStatisticsVariable = "$(" + GenerateModelStatistics + ")";
            private const string SortModelStatisticsByVariable = "$(" + SortModelStatisticsBy + ")";
    
            private enum SortBy { None, Name, Value };
            private static Dictionary<string, SortBy> SortByMap = new Dictionary<string, SortBy>(StringComparer.OrdinalIgnoreCase)
            {
                { "none", SortBy.None },
                { "name", SortBy.Name },
                { "value", SortBy.Value },
            };
    
            private SortBy _sortBy = SortBy.None;
    
        Private Const GenerateModelStatistics As String = "GenerateModelStatistics"
        Private Const SortModelStatisticsBy As String = "SortModelStatisticsBy"
        Private Const GenerateModelStatisticsVariable As String = "$(" & GenerateModelStatistics & ")"
        Private Const SortModelStatisticsByVariable As String = "$(" & SortModelStatisticsBy & ")"
    
        Private Enum SortBy
            None
            Name
            Value
        End Enum
        '' SortByMap maps the command-line parameter string values to the enumeration values
        Private Shared SortByMap As New Dictionary(Of String, SortBy)(StringComparer.OrdinalIgnoreCase)
    
        Private _sortBy As SortBy = SortBy.None
    

    Estos miembros le permiten especificar si las estadísticas se deberían generar mediante la opción GenerateModelStatistics y cómo deberían ordenarse las estadísticas al especificar la opción SortModelStatisticsBy.

    A continuación, reemplace el método OnPopulateArguments para compilar la lista de argumentos que se va a pasar al colaborador de compilación.

Para reemplazar OnPopulateArguments

  • Agregue el siguiente método de reemplazo a la clase ModelStatistics:

        /// <summary>
        /// Override the OnPopulateArgument method to build a list of arguments from the input
        /// configuration information.
        /// </summary>
            protected override IList<ContributorArgumentConfiguration> OnPopulateArguments()
            {
                List<ContributorArgumentConfiguration> args = new List<ContributorArgumentConfiguration>();
    
                args.Add(new ContributorArgumentConfiguration(
                    name: GenerateModelStatistics,
                    value: GenerateModelStatisticsVariable,
                    condition: null));
    
                args.Add(new ContributorArgumentConfiguration(
                    name: SortModelStatisticsBy,
                    value: SortModelStatisticsByVariable,
                    condition: null));
    
                return args;
            }
    
        ''' <summary>
        ''' Override the OnPopulateArgument method to build a list of arguments from the input
        ''' configuration information.
        ''' </summary>
        Protected Overrides Function OnPopulateArguments() As System.Collections.Generic.IList(Of Microsoft.Data.Schema.Build.ContributorArgumentConfiguration)
            Dim args As List(Of ContributorArgumentConfiguration) = New List(Of ContributorArgumentConfiguration)
    
            args.Add(New ContributorArgumentConfiguration(name:=GenerateModelStatistics, _
                                                           value:=GenerateModelStatisticsVariable, _
                                                           condition:=Nothing))
            args.Add(New ContributorArgumentConfiguration(name:=SortModelStatisticsBy, _
                                                           value:=SortModelStatisticsByVariable, _
                                                           condition:=Nothing))
            Return MyBase.OnPopulateArguments()
        End Function
    

    Compile dos objetos ContributorArgumentConfiguration y agréguelos a la lista de argumentos.

    A continuación, reemplace el método OnExecute para agregar el código que desea que se ejecute cuando se compile un proyecto de base de datos.

Para reemplazar OnExecute

  • Agregue el siguiente método a su clase ModelStatistics:

        /// <summary>
        /// Override the OnExecute method to perform actions when you build a database project.
        /// </summary>
            protected override void OnExecute(BuildContributorContext context, ErrorManager errorsContainer)
            {
                // handle related arguments, passed in as part of
                // the context information.
                bool generateModelStatistics;
                ParseArguments(context.Arguments, errorsContainer, out generateModelStatistics);
    
                // Only generate statistics if requested to do so
                if (generateModelStatistics)
                {
                    // First, output model-wide information, such
                    // as the type of database schema provider (DSP)
                    // and the collation.
                    List<DataSchemaError> args = new List<DataSchemaError>();
                    args.Add(new DataSchemaError(" ", ErrorSeverity.Message));
                    args.Add(new DataSchemaError("Model Statistics:", ErrorSeverity.Message));
                    args.Add(new DataSchemaError("=================", ErrorSeverity.Message));
                    args.Add(new DataSchemaError(" ", ErrorSeverity.Message));
                    errorsContainer.Add(args, ErrorManager.DefaultCategory);
    
                    var model = context.Model;
    
                    // Start building up the XML that will later
                    // be serialized.
                    var xRoot = new XElement("ModelStatistics");
    
                    SummarizeModelInfo(model, xRoot, errorsContainer);
    
                    // First, count the elements that are contained 
                    // in this model.
                    var elements = model.GetElements(typeof(IModelElement), ModelElementQueryFilter.Internal);
                    Summarize(elements, element => element.ElementClass.ClassName, "Internal Elements", xRoot, errorsContainer);
    
                    // Now, count the elements that are defined in
                    // another model. Examples include built-in types,
                    // roles, filegroups, assemblies, and any 
                    // referenced objects from another database.
                    elements = model.GetElements(typeof(IModelElement), ModelElementQueryFilter.External);
                    Summarize(elements, element => element.ElementClass.ClassName, "External Elements", xRoot, errorsContainer);
    
                    // Now, count the number of each type
                    // of relationship in the model.
                    SurveyRelationships(model, xRoot, errorsContainer);
    
                    // Count the various types of annotations
                    // in the model.
                    var annotations = model.GetAllAnnotations();
                    Summarize(annotations, anno => anno.AnnotationClass.ClassName, "Annotations", xRoot, errorsContainer);
    
                    // finally, count any types of custom data
                    // defined for the model.
                    var customData = model.GetCustomData();
                    Summarize(customData, custom => custom.Category, "Custom Data", xRoot, errorsContainer);
    
                    // Determine where the user wants to save
                    // the serialized XML file.
                    string outDir;
                    if (context.Arguments.TryGetValue("OutDir", out outDir) == false)
                    {
                        outDir = ".";
                    }
                    var filePath = Path.Combine(outDir, "ModelStatistics.xml");
                    // Save the XML file and tell the user
                    // where it was saved.
                    xRoot.Save(filePath);
                    DataSchemaError resultArg = new DataSchemaError("Result was saved to " + filePath, ErrorSeverity.Message);
                    errorsContainer.Add(resultArg, ErrorManager.BuildCategory);
                }
            }
    
    ''' <summary>
    ''' Override the OnExecute method to perform actions when you build a database project.
    ''' </summary>
    Protected Overloads Overrides Sub OnExecute(ByVal context As BuildContributorContext, ByVal errorsContainer As ErrorManager)
        ' handle related arguments, passed in as part of
        ' the context information.
        Dim generateModelStatistics As Boolean
        ParseArguments(context.Arguments, errorsContainer, generateModelStatistics)
    
        ' Only generate statistics if requested to do so
        If generateModelStatistics Then
            ' First, output model-wide information, such
            ' as the type of database schema provider (DSP)
            ' and the collation.
            Dim args As New List(Of DataSchemaError)()
            args.Add(New DataSchemaError(" ", ErrorSeverity.Message))
            args.Add(New DataSchemaError("Model Statistics:", ErrorSeverity.Message))
            args.Add(New DataSchemaError("=================", ErrorSeverity.Message))
            args.Add(New DataSchemaError(" ", ErrorSeverity.Message))
            errorsContainer.Add(args, ErrorManager.DefaultCategory)
    
            Dim model = context.Model
    
            ' Start building up the XML that will later
            ' be serialized.
            Dim xRoot = New XElement("ModelStatistics")
    
            SummarizeModelInfo(model, xRoot, errorsContainer)
    
            ' First, count the elements that are contained 
            ' in this model.
            Dim elements = model.GetElements(GetType(IModelElement), ModelElementQueryFilter.Internal)
            Summarize(elements, Function(element) element.ElementClass.ClassName, "Internal Elements", xRoot, errorsContainer)
    
            ' Now, count the elements that are defined in
            ' another model. Examples include built-in types,
            ' roles, filegroups, assemblies, and any 
            ' referenced objects from another database.
            elements = model.GetElements(GetType(IModelElement), ModelElementQueryFilter.External)
            Summarize(elements, Function(element) element.ElementClass.ClassName, "External Elements", xRoot, errorsContainer)
    
            ' Now, count the number of each type
            ' of relationship in the model.
            SurveyRelationships(model, xRoot, errorsContainer)
    
            ' Count the various types of annotations
            ' in the model.
            Dim annotations = model.GetAllAnnotations()
            Summarize(annotations, Function(anno) anno.AnnotationClass.ClassName, "Annotations", xRoot, errorsContainer)
    
            ' finally, count any types of custom data
            ' defined for the model.
            Dim customData = model.GetCustomData()
            Summarize(customData, Function([custom]) [custom].Category, "Custom Data", xRoot, errorsContainer)
    
            ' Determine where the user wants to save
            ' the serialized XML file.
            Dim outDir As String
            If context.Arguments.TryGetValue("OutDir", outDir) = False Then
                outDir = "."
            End If
            Dim filePath = Path.Combine(outDir, "ModelStatistics.xml")
            ' Save the XML file and tell the user
            ' where it was saved.
            xRoot.Save(filePath)
            Dim resultArg As New DataSchemaError("Result was saved to " & filePath, ErrorSeverity.Message)
            errorsContainer.Add(resultArg, ErrorManager.BuildCategory)
        End If
    End Sub
    

    Se pasa un objeto BuildContributorContext que proporciona acceso a cualquier argumento especificado, el modelo de base de datos, propiedades de compilación y archivos de extensión al método OnExecute. En este ejemplo, recuperamos el modelo y, a continuación, llamamos a las funciones auxiliares para generar información sobre el modelo. También se pasa un objeto ErrorManager al método para utilizarlo para notificar cualquier error que se produzca.

    Entre los tipos y métodos adicionales de interés se incluyen los siguientes: DataSchemaModel, ModelStore, GetElements, GetAllAnnotations, GetCustomData y ModelElement.

    A continuación, defina los métodos auxiliares que examinan los detalles del modelo.

Para agregar métodos auxiliares que generan las estadísticas

  • Primero, agregue los esqueletos de los cuatro métodos auxiliares agregando el siguiente código:

            /// <summary>
            /// Examine the arguments provided by the user
            /// to determine if model statistics should be generated
            /// and, if so, how the results should be sorted.
            /// </summary>
            private void ParseArguments(IDictionary<string, string> arguments, ErrorManager errorsContainer, out bool generateModelStatistics)
            {
            }
    
            /// <summary>
            /// Retrieve the database schema provider for the
            /// model and the collation of that model.
            /// Results are output to the console and added to the XML
            /// being constructed.
            /// </summary>
            private static void SummarizeModelInfo(DataSchemaModel model, XElement xContainer, ErrorManager errorsContainer)
            {
            }
    
            /// <summary>
            /// For a provided list of model elements, count the number
            /// of elements for each class name, sorted as specified
            /// by the user.
            /// Results are output to the console and added to the XML
            /// being constructed.
            /// </summary>
            private void Summarize<T>(IList<T> set, Func<T, string> groupValue, string category, XElement xContainer, ErrorManager errorsContainer)
            {
            }
    
            /// <summary>
            /// Iterate over all model elements, counting the
            /// styles and types for relationships that reference each 
            /// element
            /// Results are output to the console and added to the XML
            /// being constructed.
            /// </summary>
            private static void SurveyRelationships(DataSchemaModel model, XElement xContainer, ErrorManager errorsContainer)
            {
            }
    
            /// <summary>
            /// Performs the actual output for this contributor,
            /// writing the specified set of statistics, and adding any 
            /// output information to the XML being constructed.
            /// </summary>
            private static void OutputResult<T>(string category, Dictionary<string, T> statistics, XElement xContainer, ErrorManager errorsContainer)
            {
            }
    
        ''' <summary> 
        ''' This method goes through the provided arguments to the contributor and determines what 
        ''' parameters and parameter values were provided by the user.
        ''' </summary> 
        Private Sub ParseArguments(ByVal arguments As IDictionary(Of String, String), ByVal errorsContainer As ErrorManager, ByRef generateModelStatistics__1 As Boolean)
        End Sub
    
        ''' <summary> 
        ''' This method collects and outputs information about the model itself. 
        ''' </summary> 
    Private Shared Sub SummarizeModelInfo(ByVal model As DataSchemaModel, ByVal xContainer As XElement, ByVal errorsContainer As ErrorManager)
        End Sub
    
        ''' <summary> 
        ''' This method goes counts the number of elements in specific categories from the provided element list.
        ''' </summary> 
    Private Sub Summarize(Of T)(ByVal [set] As IList(Of T), ByVal groupValue As Func(Of T, String), ByVal category As String, ByVal xContainer As XElement, ByVal errorsContainer As ErrorManager)
        End Sub
    
        ''' <summary> 
        ''' This method counts the number of relationships of each type that reference elements in the model 
        ''' </summary> 
    Private Shared Sub SurveyRelationships(ByVal model As DataSchemaModel, ByVal xContainer As XElement, ByVal errorsContainer As ErrorManager)
        End Sub
    
        ''' <summary> 
        ''' This method processes the provided data, outputting it to the console and adding the information  
        ''' to the provided XML.
        ''' </summary> 
    Private Shared Sub OutputResult(Of T)(ByVal category As String, ByVal statistics As Dictionary(Of String, T), ByVal xContainer As XElement, ByVal errorsContainer As ErrorManager)
        End Sub
    

Para definir el cuerpo del método ParseArguments

  • Agregue el código siguiente al cuerpo del método ParseArguments:

                // By default, we don't generate model statistics
                generateModelStatistics = false;
    
                // see if the user provided the GenerateModelStatistics 
                // option and if so, what value was it given.
                string valueString;
                arguments.TryGetValue(GenerateModelStatistics, out valueString);
                if (string.IsNullOrWhiteSpace(valueString) == false)
                {
                    if (bool.TryParse(valueString, out generateModelStatistics) == false)
                    {
                        generateModelStatistics = false;
    
                        // The value was not valid from the end user
                        DataSchemaError invalidArg = new DataSchemaError(
                            GenerateModelStatistics + "=" + valueString + " was not valid.  It can be true or false", ErrorSeverity.Error);
                        errorsContainer.Add(invalidArg, ErrorManager.BuildCategory);
                        return;
                    }
                }
    
                // Only worry about sort order if the user requested
                // that we generate model statistics.
                if (generateModelStatistics)
                {
                    // see if the user provided the sort option and
                    // if so, what value was provided.
                    arguments.TryGetValue(SortModelStatisticsBy, out valueString);
                    if (string.IsNullOrWhiteSpace(valueString) == false)
                    {
                        SortBy sortBy;
                        if (SortByMap.TryGetValue(valueString, out sortBy))
                        {
                            _sortBy = sortBy;
                        }
                        else
                        {
                            // The value was not valid from the end user
                            DataSchemaError invalidArg = new DataSchemaError(
                                SortModelStatisticsBy + "=" + valueString + " was not valid.  It can be none, name, or value", ErrorSeverity.Error);
                            errorsContainer.Add(invalidArg, ErrorManager.BuildCategory);
                        }
                    }
                }
    
            ' By default, we don't generate model statistics 
            generateModelStatistics__1 = False
    
            ' see if the user provided the GenerateModelStatistics 
            ' option and if so, what value was it given. 
            Dim valueString As String = ""
            arguments.TryGetValue(GenerateModelStatistics, valueString)
            If String.IsNullOrWhiteSpace(valueString) = False Then
                If Boolean.TryParse(valueString, generateModelStatistics__1) = False Then
                    generateModelStatistics__1 = False
    
                    ' The value was not valid from the end user 
                    Dim invalidArg As New DataSchemaError((GenerateModelStatistics & "=") + valueString & " was not valid. It can be true or false", ErrorSeverity.[Error])
                    errorsContainer.Add(invalidArg, ErrorManager.BuildCategory)
                    Exit Sub
                End If
            End If
    
            If SortByMap.Count = 0 Then
                '' haven't populated the map yet
                SortByMap.Add("none", SortBy.None)
                SortByMap.Add("name", SortBy.Name)
                SortByMap.Add("value", SortBy.Value)
            End If
    
            ' Only worry about sort order if the user requested 
            ' that we generate model statistics. 
            If generateModelStatistics__1 Then
                ' see if the user provided the sort option and 
                ' if so, what value was provided. 
                arguments.TryGetValue(SortModelStatisticsBy, valueString)
                If String.IsNullOrWhiteSpace(valueString) = False Then
                    Dim localSortBy As SortBy
                    If SortByMap.TryGetValue(valueString, localSortBy) Then
                        _sortBy = localSortBy
                    Else
                        ' The value was not valid from the end user 
                        Dim invalidArg As New DataSchemaError((SortModelStatisticsBy & "=") + valueString & " was not valid. It can be none, name, or value", ErrorSeverity.[Error])
                        errorsContainer.Add(invalidArg, ErrorManager.BuildCategory)
                    End If
                End If
            End If
    

    Los tipos y métodos de interés incluyen: ErrorManager y DataSchemaError.

Para definir el cuerpo del método SummarizeModelInfo

  • Agregue el código siguiente al cuerpo del método SummarizeModelInfo:

                // use a Dictionary to accumulate the information
                // that will later be output.
                var info = new Dictionary<string, string>();
    
                // Two things of interest: the database schema
                // provider for the model, and the language id and
                // case sensitivity of the collation of that
                // model
                info.Add("DSP", model.DatabaseSchemaProvider.GetType().Name);
                info.Add("Collation", string.Format("{0}({1})", model.Collation.Lcid, model.Collation.CaseSensitive ? "CS" : "CI"));
    
                // Output the accumulated information and add it to 
                // the XML.
                OutputResult("Basic model info", info, xContainer, errorsContainer);
    
        ' use a Dictionary to accumulate the information
        ' that will later be output.
        Dim info = New Dictionary(Of String, String)()
    
        ' Two things of interest: the database schema
        ' provider for the model, and the language id and
        ' case sensitivity of the collation of that
        ' model
        info.Add("DSP", model.DatabaseSchemaProvider.[GetType]().Name)
        info.Add("Collation", String.Format("{0}({1})", model.Collation.Lcid, If(model.Collation.CaseSensitive, "CS", "CI")))
    
        ' Output the accumulated information and add it to 
        ' the XML.
        OutputResult("Basic model info", info, xContainer, errorsContainer)
    

    Los tipos y miembros clave aquí incluyen: DataSchemaModel, ModelStore, DatabaseSchemaProvider y ModelCollation.

Para agregar el cuerpo al método Summarize

  • Agregue el código siguiente al cuerpo del método Summarize:

                // Use a Dictionary to keep all summarized information
                var statistics = new Dictionary<string, int>();
    
                // For each element in the provided list,
                // count items based on the specified grouping
                var groups =
                    from item in set
                    group item by groupValue(item) into g
                    select new { g.Key, Count = g.Count() };
    
                // order the groups as requested by the user
                if (this._sortBy == SortBy.Name)
                {
                    groups = groups.OrderBy(group => group.Key);
                }
                else if (this._sortBy == SortBy.Value)
                {
                    groups = groups.OrderBy(group => group.Count);
                }
    
                // build the Dictionary of accumulated statistics
                // that will be passed along to the OutputResult method.
                foreach (var item in groups)
                {
                    statistics.Add(item.Key, item.Count);
                }
    
                statistics.Add("subtotal", set.Count);
                statistics.Add("total items", groups.Count());
    
                // output the results, and build up the XML
                OutputResult(category, statistics, xContainer, errorsContainer);
    
        ' Use a Dictionary to keep all summarized information
        Dim statistics = New Dictionary(Of String, Integer)()
    
        ' For each element in the provided list,
        ' count items based on the specified grouping
        Dim groups = From item In [set] _ 
            Group item By groupValue(item)Intog _ 
            Select New ()
    
        ' order the groups as requested by the user
        If Me._sortBy = SortBy.Name Then
            groups = groups.OrderBy(Function(group) group.Key)
        ElseIf Me._sortBy = SortBy.Value Then
            groups = groups.OrderBy(Function(group) group.Count)
        End If
    
        ' build the Dictionary of accumulated statistics
        ' that will be passed along to the OutputResult method.
        For Each item In groups
            statistics.Add(item.Key, item.Count)
        Next
    
        statistics.Add("subtotal", [set].Count)
        statistics.Add("total items", groups.Count())
    
        ' output the results, and build up the XML
        OutputResult(category, statistics, xContainer, errorsContainer)
    

    De nuevo, los comentarios de código proporcionan la información pertinente.

Para agregar el cuerpo al método SurveyRelationships

  • Agregue el código siguiente al cuerpo del método SurveyRelationships:

                // get a list that contains all elements in the model
                var elements = model.GetElements(typeof(IModelElement), ModelElementQueryFilter.All);
                // We are interested in all relationships that
                // reference each element.
                var entries =
                    from element in elements
                    from entry in element.GetReferencedRelationshipEntries()
                    select entry;
    
                // initialize our counting buckets
                var single = 0;
                var many = 0;
                var composing = 0;
                var hierachical = 0;
                var peer = 0;
                var reverse = 0;
    
                // process each relationship, adding to the 
                // appropriate bucket for style and type.
                foreach (var entry in entries)
                {
                    switch (entry.RelationshipClass.ModelRelationshipCardinalityStyle)
                    {
                        case ModelRelationshipCardinalityStyle.Many:
                            ++many;
                            break;
                        case ModelRelationshipCardinalityStyle.Single:
                            ++single;
                            break;
                        default:
                            break;
                    }
    
                    switch (entry.RelationshipClass.ModelRelationshipType)
                    {
                        case ModelRelationshipType.Composing:
                            ++composing;
                            break;
                        case ModelRelationshipType.Hierarchical:
                            ++hierachical;
                            break;
                        case ModelRelationshipType.Peer:
                            ++peer;
                            break;
                        case ModelRelationshipType.Reverse:
                            // We count these, but reverse relationships
                            // are not actually stored*, so the count
                            // will always be zero.
                            // * - reverse relationships are generated
                            // dynamically when they are accessed.
                            ++reverse;
                            break;
                        default:
                            break;
                    }
                }
    
                // build a dictionary of data to pass along
                // to the OutputResult method.
                var stat = new Dictionary<string, int>();
                stat.Add("Multiple", many);
                stat.Add("Single", single);
                stat.Add("Composing", composing);
                stat.Add("Hierarchical", hierachical);
                stat.Add("Peer", peer);
                // As noted, no need to output the count of reverse
                // relationships as it will always be zero.
                //stat.Add("Reverse", reverse);
                stat.Add("subtotal", entries.Count());
    
                OutputResult("Relationships", stat, xContainer, errorsContainer);
    
            ' get a list that contains all elements in the model 
            Dim elements = model.GetElements(GetType(IModelElement), ModelElementQueryFilter.All)
            ' We are interested in all relationships that 
            ' reference each element. 
            Dim entries = From element In elements _
                From entry In element.GetReferencedRelationshipEntries() _
                Select entry
    
            ' initialize our counting buckets 
            Dim [single] = 0
            Dim many = 0
            Dim composing = 0
            Dim hierachical = 0
            Dim peer = 0
            Dim reverse = 0
    
            ' process each relationship, adding to the 
            ' appropriate bucket for style and type. 
            For Each entry In entries
                Select Case entry.RelationshipClass.ModelRelationshipCardinalityStyle
                    Case ModelRelationshipCardinalityStyle.Many
                        many += 1
                        Exit Select
                    Case ModelRelationshipCardinalityStyle.[Single]
                        [single] += 1
                        Exit Select
                    Case Else
                        Exit Select
                End Select
    
                Select Case entry.RelationshipClass.ModelRelationshipType
                    Case ModelRelationshipType.Composing
                        composing += 1
                        Exit Select
                    Case ModelRelationshipType.Hierarchical
                        hierachical += 1
                        Exit Select
                    Case ModelRelationshipType.Peer
                        peer += 1
                        Exit Select
                    Case ModelRelationshipType.Reverse
                        ' We count these, but reverse relationships 
                        ' are not actually stored*, so the count 
                        ' will always be zero. 
                        ' * - reverse relationships are generated 
                        ' dynamically when they are accessed. 
                        reverse += 1
                        Exit Select
                    Case Else
                        Exit Select
                End Select
            Next
    
            ' build a dictionary of data to pass along 
            ' to the OutputResult method. 
            Dim stat = New Dictionary(Of String, Integer)()
            stat.Add("Multiple", many)
            stat.Add("Single", [single])
            stat.Add("Composing", composing)
            stat.Add("Hierarchical", hierachical)
            stat.Add("Peer", peer)
            ' As noted, no need to output the count of reverse 
            ' relationships as it will always be zero. 
            'stat.Add("Reverse", reverse); 
            stat.Add("subtotal", entries.Count())
    
        OutputResult("Relationships", stat, xContainer, errorsContainer)
    

    Los comentarios de código explican los aspectos clave de este método. Los tipos y métodos de referencia de interés incluyen: DataSchemaModel, ModelStore, GetElements y ModelElement.

Para agregar el cuerpo del método OutputResult

  • Agregue el cuerpo siguiente al método OutputResult:

                var maxLen = statistics.Max(stat => stat.Key.Length) + 2;
                var format = string.Format("{{0, {0}}}: {{1}}", maxLen);
    
                List<DataSchemaError> args = new List<DataSchemaError>();
                args.Add(new DataSchemaError(category, ErrorSeverity.Message));
                args.Add(new DataSchemaError("-----------------", ErrorSeverity.Message));
    
                // Remove any blank spaces from the category name
                var xCategory = new XElement(category.Replace(" ", ""));
                xContainer.Add(xCategory);
    
                foreach (var item in statistics)
                {
                    //Console.WriteLine(format, item.Key, item.Value);
                    var entry = string.Format(format, item.Key, item.Value);
                    args.Add(new DataSchemaError(entry, ErrorSeverity.Message));
                    // Replace any blank spaces in the element key with
                    // underscores.
                    xCategory.Add(new XElement(item.Key.Replace(' ', '_'), item.Value));
                }
                args.Add(new DataSchemaError(" ", ErrorSeverity.Message));
                errorsContainer.Add(args, ErrorManager.BuildCategory);
    
        Dim maxLen = statistics.Max(Function(stat) stat.Key.Length) + 2
        Dim format = String.Format("{{0, {0}}}: {{1}}", maxLen)
    
        Dim args As New List(Of DataSchemaError)()
        args.Add(New DataSchemaError(category, ErrorSeverity.Message))
        args.Add(New DataSchemaError("-----------------", ErrorSeverity.Message))
    
        ' Remove any blank spaces from the category name
        Dim xCategory = New XElement(category.Replace(" ", ""))
        xContainer.Add(xCategory)
    
        For Each item In statistics
            'Console.WriteLine(format, item.Key, item.Value);
            Dim entry = String.Format(format, item.Key, item.Value)
            args.Add(New DataSchemaError(entry, ErrorSeverity.Message))
            ' Replace any blank spaces in the element key with
            ' underscores.
            xCategory.Add(New XElement(item.Key.Replace(" "c, "_"c), item.Value))
        Next
        args.Add(New DataSchemaError(" ", ErrorSeverity.Message))
        errorsContainer.Add(args, ErrorManager.BuildCategory)
    
  • Guarde los cambios en Class1.cs.

    A continuación, compile la biblioteca de clases.

Para firmar y compilar el ensamblado

  1. En el menú Proyecto, haga clic en Propiedades de MyBuildContributor.

  2. Haga clic en la ficha Firma.

  3. Haga clic en Firmar el ensamblado.

  4. En Elija un archivo de clave de nombre seguro, haga clic en <Nuevo>.

  5. En el cuadro de diálogo Crear clave de nombre seguro, en Nombre del archivo de clave, escriba MyRefKey.

  6. (opcional) Puede especificar una contraseña para el archivo de clave de nombre seguro.

  7. Haga clic en Aceptar.

  8. En el menú Archivo, haga clic en Guardar todo.

  9. En el menú Generar, haga clic en Generar solución.

    Después, debe instalar y registrar el ensamblado para que se cargue al compilar proyectos de base de datos.

Instalar un colaborador de compilación

Para instalar un colaborador de compilación, debe realizar las siguientes tareas:

  • Copiar el ensamblado y el archivo .pdb asociado en la carpeta Extensions

  • Crear un archivo Extensions.xml para registrar el colaborador de compilación de modo que se cargue al compilar proyectos de base de datos

Para instalar el ensamblado MyBuildContributor

  1. Cree una carpeta denominada MyExtensions en la carpeta %Archivos de programa%\Microsoft Visual Studio 10.0\VSTSDB\Extensions.

  2. Copie el ensamblado firmado (MyBuildContributor.dll) en la carpeta %Archivos de programa%\Microsoft Visual Studio 10.0\VSTSDB\Extensions\MyExtensions.

    Nota

    Se recomienda no copiar directamente los archivos XML a la carpeta %Program Files%\Microsoft Visual Studio 10.0\VSTSDB\Extensions. Si usa una subcarpeta en su lugar, evitará cambios accidentales en los demás archivos proporcionados con Visual Studio Premium.

    A continuación, debe registrar el ensamblado, un tipo de extensión de características, para que aparezca en Visual Studio Premium.

Para registrar el ensamblado MyBuildContributor

  1. En el menú Ver, haga clic en Otras ventanas y, a continuación, haga clic en Ventana Comandos para abrir la ventana Comando.

  2. En la ventana Comando, escriba el código siguiente. En FilePath, sustituya la ruta de acceso y el nombre de archivo por la ruta y el nombre de su archivo .dll compilado. Debe escribir la ruta de acceso y el nombre de archivo entre comillas.

    Nota

    De forma predeterminada, la ruta de acceso del archivo .dll compilado es rutaDeAccesoDeSolución\bin\Debug o rutaDeAccesoDeSolución\bin\Release.

    ? System.Reflection.Assembly.LoadFrom("FilePath").FullName
    
    ? System.Reflection.Assembly.LoadFrom(@"FilePath").FullName
    
  3. Presione Entrar.

  4. Copie la línea resultante al Portapapeles. Debe ser similar a la siguiente:

    "MyBuildContributor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=nnnnnnnnnnnnnnnn"
    
  5. Abra un editor de texto sin formato, como el Bloc de notas.

    Nota importanteImportante

    En Windows Vista y Microsoft Windows Server 2008, abra el editor como administrador para que pueda guardar el archivo en su carpeta Archivos de programa.

  6. Proporcione la información siguiente. Puede pegar la información que copió en el paso 4. Especifique su propio nombre de ensamblado, token de clave pública y tipo de extensión:

    <?xml version="1.0" encoding="utf-8" ?> 
    <extensions assembly="" version="1" xmlns="urn:Microsoft.Data.Schema.Extensions" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:Microsoft.Data.Schema.Extensions Microsoft.Data.Schema.Extensions.xsd">
      <extension type="MyBuildContributor.ModelStatistics" 
    assembly="MyBuildContributor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=<enter key here>" enabled="true" />
    </extensions>
    

    Utilice este archivo XML para registrar la clase que hereda de BuildContributor.

  7. Guarde el archivo como MyBuildContributor.extensions.xml en la carpeta %Archivos de programa%\Microsoft Visual Studio 10.0\VSTSDB\Extensions\MyExtensions.

    Nota

    En Guardar como tipo, compruebe que especificó Todos los archivos o el Bloc de notas omitirá la extensión y guardará los archivos con la extensión .txt.

  8. Cierre Visual Studio.

    A continuación, compilará un proyecto de base de datos para probar su colaborador.

Probar su colaborador de compilación

Para probar su colaborador de compilación, debe realizar las siguientes tareas:

  • Agregar propiedades al archivo .dbproj que piensa compilar

  • Compilar el proyecto de base de datos utilizando MSBuild y proporcionando los parámetros adecuados

Agregar propiedades al archivo de proyecto de base de datos (.dbproj)

Dado que este colaborador de compilación acepta parámetros de línea de comandos de MSBuild, debe modificar el proyecto de base de datos para permitir a los usuarios pasar esos parámetros a través de MSBuild. Hay dos maneras de hacerlo. Puede modificar manualmente el archivo .dbproj para agregar los argumentos necesarios. Podría optar por hacer esto si únicamente compila su proyecto de base de datos mediante MSBuild. Si elige esta opción, agregue las siguientes instrucciones al archivo .dbproj entre el último nodo </ItemGroup> del archivo y el nodo final </Project>:

  <ItemGroup>
    <BuildContributorArgument Include="OutDir=$(OutDir)" />
    <BuildContributorArgument Include="GenerateModelStatistics=$(GenerateModelStatistics)" />
    <BuildContributorArgument Include="SortModelStatisticsBy=$(SortModelStatisticsBy)" />
  </ItemGroup>

El método más fácil es cargar su proyecto de base de datos en Visual Studio, compilar el proyecto de base de datos una vez y salir de Visual Studio. Al salir, guarde los cambios realizados en el proyecto. Si utiliza este método, los argumentos adicionales se agregan automáticamente a su archivo de proyecto de base de datos (.dbproj).

Después de haber seguido uno de estos enfoques, puede utilizar MSBuild para pasar los parámetros de las compilaciones de línea de comandos.

Para agregar propiedades al archivo de proyecto de base de datos

  1. Abra el proyecto de base de datos en Visual Studio. Para obtener más información, vea Cómo: Abrir un proyecto de base de datos o de servidor.

  2. Compile su proyecto de base de datos. Para obtener más información, vea Cómo: Compilar un proyecto de base de datos para generar un archivo de esquema (.dbschema) compilado.

  3. Cierre Visual Studio. Guarde la solución y el proyecto si el sistema se lo pide.

    A continuación puede compilar su proyecto de base de datos utilizando MSBuild y especificando argumentos para permitir que su colaborador de compilación genere estadísticas del modelo.

Compilar el proyecto de base de datos

Para recompilar un proyecto de base de datos mediante el uso de MSBuild y generar estadísticas

  1. Abra un símbolo del sistema de Visual Studio. En el menú Iniciar, haga clic en Todos los programas, en Microsoft Visual Studio 2010, en Visual Studio Tools y, a continuación, en Símbolo del sistema de Visual Studio (2010).

  2. En el símbolo del sistema, navegue a la carpeta que contiene el proyecto de base de datos.

  3. En el símbolo del sistema, escriba la siguiente línea de comandos:

    MSBuild /t:Rebuild MyDatabaseProject.dbproj /p:GenerateModelStatistics=true /p:SortModelStatisticsBy=name /p:OutDir=.\
    

    Sustituya MyDatabaseProject por el nombre del proyecto de base de datos que desea compilar. Si hubiera cambiado el proyecto después de compilarlo por última vez, podría utilizar /t:Build en lugar de /t:Rebuild.

    Aparecen resultados como los siguientes:

Microsoft (R) Build Engine Version 4.0.20817.0
[Microsoft .NET Framework, Version 4.0.20817.0]
Copyright (C) Microsoft Corporation 2007. All rights reserved.

Build started 8/19/2009 2:46:04 PM.
Project "C:\Users\UserName\Documents\Visual Studio 2010\Projects\MyDatabaseProject\MyDatabaseProject\MyDatabaseProject.dbproj" on node 1 (Rebuild target(s)).
CoreClean:
  Deleting file "c:\users\UserName\documents\visual studio 2010\Projects\MyDatabaseProject\MyDatabaseProject\sql\debug\MyDatabaseProject_Script.PreDeployment.sql".
  Deleting file "c:\users\UserName\documents\visual studio 2010\Projects\MyDatabaseProject\MyDatabaseProject\sql\debug\MyDatabaseProject_Script.PostDeployment.sql".
  Deleting file "c:\users\UserName\documents\visual studio 2010\Projects\MyDatabaseProject\MyDatabaseProject\sql\debug\MyDatabaseProject_Database.sqlsettings".
  Deleting file "c:\users\UserName\documents\visual studio 2010\Projects\MyDatabaseProject\MyDatabaseProject\sql\debug\MyDatabaseProject_Database.sqldeployment".
  Deleting file "c:\users\UserName\documents\visual studio 2010\Projects\MyDatabaseProject\MyDatabaseProject\sql\debug\MyDatabaseProject_Database.sqlcmdvars".
  Deleting file "c:\users\UserName\documents\visual studio 2010\Projects\MyDatabaseProject\MyDatabaseProject\sql\debug\MyDatabaseProject.deploymanifest".
  Deleting file "c:\users\UserName\documents\visual studio 2010\Projects\MyDatabaseProject\MyDatabaseProject\sql\debug\MyDatabaseProject.dbschema".
  Deleting file "c:\users\UserName\documents\visual studio 2010\Projects\MyDatabaseProject\MyDatabaseProject\obj\Debug\MyDatabaseProject.dbschema".
DspBuild:
  Creating a model to represent the project...
  Loading project files...
  Building the project model and resolving object interdependencies...
  Validating the project model...

Model Statistics:
=================

Basic model info
-----------------
        DSP: Sql100DatabaseSchemaProvider
  Collation: 1033(CI)

Internal Elements
-----------------
                    ISql100DatabaseOptions: 1
                          ISql100Filegroup: 1
                      ISql100FullTextIndex: 3
                              ISql100Index: 95
  ISql100MultiStatementTableValuedFunction: 1
               ISql100PrimaryKeyConstraint: 71
                          ISql100Procedure: 10
                     ISql100ScalarFunction: 10
                       ISql100SimpleColumn: 481
                ISql100SubroutineParameter: 41
                              ISql100Table: 71
                   ISql100UniqueConstraint: 1
                               ISql100View: 20
                           ISql100XmlIndex: 8
                     ISql90CheckConstraint: 89
                      ISql90ComputedColumn: 302
                  ISql90DatabaseDdlTrigger: 1
                   ISql90DefaultConstraint: 152
                          ISql90DmlTrigger: 10
                                ISql90File: 3
                ISql90ForeignKeyConstraint: 90
                     ISql90FullTextCatalog: 1
                               ISql90Route: 1
                              ISql90Schema: 5
           ISql90TriggerEventTypeSpecifier: 125
                       ISql90TypeSpecifier: 524
                 ISql90UserDefinedDataType: 6
                 ISql90XmlSchemaCollection: 6
                    ISql90XmlTypeSpecifier: 8
                   ISqlDynamicColumnSource: 5
                      ISqlExtendedProperty: 1161
          ISqlFullTextIndexColumnSpecifier: 4
            ISqlIndexedColumnSpecification: 220
          ISqlScriptFunctionImplementation: 11
                                  subtotal: 3538
                               total items: 34

External Elements
-----------------
           ISql100Filegroup: 1
               ISql100Queue: 3
             ISql100Service: 3
             ISql90Assembly: 1
       ISql90AssemblySource: 1
            ISql90ClrMethod: 151
   ISql90ClrMethodParameter: 138
          ISql90ClrProperty: 16
             ISql90Contract: 6
             ISql90Endpoint: 5
          ISql90MessageType: 14
                 ISql90Role: 10
               ISql90Schema: 13
        ISql90TypeSpecifier: 305
                 ISql90User: 4
  ISql90UserDefinedDataType: 1
      ISql90UserDefinedType: 3
            ISqlBuiltInType: 32
             ISqlServerRole: 9
                   subtotal: 716
                total items: 19

Relationships
-----------------
      Multiple: 3002
        Single: 4017
     Composing: 2332
  Hierarchical: 1812
          Peer: 2875
      subtotal: 7019

Annotations
-----------------
                         ExternalPropertyAnnotation: 1475
                        ExternalReferenceAnnotation: 187
                           ExternalSourceAnnotation: 2
                         ModuleInvocationAnnotation: 20
                      ParameterOrVariableAnnotation: 68
  ResolveTimeVerifiedDanglingRelationshipAnnotation: 119
                      SqlInlineConstraintAnnotation: 1
                SqlModelBuilderResolvableAnnotation: 7825
                        SysCommentsObjectAnnotation: 52
                                           subtotal: 9749
                                        total items: 9

Custom Data
-----------------
          AnsiNulls: 1
   ClrTypesDbSchema: 1
  CompatibilityMode: 1
    ModelCapability: 1
        Permissions: 1
   QuotedIdentifier: 1
           subtotal: 6
        total items: 6

Result was saved to .\ModelStatistics.xml

  Writing model to MyDatabaseProject.dbschema...
CopyFilesToOutputDirectory:
  Copying file from "obj\Debug\MyDatabaseProject.dbschema" to ".\sql\debug\MyDatabaseProject.dbschema".
  MyDatabaseProject -> C:\Users\UserName\Documents\Visual Studio 2010\Projects\MyDatabaseProject\MyDatabaseProject\sql\debug\MyDatabaseProject.dbschema
Done Building Project "C:\Users\UserName\Documents\Visual Studio 2010\Projects\MyDatabaseProject\MyDatabaseProject\MyDatabaseProject.dbproj" (Rebuild target(s)).

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:11.20
  1. Abra ModelStatistics.xml y examine el contenido.

    Los resultados que se notificaron también se conservan en el archivo XML.

Pasos siguientes

Podría crear herramientas adicionales para realizar el procesamiento del archivo XML de resultados. Éste es simplemente un ejemplo de un colaborador de compilación. Por ejemplo, podría crear un colaborador de compilación para generar un archivo de diccionario de datos como parte de su compilación.

Vea también

Conceptos

Extender las características de base de datos de Visual Studio

Otros recursos

Personalizar la compilación e implementación de bases de datos mediante colaboradores de compilación e implementación

Tutorial: Extender la implementación del proyecto de base de datos para analizar el plan de implementación