Condividi tramite


Procedura dettagliata: estendere la compilazione del progetto di database per generare statistiche dal modello

È possibile creare un collaboratore alla distribuzione per eseguire azioni personalizzate quando si compila un progetto di database. In questa procedura dettagliata so creerà un collaboratore alla compilazione denominato ModelStatistics che genera statistiche dal modello del database quando si compila un progetto di database. Poiché questo collaboratore alla compilazione accetta parametri in fase di compilazione, sono richiesti alcuni passaggi aggiuntivi.

In questa procedura dettagliata si completeranno le seguenti attività principali:

  • Creare un collaboratore alla compilazione

  • Installare il collaboratore alla compilazione

  • Testare il collaboratore alla compilazione

Prerequisiti

Per completare la procedura dettagliata, è necessario disporre dei componenti seguenti:

  • È necessario avere installato Visual Studio 2010 Premium o Visual Studio 2010 Ultimate.

  • È necessario disporre di un progetto di database contenente oggetti di database.

Nota

Questa procedura dettagliata è destinata a utenti che hanno già familiarità con le funzionalità di database di Visual Studio Premium. È inoltre necessario conoscere i concetti di base di Visual Studio, ad esempio come creare una libreria di classi e come utilizzare l'editor di codice per aggiungere codice a una classe.

Creare un collaboratore alla compilazione

Per creare un collaboratore alla compilazione, è necessario effettuare le attività seguenti:

  • Creare un progetto Libreria di classi e aggiungere riferimenti obbligatori

  • Definire una classe denominata ModelStatistics che eredita da BuildContributor

  • Eseguire l'override dei metodi OnPopulateArguments e OnExecute

  • Aggiungere alcuni metodi di supporto privati

  • Compilare l'assembly risultante

Nota

Questo collaboratore restituirà un output solo quando un progetto di database viene compilato tramite MSBuild. Il rapporto viene disattivato per impostazione predefinita, ma è possibile eseguirne l'override fornendo una proprietà nella riga di comando di MSBuild. Per un esempio di come abilitare l'output nella finestra di output, vedere Procedura dettagliata: estendere la distribuzione del progetto di database per analizzare il piano di distribuzione.

Per creare un progetto Libreria di classi

  1. Creare un progetto Libreria di classi di Visual Basic or Visual C# denominato MyBuildContributor.

  2. In Esplora soluzioni fare clic con il pulsante destro del mouse sul nodo del progetto, quindi scegliere Aggiungi riferimento.

  3. Fare clic sulla scheda .NET.

  4. Evidenziare le voci Microsoft.Data.Schema e Microsoft.Data.Schema.Sql e fare clic su OK.

    Iniziare quindi ad aggiungere codice alla classe.

Per definire la classe ModelStatistics

  1. Nell'editor di codice aggiornare il file class1.cs affinché corrisponda alle seguenti istruzioni 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. Aggiornare la definizione della classe come segue:

    /// <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
    

    A questo punto è stato definito il collaboratore alla compilazione e utilizzato l'attributo per indicare che questo collaboratore è compatibile con qualsiasi provider dello schema di database che eredita da SqlDatabaseSchemaProvider.

  3. Aggiungere quindi i membri seguenti. Si utilizzeranno i membri per consentire a questo provider di accettare parametri di compilazione da riga di comando:

            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
    

    Questi membri consentono all'utente di specificare se le statistiche devono essere generate tramite l'opzione GenerateModelStatistics e specificare come devono essere ordinate le statistiche tramite l'opzione SortModelStatisticsBy.

    Si eseguirà quindi l'override del metodo OnPopulateArguments per compilare l'elenco di argomenti da passare al collaboratore alla compilazione.

Per eseguire l'override di OnPopulateArguments

  • Aggiungere il seguente metodo di override alla classe 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
    

    Si compileranno due oggetti ContributorArgumentConfiguration e si aggiungeranno all'elenco di argomenti.

    Si eseguirà quindi l'override del metodo OnExecute per aggiungere il codice che si desidera eseguire quando viene compilato un progetto di database.

Per eseguire l'override di OnExecute

  • Aggiungere il seguente metodo alla classe 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
    

    Al metodo OnExecute viene passato un oggetto BuildContributorContext che fornisce l'accesso a qualsiasi argomento specificato, al modello del database, alle proprietà di compilazione e ai file di estensione. In questo esempio, si recupererà il modello, quindi si chiameranno funzioni di supporto per restituire informazioni sul modello. Al metodo viene passato anche un oggetto ErrorManager da utilizzare per segnalare gli eventuali errori che dovessero verificarsi.

    Tipi e metodi di interesse aggiuntivi includono i seguenti: DataSchemaModel, ModelStore, GetElements, GetAllAnnotations, GetCustomData e ModelElement.

    Si definiranno quindi i metodi di supporto che esaminano i dettagli del modello.

