다음을 통해 공유


절차: 데이터베이스 프로젝트의 빌드를 모델 통계 생성을 위해 확장하기

데이터베이스 프로젝트를 빌드할 때 사용자 지정 작업을 수행하는 빌드 참가자를 만들 수 있습니다. 이 연습에서는 데이터베이스 프로젝트를 빌드할 때 SQL 데이터베이스 모델에서 통계를 출력하는 ModelStatistics라는 빌드 기여자를 만듭니다. 이 빌드 기여자는 빌드할 때 매개 변수를 사용하므로 몇 가지 추가 단계가 필요합니다.

이 연습에서는 다음과 같은 주요 작업을 수행합니다.

필수 구성 요소

이 연습을 완료하려면 다음 구성 요소가 필요합니다.

  • SSDT(SQL Server Data Tools)를 포함하고 C# 또는 VB(Visual Basic) 개발을 지원하는 Visual Studio 버전을 설치해야 합니다.

  • SQL 개체를 포함하는 SQL 프로젝트가 있어야 합니다.

메모

이 연습은 SSDT의 SQL 기능에 이미 익숙한 사용자를 위한 것입니다. 또한 클래스 라이브러리를 만드는 방법 및 코드 편집기를 사용하여 클래스에 코드를 추가하는 방법과 같은 기본 Visual Studio 개념을 잘 알고 있어야 합니다.

빌드 기여자 배경

빌드 참가자는 프로젝트를 나타내는 모델이 생성된 후 프로젝트를 디스크에 저장하기 전에 프로젝트 빌드 중에 실행됩니다. 다음과 같은 여러 시나리오에 사용할 수 있습니다.

  • 모델 내용의 유효성을 검사하고 유효성 검사 오류를 호출자에게 보고합니다. OnExecute 메서드에 매개 변수로 전달된 목록에 오류를 추가하여 이 작업을 수행할 수 있습니다.

  • 모델 통계 생성 및 사용자에게 보고 다음은 여기에 표시된 예제입니다.

빌드 참가자의 주요 진입점은 OnExecute 메서드입니다. BuildContributor에서 상속되는 모든 클래스는 이 메서드를 구현해야 합니다. BuildContributorContext 개체는 이 메서드에 전달됩니다. 여기에는 빌드 참가자가 사용할 데이터베이스 모델, 빌드 속성 및 인수/파일과 같은 빌드에 대한 모든 관련 데이터가 포함됩니다.

TSqlModel 및 데이터베이스 모델 API

가장 유용한 개체는 TSqlModel 개체로 표현되는 데이터베이스 모델입니다. 모든 테이블, 뷰 및 기타 요소와 해당 요소 간의 관계를 포함하여 데이터베이스의 논리적 표현입니다. 특정 유형의 요소를 쿼리하고 흥미로운 관계를 트래버스하는 데 사용할 수 있는 강력한 형식의 스키마가 있습니다. 연습 코드에서 이 방법을 사용하는 방법에 대한 예제가 표시됩니다.

다음은 이 연습에서 예제 기여자가 사용하는 명령 중 일부입니다.

클래스 메서드 또는 속성 Description
TSqlModel GetObjects() 모델에서 개체를 쿼리하고 모델 API의 주요 진입점입니다. 테이블 또는 뷰와 같은 최상위 형식만 쿼리할 수 있습니다. 열과 같은 형식은 모델을 트래버스해야만 찾을 수 있습니다. ModelTypeClass 필터를 지정하지 않으면 모든 최상위 형식이 반환됩니다.
TSqlObject GetReferencedRelationshipInstances() 현재 TSqlObject에서 참조하는 요소에 대한 관계를 찾습니다. 예를 들어 테이블의 경우 테이블의 열과 같은 개체를 반환합니다. 이 경우 ModelRelationshipClass 필터를 사용하여 쿼리할 정확한 관계를 지정할 수 있습니다(예: Table.Columns 필터를 사용하면 열만 반환됨).

GetReferencingRelationshipInstances, GetChildren 및 GetParent와 같은 몇 가지 유사한 메서드가 있습니다. 자세한 내용은 API 설명서를 참조하세요.

기여자 고유 식별

빌드 프로세스 중에 사용자 지정 기여자는 표준 확장 디렉터리에서 로드됩니다. 빌드 참가자는 ExportBuildContributor 특성으로 식별됩니다. 기여자를 검색할 수 있도록 이 특성이 필요합니다. 이 특성은 다음 코드와 유사해야 합니다.

