Procédure pas à pas : étendre une build de projet de base de données pour générer des statistiques de modèle
Vous pouvez créer un collaborateur de génération pour exécuter des actions personnalisées lorsque vous générez un projet de base de données. Dans cette procédure pas à pas, créez un collaborateur de génération nommé ModelStatistics qui extrait des statistiques du modèle de base de données lorsque vous générez un projet de base de données. Étant donné que ce collaborateur de génération adopte des paramètres lors de la génération, d'autres étapes sont requises.
Dans cette procédure pas à pas, vous effectuerez les principales tâches suivantes :
Créer un collaborateur de génération
Installer le collaborateur de génération
Tester votre collaborateur de génération
Composants requis
Pour exécuter cette procédure pas à pas, vous devez disposer des composants suivants :
Vous devez avoir installé Visual Studio 2010 Premium ou Visual Studio 2010 Ultimate.
Vous devez disposer d'un projet de base de données qui contient des objets de base de données.
Notes
Cette procédure pas à pas est destinée aux utilisateurs qui sont déjà familiarisés avec les fonctionnalités de base de données de Visual Studio Premium. Vous êtes également censé connaître les concepts Visual Studio de base, tels que les modes de création d'une bibliothèque de classes et d'utilisation de l'éditeur de code pour ajouter du code à une classe.
Créer un collaborateur de génération
Pour créer un collaborateur de génération, vous devez effectuer les tâches suivantes :
Créer un projet de bibliothèque de classes et ajouter les références requises
Définir une classe nommée ModelStatistics qui hérite de BuildContributor
Substituer les méthodes OnPopulateArguments et OnExecute
Ajouter quelques méthodes d'assistance privées
Générer l'assembly obtenu
Notes
Ce collaborateur ne donnera une sortie que lorsqu'un projet de base de données est généré à l'aide de MSBuild. Le rapport est désactivé par défaut, mais vous pouvez le substituer en fournissant une propriété sur la ligne de commande MSBuild. Pour obtenir un exemple de l'activation de la sortie dans la fenêtre Sortie, consultez Procédure pas à pas : étendre un déploiement de projet de base de données pour analyser le plan de déploiement.
Pour créer un projet de bibliothèque de classes
Créez un projet de bibliothèque de classes Visual Basic ou Visual C# nommé MyBuildContributor.
Dans l'Explorateur de solutions, cliquez avec le bouton droit sur le nœud du projet, puis cliquez sur Ajouter une référence.
Cliquez sur l'onglet .NET.
Mettez en surbrillance les entrées Microsoft.Data.Schema et Microsoft.Data.Schema.Sql, puis cliquez sur OK.
Commencez ensuite à ajouter du code à la classe.
Pour définir la classe ModelStatistics
Dans l'éditeur de code, mettez à jour le fichier class1.cs pour qu'il inclue les instructions using ou Imports suivantes :
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
Mettez à jour la définition de classe comme suit :
/// <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
À présent, vous avez défini votre collaborateur de génération et utilisé l'attribut pour indiquer que ce collaborateur est compatible avec tous les fournisseurs de schémas de base de données qui héritent de SqlDatabaseSchemaProvider.
Ajoutez ensuite les membres suivants. Vous utiliserez les membres pour permettre à ce fournisseur d'accepter des paramètres de build par ligne de commande :
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
Ces membres permettent à l'utilisateur de spécifier si des statistiques doivent être générées à l'aide de l'option GenerateModelStatistics et comment celles-ci doivent être classées en indiquant l'option SortModelStatisticsBy.
Ensuite, substituez la méthode OnPopulateArguments pour générer la liste d'arguments à passer au collaborateur de génération.
Pour substituer OnPopulateArguments
Ajoutez la méthode override suivante à la 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
Générez deux objets ContributorArgumentConfiguration et ajoutez-les à la liste d'arguments.
Substituez ensuite la méthode OnExecute pour ajouter le code que vous souhaitez exécuter lors de la génération d'un projet de base de données.
Pour substituer OnExecute
Ajoutez la méthode suivante à votre 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
Un objet BuildContributorContext qui fournit l'accès à tous les arguments spécifiés, au modèle des bases de données, aux propriétés de la build et aux fichiers d'extension, est passé à la méthode OnExecute. Dans cet exemple, nous récupérons le modèle, puis appelons des fonctions d'assistance aux informations de sortie concernant le modèle. Un ErrorManager à utiliser pour signaler toutes les erreurs qui se produisent est également passé à la méthode.
D'autres types et méthodes intéressants sont les suivants : DataSchemaModel, ModelStore, GetElements, GetAllAnnotations, GetCustomData et ModelElement.
Ensuite, définissez les méthodes d'assistance qui examinent les informations du modèle.
Pour ajouter des méthodes d'assistance qui génèrent les statistiques
Tout d'abord, ajoutez les squelettes des quatre méthodes d'assistance en ajoutant le code suivant :
/// <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
Pour définir le corps de la méthode ParseArguments
Ajoutez le code suivant au corps de la méthode 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
Les types et méthodes intéressants sont les suivants : ErrorManager et DataSchemaError.
Pour définir le corps de la méthode SummarizeModelInfo
Ajoutez le code suivant au corps de la méthode 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)
Ces types de clé et membres sont les suivants : DataSchemaModel, ModelStore, DatabaseSchemaProvider et ModelCollation.
Pour ajouter le corps à la méthode Summarize
Ajoutez le code suivant au corps de la méthode 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)
Une fois encore, les commentaires de code fournissent les informations pertinentes.
Pour ajouter le corps à la méthode SurveyRelationships
Ajoutez le code suivant au corps de la méthode 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)
Les commentaires de code expliquent les aspects clés de cette méthode. Les types et méthodes référencés à retenir sont les suivants : DataSchemaModel, ModelStore, GetElements et ModelElement.
Pour ajouter le corps de la méthode OutputResult
Ajoutez le corps suivant à la méthode 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)
Enregistrez les modifications apportées au fichier Class1.cs.
Générez ensuite la bibliothèque de classes.
Pour signer et générer l'assembly
Dans le menu Projet, cliquez sur Propriétés MyBuildContributor.
Cliquez sur l'onglet Signature.
Cliquez sur Signer l'assembly.
Dans la zone Choisir un fichier de clé de nom fort, cliquez sur <Nouveau>.
Dans la boîte de dialogue Créer une clé de nom fort, en guise de nom du fichier de clé, tapez MaCléRéf.
(Facultatif) Vous pouvez spécifier un mot de passe pour votre fichier de clé de nom fort.
Cliquez sur OK.
Dans le menu Fichier, cliquez sur Enregistrer tout.
Dans le menu Générer, cliquez sur Générer la solution.
Ensuite, vous devez installer et inscrire l'assembly afin qu'il soit chargé lorsque vous générez des projets de base de données.
Installer un collaborateur de génération
Pour installer un collaborateur de génération, vous devez effectuer les tâches suivantes :
Copier l'assembly et le fichier .pdb associé dans le dossier Extensions
Créer un fichier Extensions.xml pour inscrire le collaborateur de génération afin qu'il soit chargé lorsque vous générez des projets de base de données
Pour installer l'assembly MyBuildContributor
Créez un dossier nommé MesExtensions dans le dossier %Program Files%\Microsoft Visual Studio 10.0\VSTSDB\Extensions.
Copiez votre assembly signé (MyBuildContributor.dll) dans le dossier %Program Files%\Microsoft Visual Studio 10.0\VSTSDB\Extensions\MesExtensions.
Notes
Évitez de copier directement vos fichiers XML dans le dossier %Program Files%\Microsoft Visual Studio 10.0\VSTSDB\Extensions. L'intérêt d'utiliser un sous-dossier est d'empêcher toute modification accidentelle apportée aux autres fichiers fournis avec Visual Studio Premium.
Veuillez maintenant enregistrer votre assembly, un type d'extension de fonctionnalité, pour le faire apparaître dans Visual Studio Premium.
Pour inscrire l'assembly MyBuildContributor
Dans le menu Affichage, cliquez sur Autres fenêtres, puis cliquez sur Fenêtre Commande pour ouvrir la fenêtre Commande.
Dans la fenêtre Commande, tapez le code suivant. Pour FilePath, substituez le chemin d'accès et le nom de votre fichier .dll compilé. Placez le chemin d'accès et le nom de fichier entre guillemets.
Notes
Par défaut, le chemin d'accès de votre fichier .dll compilé est CheminVotreSolution\bin\Debug ou CheminVotreSolution\bin\Release.
? System.Reflection.Assembly.LoadFrom("FilePath").FullName
? System.Reflection.Assembly.LoadFrom(@"FilePath").FullName
Appuyez sur Entrée.
Copiez la ligne résultante dans le Presse-papiers. Cette ligne doit se présenter comme suit :
"MyBuildContributor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=nnnnnnnnnnnnnnnn"
Ouvrez un éditeur de texte brut, tel que le Bloc-notes.
Important
Sur Windows Vista et Microsoft Windows Server 2008, ouvrez l'éditeur en tant qu'administrateur afin de pouvoir enregistrer le fichier dans votre dossier Program Files.
Fournissez les informations suivantes. Vous pouvez coller les informations que vous avez copiées à l'étape 4. Spécifiez vos propres nom d'assembly, jeton de clé publique et type d'extension :
<?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>
Utilisez ce fichier XML pour inscrire la classe qui hérite de BuildContributor.
Enregistrez le fichier sous le nom MyBuildContributor.extensions.xml dans le dossier %Program Files%\Microsoft Visual Studio 10.0\VSTSDB\Extensions\MesExtensions.
Notes
Dans Type de fichier, vérifiez que vous avez spécifié Tous les fichiers, ou le Bloc-notes ignorera l'extension et enregistrera les fichiers avec l'extension .txt.
Fermez Visual Studio.
Vous allez ensuite générer un projet de base de données pour tester votre collaborateur.
Tester votre collaborateur de génération
Pour tester votre collaborateur de génération, vous devez effectuer les tâches suivantes :
Ajouter des propriétés au fichier .dbproj que vous envisagez de générer
Générer le projet de base de données en utilisant MSBuild et en fournissant les paramètres appropriés
Ajouter des propriétés au fichier projet de base de données (.dbproj)
Étant donné que ce collaborateur de génération accepte les paramètres de ligne de commande de MSBuild, vous devez modifier le projet de base de données pour permettre aux utilisateurs de passer ces paramètres via MSBuild. Vous pouvez le faire de deux façons. Vous pouvez modifier manuellement le fichier .dbproj pour ajouter les arguments requis. Vous pouvez choisir de le faire si vous ne générez que votre projet de base de données à l'aide de MSBuild. Si vous choisissez cette option, ajoutez les instructions suivantes au fichier .dbproj entre le dernier nœud </ItemGroup > du fichier et le nœud </Project> final :
<ItemGroup>
<BuildContributorArgument Include="OutDir=$(OutDir)" />
<BuildContributorArgument Include="GenerateModelStatistics=$(GenerateModelStatistics)" />
<BuildContributorArgument Include="SortModelStatisticsBy=$(SortModelStatisticsBy)" />
</ItemGroup>
La méthode la plus simple consiste à charger votre projet de base de données dans Visual Studio, à générer le projet de base de données une fois, puis à quitter Visual Studio. Enregistrez les modifications apportées au projet lorsque vous quittez. Si vous utilisez cette méthode, les arguments supplémentaires sont automatiquement ajoutés à votre fichier de projet de base de données (.dbproj).
Après avoir suivi l'une de ces approches, vous pouvez utiliser MSBuild pour passer les paramètres pour les générations à partir de la ligne de commande.
Pour ajouter des propriétés au fichier de projet de base de données :
Ouvrez le projet de base de données dans Visual Studio. Pour plus d'informations, consultez Comment : ouvrir un projet de base de données ou serveur.
Générez votre projet de base de données. Pour plus d'informations, consultez Comment : générer un projet de base de données pour générer un fichier de schéma compilé (.dbschema).
Fermez Visual Studio. Enregistrez votre solution et votre projet lorsque vous y êtes invité.
Ensuite, vous pouvez générer votre projet de base de données en utilisant MSBuild et en spécifiant des arguments pour permettre à votre collaborateur de génération de générer des statistiques de modèle.
Générer le projet de base de données
Pour régénérer votre projet de base de données à l'aide de MSBuild et générer des statistiques
Ouvrez une invite de commandes Visual Studio. Dans le menu Démarrer, cliquez successivement sur Tous les programmes, Microsoft Visual Studio 2010, Visual Studio Tools, puis sur Invite de commandes de Visual Studio (2010).
À l'invite de commandes, naviguez jusqu'au dossier qui contient votre projet de base de données.
À l'invite de commandes, tapez la ligne de commande suivante :
MSBuild /t:Rebuild MyDatabaseProject.dbproj /p:GenerateModelStatistics=true /p:SortModelStatisticsBy=name /p:OutDir=.\
Remplacez MyDatabaseProject par le nom du projet de base de données que vous souhaitez générer. Si vous aviez modifié le projet après sa dernière génération, vous auriez pu utiliser /t:Build au lieu de /t:Rebuild.
Des résultats semblables aux suivants s'affichent :
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
Ouvrez ModelStatistics.xml et examinez-en le contenu.
Les résultats signalés sont également rendus persistants dans le fichier XML.
Étapes suivantes
Vous pouvez créer des outils supplémentaires pour exécuter le traitement du fichier XML de sortie. Il s'agit juste d'un exemple de collaborateur de génération. Par exemple, vous pouvez créer un collaborateur de génération pour exporter un fichier de dictionnaire de données dans le cadre de votre génération.
Voir aussi
Concepts
Extension des fonctionnalités de base de données de Visual Studio