Per aggiungere metodi di supporto che generano le statistiche

  • Aggiungere innanzitutto scheletri dei quattro metodi di supporto tramite il codice seguente:

            /// <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
    

Per definire il corpo del metodo ParseArguments

  • Aggiungere il seguente codice al corpo del metodo 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
    

    Tipi e metodi di interesse includono: ErrorManager e DataSchemaError.

Per definire il corpo del metodo SummarizeModelInfo

  • Aggiungere il seguente codice al corpo del metodo 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)
    

    Tipi e membri principali, in questo caso, includono i seguenti: DataSchemaModel, ModelStore, DatabaseSchemaProvider e ModelCollation.

Per aggiungere il corpo al metodo Summarize

  • Aggiungere il seguente codice al corpo del metodo 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)
    

    I commenti del codice forniscono di nuovo le informazioni rilevanti.

Per aggiungere il corpo al metodo SurveyRelationships

  • Aggiungere il seguente codice al corpo del metodo 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)
    

    I commenti del codice spiegano gli aspetti principali di questo metodo. Tipi di riferimento e metodi di interesse includono i seguenti: DataSchemaModel, ModelStore, GetElements e ModelElement.

Per aggiungere il corpo del metodo OutputResult

  • Aggiungere il corpo seguente al metodo 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)
    
  • Salvare le modifiche a Class1.cs.

    Compilare quindi la libreria di classi.

Per firmare e compilare l'assembly

  1. Scegliere Proprietà di MyBuildContributor dal menu Progetto.

  2. Fare clic sulla scheda Firma.

  3. Fare clic su Firma assembly.

  4. In Scegli un file chiave con nome sicuro fare clic su <Nuovo>.

  5. Nella finestra di dialogo Crea chiave con nome sicuro digitare MyRefKey nel campo Nome file di chiave.

  6. (Facoltativo) È possibile specificare una password per il file di chiave con nome sicuro.

  7. Scegliere OK.

  8. Scegliere Salva tutto dal menu File.

  9. Scegliere Compila soluzione dal menu Compila.

    Sarà quindi necessario installare e registrare l'assembly in modo che venga caricato quando si compilano progetti di database.

Installare un collaboratore alla compilazione

Per installare un collaboratore alla compilazione, è necessario effettuare le attività seguenti:

  • Copiare l'assembly e il file con estensione pdb associato nella cartella Extensions

  • Creare un file Extensions.xml per registrare il collaboratore alla compilazione in modo che venga caricato quando si compilano progetti di database

Per installare l'assembly MyBuildContributor

  1. Creare una cartella denominata MyExtensions nella cartella %Programmi%\Microsoft Visual Studio 10.0\VSTSDB\Extensions.

  2. Copiare l'assembly firmato (MyBuildContributor.dll) nella cartella %Programmi%\Microsoft Visual Studio 10.0\VSTSDB\Extensions\MyExtensions.

    Nota

    Si consiglia di non copiare i file XML direttamente nella cartella %Programmi%\Microsoft Visual Studio 10.0\VSTSDB\Extensions. Se si utilizza una sottocartella, si eviterà che vengano apportate modifiche accidentali agli altri file forniti con Visual Studio Premium.

    Sarà quindi necessario registrare l'assembly, un tipo di estensione di funzionalità, in modo tale che venga visualizzato in Visual Studio Premium.

Per registrare l'assembly MyBuildContributor

  1. Scegliere Altre finestre dal menu Visualizza, quindi fare clic su Finestra di comando per aprire la finestra Comando.

  2. Nella finestra Comando digitare il codice seguente. Sostituire FilePath con il percorso e il nome del file con estensione dll compilato. Racchiudere il percorso e il nome del file tra virgolette.

    Nota

    Per impostazione predefinita, il percorso del file con estensione dll compilato è PercorsoSoluzione\bin\Debug o PercorsoSoluzione\bin\Release.

    ? System.Reflection.Assembly.LoadFrom("FilePath").FullName
    
    ? System.Reflection.Assembly.LoadFrom(@"FilePath").FullName
    
  3. Premere Invio.

  4. Copiare negli Appunti la riga risultante, che dovrebbe essere simile alla seguente:

    "MyBuildContributor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=nnnnnnnnnnnnnnnn"
    
  5. Aprire un editor di testo, ad esempio Blocco note.

    Nota importanteImportante

    In Windows Vista e in Microsoft Windows Server 2008 aprire l'editor come amministratore in modo da poter salvare il file nella cartella Programmi.

  6. Fornire le seguenti informazioni. È possibile incollare le informazioni copiate al passaggio 4. Specificare nome dell'assembly, token di chiave pubblica e tipo di estensione:

    <?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>
    

    Questo file XML viene utilizzato per registrare la classe che eredita da BuildContributor.

  7. Salvare il file con il nome MyBuildContributor.extensions.xml nella cartella %Programmi%\Microsoft Visual Studio 10.0\VSTSDB\Extensions\MyExtensions.

    Nota

    In Salva come verificare di avere specificato Tutti i file. In caso contrario, in Blocco note l'estensione verrà ignorata e il file verrà salvato con estensione txt.

  8. Chiudere Visual Studio.

    Si compilerà quindi un progetto di database per testare il collaboratore.