[ExportBuildContributor("ExampleContributors.ModelStatistics", "1.0.0.0")]

이 경우 특성에 대한 첫 번째 매개 변수는 고유 식별자여야 합니다. 이 매개 변수는 프로젝트 파일에서 기여자를 식별하는 데 사용됩니다. 라이브러리의 네임스페이스(이 연습에서는 "ExampleContributors")를 클래스 이름(이 연습에서는 "ModelStatistics")과 결합하여 식별자를 생성하는 것이 가장 좋습니다. 이 네임스페이스를 사용하여 기여자가 나중에 실행되도록 지정하는 방법을 확인할 수 있습니다.

빌드 기여자 만들기

빌드 참가자를 만들려면 다음 작업을 수행해야 합니다.

  • 클래스 라이브러리 프로젝트를 만들고 필요한 참조를 추가합니다.

  • BuildContributor상속하는 ModelStatistics라는 클래스를 정의합니다.

  • OnExecute 메서드를 재정의합니다.

  • 몇 가지 프라이빗 도우미 메서드를 추가합니다.

  • 결과 어셈블리를 빌드합니다.

클래스 라이브러리 프로젝트 만들기

  1. MyBuildContributor라는 Visual Basic 또는 C# 클래스 라이브러리 프로젝트를 만듭니다.

  2. 파일 이름을 "Class1.cs"에서 "ModelStatistics.cs"로 바꿉니다.

  3. 솔루션 탐색기에서 프로젝트 노드를 마우스 오른쪽 단추로 클릭한 다음 참조 추가를 선택합니다.

  4. System.ComponentModel.Composition 항목을 선택한 다음 확인을 선택합니다.

  5. 필요한 SQL 참조 추가: 프로젝트 노드를 마우스 오른쪽 단추로 클릭한 다음 참조 추가를 선택합니다. 찾아보기 단추를 선택합니다. C:\Program Files (x86)\Microsoft SQL Server\110\DAC\Bin 폴더로 이동합니다. Microsoft.SqlServer.Dac.dll, Microsoft.SqlServer.Dac.Extensions.dllMicrosoft.Data.Tools.Schema.Sql.dll항목을 선택한 다음 확인을 선택합니다.

    다음으로, 클래스에 코드를 추가하기 시작합니다.

