逐步解說:擴充資料庫專案組建以產生模型統計資料
您可以建立建置參與者,以在建置資料庫專案時執行自訂動作。 在這個逐步解說中,您會建立名為 ModelStatistics 的建置參與者,這個參與者會在您建置資料庫專案時,從資料庫模型輸出統計資料。 因為當您建置時,這個建置參與者需要一些參數,所以您需要執行一些額外步驟。
在這個逐步解說中,您將完成下列主要工作:
建立建置參與者
安裝建置參與者
測試建置參與者
必要條件
您需要下列元件才能完成此逐步解說:
您必須已安裝 Visual Studio 2010 Premium 或 Visual Studio 2010 Ultimate。
您必須具有包含資料庫物件的資料庫專案。
注意事項 |
---|
此逐步解說適合已經熟悉 Visual Studio Premium 資料庫功能的使用者使用。 您也必須熟悉基本的 Visual Studio 概念,例如如何建立類別庫,以及如何使用程式碼編輯器,將程式碼加入至類別。 |
建立建置參與者
若要建立建置參與者,您必須執行下列工作:
建立類別庫專案並加入必要的參考
定義名為 ModelStatistics 的類別,該類別繼承自 BuildContributor
覆寫 OnPopulateArguments 和 OnExecute 方法
加入一些私用 Helper 方法
建置產生的組件
注意事項 |
---|
只有在使用 MSBuild 建置資料庫專案時,此參與者才會輸出。 報表功能預設為關閉,但您可以在 MSBuild 命令列中提供屬性來覆寫此預設值。 如需如何在 [輸出視窗] 中啟用輸出的範例,請參閱逐步解說:擴充資料庫專案部署以分析部署計劃。 |
若要建立類別庫專案
建立名為 MyBuildContributor 的 Visual Basic 或 Visual C# 類別庫專案。
在 [方案總管] 中,以滑鼠右鍵按一下專案節點,然後按一下 [加入參考]。
按一下 [.NET] 索引標籤。
反白顯示 [Microsoft.Data.Schema] 和 [Microsoft.Data.Schema.Sql] 項目,然後按一下 [確定]。
接下來,您會開始將程式碼加入至類別。
若要定義 ModelStatistics 類別
在程式碼編輯器中,將 class1.cs 檔更新為符合下列 using 或 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
將類別定義更新為符合下列各行程式碼:
/// <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
現在,您已定義建置參與者,而且已使用屬性表示這個參與者與所有繼承自 SqlDatabaseSchemaProvider 的資料庫結構描述提供者都相容。
接下來,加入下列成員。 您會使用這些成員,讓這個提供者得以接受命令列組建參數:
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
這些成員讓使用者得以指定是否應該使用 GenerateModelStatistics 選項來產生統計資料,以及指定如何藉由指定 SortModelStatisticsBy 選項來排序統計資料。
接下來,您會覆寫 OnPopulateArguments 方法以建置要傳遞給建置參與者的引數清單。
若要覆寫 OnPopulateArguments
將下列覆寫方法加入至 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
您會建置兩個 ContributorArgumentConfiguration 物件,並將這些物件加入至引數清單。
接下來,您會覆寫 OnExecute 方法以加入要在建置資料庫專案時執行的程式碼。
若要覆寫 OnExecute
將下列方法加入至 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
OnExecute 方法接受 BuildContributorContext 物件,這個物件可用來存取任何指定的引數、資料庫模型、建置屬性和擴充檔。 在這個範例中,我們會擷取模型,然後呼叫 Helper 函式來輸出模型的相關資訊。 這個方法也接受 ErrorManager 以用來報告任何發生的錯誤。
其他要注意的型別和方法包括:DataSchemaModel、ModelStore、GetElements、GetAllAnnotations、GetCustomData 和 ModelElement。
接下來,您會定義用以檢查模型詳細資料的 Helper 方法。
若要加入會產生統計資料的 Helper 方法
首先,加入下列程式碼以加入四個 Helper 方法的基本架構:
/// <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
若要定義 ParseArguments 方法的主體
將下列程式碼加入至 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
要注意的型別和方法包括:ErrorManager 和 DataSchemaError。
若要定義 SummarizeModelInfo 方法的主體
將下列程式碼加入至 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)
關鍵的型別和成員包括:DataSchemaModel、ModelStore、DatabaseSchemaProvider 和 ModelCollation。
若要加入 Summarize 方法的主體
將下列程式碼加入至 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)
同樣地,程式碼註解會提供相關資訊。
若要加入 SurveyRelationships 方法的主體
將下列程式碼加入至 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)
程式碼註解會說明這個方法的處理重點。 在所參考的型別和方法中,需要注意的包括:DataSchemaModel、ModelStore、GetElements 和 ModelElement。
若要加入 OutputResult 方法的主體
在 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)
將這些變更儲存至 Class1.cs。
接下來,您會建置類別庫。
若要簽署和建置組件
按一下 [專案] 功能表上的 [MyBuildContributor 屬性]。
按一下 [簽署] 索引標籤。
按一下 [簽署組件]。
在 [選擇強式名稱金鑰檔] 中,按一下 [<新增>]。
在 [建立強式名稱金鑰] 對話方塊的 [金鑰檔名稱] 中,輸入 MyRefKey。
(選擇性) 您可以為強式名稱金鑰檔指定密碼。
按一下 [確定]。
在 [檔案] 功能表上按一下 [全部儲存]。
在 [建置] 功能表上,按一下 [建置方案]。
接下來,您必須安裝並註冊組件,以便在建置資料庫專案時可以載入它。
安裝建置參與者
若要安裝建置參與者,您必須執行下列工作:
將組件和相關聯的 .pdb 檔案複製到 Extensions 資料夾
建立 Extensions.xml 檔來註冊建置參與者,以在您建置資料庫專案時載入該建置參與者。
若要安裝 MyBuildContributor 組件
在 %Program Files%\Microsoft Visual Studio 10.0\VSTSDB\Extensions 資料夾中建立名為 MyExtensions 的資料夾。
將已簽署的組件 (MyBuildContributor.dll) 複製到 %Program Files%\Microsoft Visual Studio 10.0\VSTSDB\Extensions\MyExtensions 資料夾。
注意事項 建議您不要直接將 XML 檔案複製到 %Program Files%\Microsoft Visual Studio 10.0\VSTSDB\Extensions 資料夾中。 如果改用子資料夾,可以防止不小心變更 Visual Studio Premium 隨附的其他檔案。
接下來,您必須註冊組件 (一種「擴充功能」(Feature Extension) 類型),以便讓它出現在 Visual Studio Premium 中。
若要註冊 MyBuildContributor 組件
按一下 [檢視] 功能表上的 [其他視窗],然後按一下 [命令視窗] 開啟 [命令視窗]。
在 [命令] 視窗中輸入下列程式碼。 將 FilePath 替代為已編譯之 .dll 檔案的路徑和檔案名稱。 請在路徑和檔案名稱周圍加上引號。
注意事項 根據預設,已編譯之 .dll 檔案的路徑為 <您的方案路徑>\bin\Debug 或 <您的方案路徑>\bin\Release。
? System.Reflection.Assembly.LoadFrom("FilePath").FullName
? System.Reflection.Assembly.LoadFrom(@"FilePath").FullName
按 Enter。
將產生的程式碼行複製到剪貼簿中。 此程式碼行應該與下列程式碼相似:
"MyBuildContributor, Version=1.0.0.0, Culture=neutral, PublicKeyToken=nnnnnnnnnnnnnnnn"
開啟純文字編輯器,如 [記事本]。
重要事項 在 Windows Vista 和 Microsoft Windows Server 2008 上,以系統管理員身分開啟編輯器,以便將檔案儲存至 Program Files 資料夾。
提供下列資訊。 您可以貼上在步驟 4 中複製的資訊。 指定您自己的組件名稱、公開金鑰語彙基元和副檔名類型:
<?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>
您可使用此 XML 檔案註冊繼承自 BuildContributor 的類別。
將此檔案另存為 %Program Files%\Microsoft Visual Studio 10.0\VSTSDB\Extensions\MyExtensions 資料夾中的 MyBuildContributor.extensions.xml。
注意事項 在 [存檔類型] 中確認您已指定 [所有檔案],否則 [記事本] 會忽略副檔名,而以 .txt 副檔名儲存檔案。
關閉 Visual Studio。
接下來,您會建置資料庫專案以測試參與者。
測試建置參與者
若要測試建置參與者,您必須執行下列工作:
將屬性加入至您打算建置的 .dbproj 檔案
使用 MSBuild 並提供適當參數來建置資料庫專案
將屬性加入至資料庫專案檔 (.dbproj)
因為這個建置參與者會接受來自 MSBuild 的命令列參數,所以您必須修改資料庫專案,讓使用者能夠透過 MSBuild 傳遞這些參數。 您可以使用下列其中一種作法: 您可以手動修改 .dbproj 檔案以加入必要引數。 如果您只使用 MSBuild 來建置資料庫專案,即可以選擇這樣做。 如果您選擇此選項,請在 .dbproj 檔案中最後一個 </ItemGroup> 節點與最後的 </Project> 節點之間加入下列陳述式:
<ItemGroup>
<BuildContributorArgument Include="OutDir=$(OutDir)" />
<BuildContributorArgument Include="GenerateModelStatistics=$(GenerateModelStatistics)" />
<BuildContributorArgument Include="SortModelStatisticsBy=$(SortModelStatisticsBy)" />
</ItemGroup>
較簡單的方法是將資料庫專案載入至 Visual Studio 中、建置一次資料庫專案,然後結束 Visual Studio。結束時儲存專案的變更。 如果您使用這個方法,資料庫專案檔 (.dbproj) 中會自動加入其他引數。
在執行上述其中一種方法之後,您就可以使用 MSBuild 傳入命令列建置參數。
若要將屬性加入至資料庫專案檔
在 Visual Studio 中開啟資料庫專案。 如需詳細資訊,請參閱 HOW TO:開啟資料庫或伺服器專案。
建置資料庫專案。 如需詳細資訊,請參閱 HOW TO:建置資料庫專案來產生已編譯的結構描述 (.dbschema) 檔案。
關閉 Visual Studio。如果您收到儲存方案和專案的提示,請進行儲存。
接下來,您可以經由使用 MSBuild 並且指定引數讓建置參與者產生模型統計資料,來建置資料庫專案。
建置資料庫專案
若要使用 MSBuild 重新建置資料庫專案以及產生統計資料
開啟 Visual Studio 命令提示字元。 依序按一下 [開始] 功能表、[所有程式]、[Microsoft Visual Studio 2010]、[Visual Studio Tools],然後按一下 [Visual Studio 命令提示字元 (2010)]。
在命令提示字元中,巡覽至包含資料庫專案的資料夾。
在命令提示字元中,輸入下列命令列:
MSBuild /t:Rebuild MyDatabaseProject.dbproj /p:GenerateModelStatistics=true /p:SortModelStatisticsBy=name /p:OutDir=.\
以您要建置的資料庫專案名稱取代 MyDatabaseProject。 如果專案自您上次加以建置之後有所變更,您可以使用 /t:Build 而不是 /t:Rebuild。
會出現類似以下的輸出:
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
開啟 ModelStatistics.xml 並檢查內容。
當初報告的結果也已保存至 XML 檔案中。
後續步驟
您可以建立其他工具來對輸出的 XML 檔案進行處理。 這只是一個建置參與者的範例。 例如,您可以建立建置參與者,以在建置過程中輸出資料字典檔。