Testare il collaboratore alla compilazione

Per testare il collaboratore alla compilazione, è necessario effettuare le attività seguenti:

  • Aggiungere proprietà al file con estensione dbproj che si prevede di compilare.

  • Compilare il progetto di database tramite MSBuild e fornendo i parametro adatti

Aggiungere proprietà al file del progetto di database (con estensione dbproj).

Poiché questo collaboratore alla compilazione accetta parametri della riga di comando da MSBuild, è necessario modificare il progetto di database per consentire agli utenti di passare tali parametri tramite MSBuild. Questa operazione può essere eseguita in due modi. È possibile modificare manualmente il file con estensione dbproj per aggiungere gli argomenti obbligatori. È possibile scegliere di procedere in questo modo se si compila solo il progetto di database tramite MSBuild. Se si sceglie questa opzione, aggiungere le istruzioni seguenti al file con estensione dbproj tra l'ultimo nodo </ItemGroup > nel file e il nodo </Progetto> finale:

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

Il metodo più facile consiste nel caricare il progetto di database in Visual Studio, compilare il progetto di database una volta, quindi uscire da Visual Studio. Al momento dell'uscita, salvare le modifiche al progetto. Se si utilizza questo metodo, gli argomenti aggiuntivi vengono inclusi automaticamente al file progetto del database (con estensione dbproj).

Dopo avere seguito uno di questi approcci, è possibile utilizzare MSBuild per passare parametri per le compilazioni da riga di comando.

Per aggiungere proprietà al file del progetto di database

  1. Aprire il progetto di database in Visual Studio. Per ulteriori informazioni, vedere Procedura: aprire un progetto server o di database.

  2. Compilare il progetto di database. Per ulteriori informazioni, vedere Procedura: compilare un progetto di database per generare un file di schema compilato (con estensione dbschema).

  3. Chiudere Visual Studio. Salvare la soluzione e il progetto quando viene richiesto.

    Sarà quindi possibile compilare il progetto di database tramite MSBuild e specificare argomenti per consentire al collaboratore alla compilazione di generare statistiche del modello.

Compilare il progetto di database

Per ricompilare il progetto di database tramite MSBuild e generare statistiche

  1. Aprire un prompt dei comandi di Visual Studio. Fare clic sul pulsante Start, scegliere Tutti i programmi, quindi Microsoft Visual Studio 2010, Visual Studio Tools e fare clic su Prompt dei comandi di Visual Studio 2010.

  2. Al prompt dei comandi individuare la cartella che contiene il progetto di database.

  3. Al prompt dei comandi digitare la riga di comando seguente:

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

    Sostituire MyDatabaseProject con il nome del progetto di database che si desidera compilare. Se il progetto fosse stato modificato dopo l'ultima compilazione, è possibile utilizzare /t:Build anziché /t:Rebuild.

    Verrà visualizzato un output analogo al seguente:

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. Aprire ModelStatistics.xml ed esaminare il contenuto.

    I risultati indicati sono salvati in modo permanente anche al file XML.

Passaggi successivi

È possibile creare strumenti aggiuntivi per eseguire l'elaborazione del file XML di output. Si tratta solo di un esempio di un collaboratore alla compilazione. È possibile, ad esempio, creare un collaboratore alla compilazione per restituire un file del dizionario dei dati nell'ambito della compilazione.

Vedere anche

Concetti

Estensione delle funzionalità di database di Visual Studio

Altre risorse

Personalizzare compilazione e distribuzione del database tramite collaboratori alla compilazione e distribuzione

Procedura dettagliata: estendere la distribuzione del progetto di database per analizzare il piano di distribuzione