ModelStatistics 클래스 정의

  1. ModelStatistics 클래스는 OnExecute 메서드에 전달된 데이터베이스 모델을 처리하고 모델의 내용을 자세히 설명하는 XML 보고서를 생성합니다.

    코드 편집기에서 다음 코드와 일치하도록 ModelStatistics.cs 파일을 업데이트합니다.

    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;
    
    namespace ExampleContributors
    {
    /// <summary>
        /// A BuildContributor that generates statistics about a model and saves this to the output directory.
        /// Only runs if a "GenerateModelStatistics=true" contributor argument is set in the project file, or a targets file.
        /// Statistics can be sorted by "none, "name" or "value", with "none" being the default sort behavior.
        ///
        /// To set contributor arguments in a project file, add:
        ///
        /// <PropertyGroup>
        ///     <ContributorArguments Condition="'$(Configuration)' == 'Debug'">
        /// $(ContributorArguments);ModelStatistics.GenerateModelStatistics=true;ModelStatistics.SortModelStatisticsBy="name";
        ///     </ContributorArguments>
        /// <PropertyGroup>
        ///
        /// This generates model statistics when building in Debug mode only - remove the condition to generate in all build modes.
        /// </summary>
        [ExportBuildContributor("ExampleContributors.ModelStatistics", "1.0.0.0")]
        public class ModelStatistics : BuildContributor
        {
            public const string GenerateModelStatistics = "ModelStatistics.GenerateModelStatistics";
            public const string SortModelStatisticsBy = "ModelStatistics.SortModelStatisticsBy";
            public const string OutDir = "ModelStatistics.OutDir";
            public const string ModelStatisticsFilename = "ModelStatistics.xml";
            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;
    
            /// <summary>
            /// Override the OnExecute method to perform actions when you build a database project.
            /// </summary>
            protected override void OnExecute(BuildContributorContext context, IList<ExtensibilityError> errors)
            {
                // handle related arguments, passed in as part of
                // the context information.
                bool generateModelStatistics;
                ParseArguments(context.Arguments, errors, 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.
                    StringBuilder statisticsMsg = new StringBuilder();
                    statisticsMsg.AppendLine(" ")
                                 .AppendLine("Model Statistics:")
                                 .AppendLine("===")
                                 .AppendLine(" ");
                    errors.Add(new ExtensibilityError(statisticsMsg.ToString(), Severity.Message));
    
                    var model = context.Model;
    
                    // Start building up the XML that is serialized later
                    var xRoot = new XElement("ModelStatistics");
    
                    SummarizeModelInfo(model, xRoot, errors);
    
                    // First, count the elements that are contained
                    // in this model.
                    IList<TSqlObject> elements = model.GetObjects(DacQueryScopes.UserDefined).ToList();
                    Summarize(elements, element => element.ObjectType.Name, "UserDefinedElements", xRoot, errors);
    
                    // 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.GetObjects(DacQueryScopes.BuiltIn | DacQueryScopes.SameDatabase | DacQueryScopes.System).ToList();
                    Summarize(elements, element => element.ObjectType.Name, "OtherElements", xRoot, errors);
    
                    // Now, count the number of each type
                    // of relationship in the model.
                    SurveyRelationships(model, xRoot, errors);
    
                    // Determine where the user wants to save
                    // the serialized XML file.
                    string outDir;
                    if (context.Arguments.TryGetValue(OutDir, out outDir) == false)
                    {
                        outDir = ".";
                    }
                    string filePath = Path.Combine(outDir, ModelStatisticsFilename);
                    // Save the XML file and tell the user
                    // where it was saved.
                    xRoot.Save(filePath);
                    ExtensibilityError resultArg = new ExtensibilityError("Result was saved to " + filePath, Severity.Message);
                    errors.Add(resultArg);
                }
            }
    
            /// <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, IList<ExtensibilityError> errors, out bool generateModelStatistics)
            {
                // 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
                        ExtensibilityError invalidArg = new ExtensibilityError(
                            GenerateModelStatistics + "=" + valueString + " was not valid.  It can be true or false", Severity.Error);
                        errors.Add(invalidArg);
                        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
                            ExtensibilityError invalidArg = new ExtensibilityError(
                                SortModelStatisticsBy + "=" + valueString + " was not valid.  It can be none, name, or value", Severity.Error);
                            errors.Add(invalidArg);
                        }
                    }
                }
            }
    
            /// <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(TSqlModel model, XElement xContainer, IList<ExtensibilityError> errors)
            {
                // use a Dictionary to accumulate the information
                // that is later 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("Version", model.Version.ToString());
    
                TSqlObject options = model.GetObjects(DacQueryScopes.UserDefined, DatabaseOptions.TypeClass).FirstOrDefault();
                if (options != null)
                {
                    info.Add("Collation", options.GetProperty<string>(DatabaseOptions.Collation));
                }
    
                // Output the accumulated information and add it to
                // the XML.
                OutputResult("Basic model info", info, xContainer, errors);
            }
    
            /// <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, IList<ExtensibilityError> errors)
            { // 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 is 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, errors);
            }
    
            /// <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(TSqlModel model, XElement xContainer, IList<ExtensibilityError> errors)
            {
                // get a list that contains all elements in the model
                var elements = model.GetObjects(DacQueryScopes.All);
                // We are interested in all relationships that
                // reference each element.
                var entries =
                    from element in elements
                    from entry in element.GetReferencedRelationshipInstances(DacExternalQueryScopes.All)
                    select entry;
    
                // initialize our counting buckets
                var composing = 0;
                var hierachical = 0;
                var peer = 0;
    
                // process each relationship, adding to the
                // appropriate bucket for style and type.
                foreach (var entry in entries)
                {
                    switch (entry.Relationship.Type)
                    {
                        case RelationshipType.Composing:
                            ++composing;
                            break;
                        case RelationshipType.Hierarchical:
                            ++hierachical;
                            break;
                        case RelationshipType.Peer:
                            ++peer;
                            break;
                        default:
                            break;
                    }
                }
    
                // build a dictionary of data to pass along
                // to the OutputResult method.
                var stat = new Dictionary<string, int>
                {
                    {"Composing", composing},
                    {"Hierarchical", hierachical},
                    {"Peer", peer},
                    {"subtotal", entries.Count()}
                };
    
                OutputResult("Relationships", stat, xContainer, errors);
            }
    
            /// <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, IList<ExtensibilityError> errors)
            {
                var maxLen = statistics.Max(stat => stat.Key.Length) + 2;
                var format = string.Format("{{0, {0}}}: {{1}}", maxLen);
    
                StringBuilder resultMessage = new StringBuilder();
                //List<ExtensibilityError> args = new List<ExtensibilityError>();
                resultMessage.AppendLine(category);
                resultMessage.AppendLine("-----------------");
    
                // 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);
                    resultMessage.AppendLine(entry);
                    // Replace any blank spaces in the element key with
                    // underscores.
                    xCategory.Add(new XElement(item.Key.Replace(' ', '_'), item.Value));
                }
                resultMessage.AppendLine(" ");
                errors.Add(new ExtensibilityError(resultMessage.ToString(), Severity.Message));
            }
        }
    }
    

    다음으로 클래스 라이브러리를 빌드합니다.

어셈블리 서명 및 빌드

  1. 프로젝트 메뉴에서 MyBuildContributor 속성을 선택합니다.

  2. 서명 탭을 선택합니다.

  3. 어셈블리 서명을 선택합니다.

  4. 강력한 이름 키 파일 선택에서 새로< 만들기를 선택합니다>.

  5. 강력한 이름 키 만들기 대화 상자에서 키 파일 이름MyRefKey를 입력합니다.

  6. (선택 사항) 강력한 이름 키 파일의 암호를 지정할 수 있습니다.

  7. 확인을 선택합니다.

  8. 파일 메뉴에서 모두 저장을 선택합니다.

  9. 빌드 메뉴에서 솔루션 빌드를 선택합니다.

    다음으로, SQL 프로젝트를 빌드할 때 로드되도록 어셈블리를 설치해야 합니다.

빌드 기여자 설치

빌드 참가자를 설치하려면 어셈블리 및 연결된 .pdb 파일을 Extensions 폴더에 복사해야 합니다.

MyBuildContributor 어셈블리 설치

  1. 다음으로, 어셈블리 정보를 Extensions 디렉터리에 복사합니다. Visual Studio가 시작되면 %ProgramFiles%\Microsoft SQL Server\110\DAC\Bin\Extensions 디렉터리 및 하위 디렉터리의 모든 확장을 식별하여 사용할 수 있도록 준비합니다.

  2. 출력 디렉터리에서 MyBuildContributor.dll 어셈블리 파일을 %ProgramFiles%\Microsoft SQL Server\110\DAC\Bin\Extensions 디렉터리로 복사합니다.

    메모

    기본적으로 컴파일된 .dll 파일의 경로는 YourSolutionPath\YourProjectPath\bin\Debug 또는 YourSolutionPath\YourProjectPath\bin\Release입니다.

빌드 기여자 테스트 또는 실행

빌드 참가자를 실행하거나 테스트하려면 다음 작업을 수행해야 합니다.

  • 빌드하려는 .sqlproj 파일에 속성을 추가합니다.

  • MSBuild를 사용하여 데이터베이스 프로젝트를 빌드하고 적절한 매개 변수를 제공합니다.

SQL 프로젝트(.sqlproj) 파일에 속성 추가

실행하려는 참가자의 ID를 지정하려면 항상 SQL 프로젝트 파일을 업데이트해야 합니다. 또한 이 빌드 기여자는 MSBuild의 명령줄 매개 변수를 허용하므로 사용자가 MSBuild를 통해 해당 매개 변수를 전달할 수 있도록 SQL 프로젝트를 수정해야 합니다.

다음 두 가지 방법 중 하나로 이 작업을 수행할 수 있습니다.

  • 필요한 인수를 .sqlproj 추가하도록 파일을 수동으로 수정할 수 있습니다. 많은 수의 프로젝트에서 빌드 기여자를 다시 사용하지 않으려는 경우 이 작업을 수행할 수 있습니다. 이 옵션을 선택하는 경우 파일의 첫 번째 가져오기 노드 다음에 다음 문을 파일에 추가합니다 .sqlproj .

    <PropertyGroup>
        <BuildContributors>
            $(BuildContributors);ExampleContributors.ModelStatistics
        </BuildContributors>
        <ContributorArguments Condition="'$(Configuration)' == 'Debug'">
            $(ContributorArguments);ModelStatistics.GenerateModelStatistics=true;ModelStatistics.SortModelStatisticsBy=name;
        </ContributorArguments>
    </PropertyGroup>
    
  • 두 번째 방법은 필요한 기여자 인수를 포함하는 대상 파일을 만드는 것입니다. 이 기능은 기본값을 포함하므로 여러 프로젝트에 동일한 기여자를 사용하는 경우에 유용합니다.

    이 경우 MSBuild 확장 경로에 대상 파일을 만듭니다.

    1. %ProgramFiles%\MSBuild으로 이동합니다.

    2. 대상 파일이 저장되는 새 폴더 "MyContributors"를 만듭니다.

    3. 이 디렉터리 내에 새 파일 "MyContributors.targets"를 만들고, 다음 텍스트를 추가한 다음, 파일을 저장합니다.

      <?xml version="1.0" encoding="utf-8"?>
      
      <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
        <PropertyGroup>
          <BuildContributors>$(BuildContributors);ExampleContributors.ModelStatistics</BuildContributors>
          <ContributorArguments Condition="'$(Configuration)' == 'Debug'">$(ContributorArguments);ModelStatistics.GenerateModelStatistics=true;ModelStatistics.SortModelStatisticsBy=name;</ContributorArguments>
        </PropertyGroup>
      </Project>
      
    4. .sqlproj 참가자를 실행하려는 프로젝트의 파일 내에서 파일의 Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\SSDT\Microsoft.Data.Tools.Schema.SqlTasks.targets" /.sqlproj 노드 다음에 <다음 문을 > 추가하여 대상 파일을 가져옵니다.

      <Import Project="$(MSBuildExtensionsPath)\MyContributors\MyContributors.targets " />
      

이러한 방법 중 하나를 수행한 후에는 MSBuild를 사용하여 명령줄 빌드에 대한 매개 변수를 전달할 수 있습니다.

메모

참가자 ID를 지정하려면 항상 "BuildContributors" 속성을 업데이트해야 합니다. 이는 기여자 원본 파일의 "ExportBuildContributor" 특성에 사용되는 ID와 동일합니다. 이 설정이 없으면 프로젝트를 빌드할 때 기여 작업이 실행되지 않습니다. 참가자를 실행하는 데 필요한 인수가 있는 경우에만 "ContributorArguments" 속성을 업데이트해야 합니다.

SQL 프로젝트 빌드

MSBuild를 사용하여 데이터베이스 프로젝트 다시 빌드 및 통계 생성

  1. Visual Studio에서 프로젝트를 마우스 오른쪽 단추로 클릭하고 다시 빌드를 선택합니다. 프로젝트를 다시 빌드하면 모델 통계가 생성되고 빌드 출력에 포함된 출력과 함께 ModelStatistics.xml에 저장됩니다. XML 파일을 보려면 솔루션 탐색기의 모든 파일 표시 를 선택해야 할 수 있습니다.

  2. Visual Studio 명령 프롬프트 열기: 시작 메뉴에서 모든 프로그램을 선택하고 , Microsoft Visual Studio <Visual Studio 버전을> 선택하고, Visual Studio 도구를 선택한 다음, Visual Studio 명령 프롬프트(<Visual Studio 버전>)를 선택합니다.

  3. 명령 프롬프트에서 SQL 프로젝트가 포함된 폴더로 이동합니다.

  4. 명령 프롬프트에서 다음 명령을 입력합니다.

    MSBuild /t:Rebuild MyDatabaseProject.sqlproj /p:BuildContributors=$(BuildContributors);ExampleContributors.ModelStatistics /p:ContributorArguments=$(ContributorArguments);GenerateModelStatistics=true;SortModelStatisticsBy=name;OutDir=.\;
    

    MyDatabaseProject을 빌드하려는 데이터베이스 프로젝트의 이름으로 바꾸십시오. 프로젝트를 마지막으로 빌드한 후 변경한 경우 대신 사용할 /t:Build/t:Rebuild수 있습니다.

    출력 내에 다음 예제와 같은 빌드 정보가 표시됩니다.

    Model Statistics:
    ===
    
    Basic model info
    -----------------
        Version: Sql110
      Collation: SQL_Latin1_General_CP1_CI_AS
    
    UserDefinedElements
    -----------------
      DatabaseOptions: 1
             subtotal: 1
          total items: 1
    
    OtherElements
    -----------------
                    Assembly: 1
           BuiltInServerRole: 9
               ClrTypeMethod: 218
      ClrTypeMethodParameter: 197
             ClrTypeProperty: 20
                    Contract: 6
                    DataType: 34
                    Endpoint: 5
                   Filegroup: 1
                 MessageType: 14
                       Queue: 3
                        Role: 10
                      Schema: 13
                     Service: 3
                        User: 4
             UserDefinedType: 3
                    subtotal: 541
                 total items: 16
    
    Relationships
    -----------------
         Composing: 477
      Hierarchical: 6
              Peer: 19
          subtotal: 502
    
  5. ModelStatistics.xml 열고 내용을 검사합니다.

    보고된 결과도 XML 파일에 유지됩니다.