방법: UML 모델에 대한 유효성 검사 제약 조건 정의
Visual Studio Ultimate에서는 모델이 지정된 조건을 충족하는지 여부를 테스트하는 유효성 검사 제약 조건을 정의할 수 있습니다.예를 들어 사용자가 상속 관계 루프를 만들지 않도록 제약 조건을 정의할 수 있습니다.제약 조건은 사용자가 모델을 열거나 저장하려고 할 때 호출되며 수동으로 호출할 수도 있습니다.제약 조건이 실패하면 정의된 오류 메시지가 오류 창에 추가됩니다.이러한 제약 조건을 VSIX(Visual Studio Integration Extension)에 패키지하여 다른 Visual Studio Ultimate 사용자에게 배포할 수 있습니다.
데이터베이스와 같은 외부 리소스를 기준으로 모델의 유효성을 검사하는 제약 조건을 정의할 수도 있습니다.
[!참고]
레이어 다이어그램을 기준으로 프로그램 코드의 유효성을 검사하려면 레이어 다이어그램에 사용자 지정 아키텍처 유효성 검사 추가를 참조하십시오.
요구 사항
얻을 수 있는 Visual Studio SDK Visual Studio 갤러리.
시각화 및에서 구할 수 모델링 SDK Visual Studio Visual Studio 시각화 및 모델링 SDK 코드 갤러리에서.
유효성 검사 제약 조건 적용
유효성 검사 제약 조건은 모델을 저장하거나, 모델을 열거나, 아키텍처 메뉴에서 UML 모델 유효성 검사를 클릭할 경우에 적용됩니다.일반적으로 각 제약 조건은 둘 이상의 경우에 적용되도록 정의하게 되지만 각 경우에는 해당 경우에 대해 정의된 제약 조건만 적용됩니다.
유효성 검사 오류는 Visual Studio 오류 창에 표시되며, 오류를 두 번 클릭하면 해당 오류에 관련된 모델 요소가 선택됩니다.
유효성 검사를 적용하는 방법에 대한 자세한 내용은 UML 모델 유효성 검사를 참조하십시오.
유효성 검사 확장 정의
UML 디자이너를 위한 유효성 검사 확장을 만들려면 유효성 검사 제약 조건을 정의하는 클래스를 만들어서 VSIX(Visual Studio Integration Extension)에 포함합니다.VSIX는 제약 조건을 설치할 수 있는 컨테이너 역할을 합니다.다음과 같은 두 가지 방법으로 유효성 검사 확장을 정의할 수도 있습니다.
프로젝트 템플릿을 사용하여 자체 VSIX에 유효성 검사 확장 만들기. 이는 보다 빠른 방법입니다.유효성 검사 제약 조건을 메뉴 명령, 사용자 지정 도구 상자 항목, 제스처 처리기 등의 다른 유형의 확장과 결합하지 않으려는 경우 이 방법을 사용합니다.한 클래스에 여러 제약 조건을 정의할 수 있습니다.
별도의 유효성 검사 클래스와 VSIX 프로젝트 만들기. 같은 VSIX에 여러 유형의 확장을 결합하려는 경우 이 방법을 사용합니다.예를 들어 메뉴 명령에서 모델이 특정 제약 조건을 준수할 것으로 예상하는 경우 이를 동일한 VSIX에 유효성 검사 메서드로 포함할 수 있습니다.
자체 VSIX에 유효성 검사 확장을 만들려면
새 프로젝트 대화 상자의 모델링 프로젝트에서 유효성 검사 확장을 선택합니다.
새 프로젝트에서 .cs 파일을 열고 클래스를 수정하여 유효성 검사 제약 조건을 구현합니다.
자세한 내용은 유효성 검사 제약 조건 구현을 참조하십시오.
중요 .cs 파일에 다음 using 문이 포함되어 있는지 확인합니다.
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Uml;
새 메서드를 정의하여 제약 조건을 더 추가할 수 있습니다.메서드를 유효성 검사 메서드로 식별하려면 초기 유효성 검사 메서드와 같은 방식으로 메서드에 속성으로 태그가 지정되어야 합니다.
F5 키를 눌러 제약 조건을 테스트합니다.자세한 내용은 유효성 검사 실행을 참조하십시오.
프로젝트에서 빌드된 bin\*\*.vsix 파일을 복사하여 메뉴 명령을 다른 컴퓨터에 설치합니다.자세한 내용은 유효성 검사 제약 조건 설치를 참조하십시오.
다른 .cs 파일을 추가하는 경우 일반적으로 다음 using 문이 필요합니다.
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Uml;
using Microsoft.VisualStudio.Modeling.Validation;
using Microsoft.VisualStudio.Uml.Classes;
다른 절차는 다음과 같습니다.
클래스 라이브러리 프로젝트에 별도의 유효성 검사 제약 조건을 만들려면
기존 VSIX 솔루션에 추가하거나 새 솔루션을 만들어 클래스 라이브러리 프로젝트를 만듭니다.
파일 메뉴에서 새로 만들기, 프로젝트를 선택합니다.
템플릿 설치, 확장 C# 또는 Visual Basic를 클릭 한 다음 가운데 열에서 선택한 클래스 라이브러리.
솔루션에 이미 하나 있는 경우가 아니면 VSIX 프로젝트를 만듭니다.
솔루션 탐색기, 솔루션의 바로 가기 메뉴에서 선택 추가, 새 프로젝트.
아래 설치 되어 있는 템플릿, 확장 C# 또는 Visual Basic, 다음 선택 확장성.가운데 열에서 VSIX 프로젝트를 클릭합니다.
VSIX 프로젝트를 솔루션의 시작 프로젝트로 설정합니다.
- 솔루션 탐색기에서 VSIX 프로젝트의 바로 가기 메뉴에서 선택 시작 프로젝트로 설정.
source.extension.vsixmanifest, 콘텐츠, MEF 구성 요소로 클래스 라이브러리 프로젝트를 추가 합니다.
에 메타 데이터 탭에서 VSIX의 이름을 설정 합니다.
에 설치 대상 탭에서 최고의 Visual Studio 및 프리미엄 대상으로 설정 합니다.
에 자산 탭에서 선택은 새, 및 설정 대화 상자에서:
형식 = MEF 구성 요소
소스 = 현재 솔루션의 프로젝트에
프로젝트 = 클래스 라이브러리 프로젝트
유효성 검사 클래스를 정의하려면
유효성 검사 프로젝트 템플릿으로 자체 VSIX가 있는 유효성 검사 클래스를 만든 경우 이 절차가 필요 없습니다.
유효성 검사 클래스 프로젝트에서 다음 .NET 어셈블리에 대한 참조를 추가합니다.
Microsoft.VisualStudio.Modeling.Sdk.11.0
Microsoft.VisualStudio.ArchitectureTools.Extensibility.Uml
Microsoft.VisualStudio.Uml.Interfaces
System.ComponentModel.Composition
다음 예와 비슷한 코드가 포함된 파일을 클래스 라이브러리 프로젝트에 추가합니다.
각 유효성 검사 제약 조건은 특정 특성으로 표시되는 메서드 내에 포함됩니다.이 메서드는 모델 요소 형식의 매개 변수를 수락합니다.유효성 검사가 호출되면 유효성 검사 프레임워크에서 모든 유효성 검사 메서드를 각 매개 변수 형식을 따르는 모든 모델 요소에 적용합니다.
이러한 메서드를 모든 클래스와 네임스페이스에 배치할 수 있습니다.메서드를 원하는 대로 변경합니다.
using System.Collections.Generic; using System.ComponentModel.Composition; using System.Linq; using Microsoft.VisualStudio.Modeling.Validation; using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Uml; using Microsoft.VisualStudio.Uml.Classes; // You might also need the other Microsoft.VisualStudio.Uml namespaces. namespace Validation { public class MyValidationExtensions { // SAMPLE VALIDATION METHOD. // All validation methods have the following attributes. [Export(typeof(System.Action<ValidationContext, object>))] [ValidationMethod( ValidationCategories.Save | ValidationCategories.Open | ValidationCategories.Menu)] public void ValidateClassNames (ValidationContext context, // This type determines what elements // will be validated by this method: IClass elementToValidate) { // A validation method should not change the model. List<string> attributeNames = new List<string>(); foreach (IProperty attribute in elementToValidate.OwnedAttributes) { string name = attribute.Name; if (!string.IsNullOrEmpty(name) && attributeNames.Contains(name)) { context.LogError( string.Format("Duplicate attribute name '{0}' in class {1}", name, elementToValidate.Name), "001", elementToValidate); } attributeNames.Add(name); } } // Add more validation methods for different element types. } }
유효성 검사 제약 조건 실행
테스트 목적으로 디버그 모드에서 유효성 검사 메서드를 실행합니다.
유효성 검사 제약 조건을 테스트하려면
키를 눌러 F5에 디버깅 메뉴를 선택 디버깅 시작.
실험적 Visual Studio 인스턴스가 시작됩니다.
문제 해결: 새 Visual Studio가 시작되지 않는 경우:
하나 이상의 프로젝트가 있는 경우 VSIX 프로젝트가 솔루션의 시작 프로젝트로 설정되어 있는지 확인하십시오.
솔루션 탐색기에서 시작 프로젝트만의 바로 가기 메뉴에서 선택 속성.프로젝트 속성 편집기에서 선택 된 디버깅 탭.시작 외부 프로그램 필드의 문자열이 대개 다음과 같은 Visual Studio의 전체 경로 이름인지 확인합니다.
C:\Program Files\Microsoft Visual Studio 11.0\Common7\IDE\devenv.exe
실험적 Visual Studio에서 모델링 프로젝트 및 모델링 다이어그램을 열거나 만듭니다.
이전 단원에 제공된 샘플 제약 조건에 대한 테스트를 설정하려면
클래스 다이어그램을 엽니다.
클래스를 만들고, 이름이 같은 두 특성을 추가합니다.
다이어그램의 원하는 위치에 바로 가기 메뉴에서 선택 유효성 검사.
모델에 포함된 모든 오류가 오류 창에 보고됩니다.
오류 보고서를 두 번 클릭합니다.보고서에 언급된 요소가 화면에 보이면 해당 요소가 강조 표시됩니다.
문제 해결: 메뉴에 유효성 검사 명령이 나타나지 않으면 다음을 검토하십시오.
유효성 검사 프로젝트에서 MEF 구성 요소로 나열 되어 있는 자산 탭에서 source.extensions.manifest VSIX 프로젝트에서.
올바른 Export 및 ValidationMethod 특성은 유효성 검사 메서드에 연결됩니다.
ValidationCategories.Menu는 ValidationMethod 특성의 인수에 포함되고 논리적 OR (|)를 사용하여 다른 값으로 구성됩니다.
Import 및 Export 특성의 모든 매개 변수가 올바른지 확인합니다.
제약 조건 평가
유효성 검사 메서드에서는 적용하려는 유효성 검사 제약 조건이 true인지 아니면 false인지를 판단해야 합니다.true이면 아무 것도 수행하지 않고,false이면 ValidationContext 매개 변수에서 제공하는 메서드를 사용하여 오류를 보고해야 합니다.
[!참고]
유효성 검사 메서드는 모델을 변경하면 안 됩니다.제약 조건이 실행되는 시기나 순서는 확정되어 있지 않습니다.유효성 검사 실행 내에서 유효성 검사 메서드가 연속으로 실행되는 사이에 정보를 전달해야 할 경우에는 여러 개의 유효성 검사 조정에 설명된 컨텍스트 캐시를 사용할 수 있습니다.
예를 들어 클래스, 인터페이스 또는 열거자 등의 모든 형식이 세 자 이상 길이의 이름을 갖도록 하려는 경우 다음 메서드를 사용할 수 있습니다.
public void ValidateTypeName(ValidationContext context, IType type)
{
if (!string.IsNullOrEmpty(type.Name) && type.Name.Length < 3)
{
context.LogError(
string.Format("Type name {0} is too short", type.Name),
"001", type);
}
}
모델을 탐색하고 읽는 데 사용할 수 있는 메서드 및 형식에 대한 자세한 내용은 UML API를 사용한 프로그래밍을 참조하십시오.
유효성 검사 제약 조건 메서드 정보
각 유효성 검사 제약 조건은 다음과 같은 형식의 메서드로 정의됩니다.
[Export(typeof(System.Action<ValidationContext, object>))]
[ValidationMethod(ValidationCategories.Save
| ValidationCategories.Menu
| ValidationCategories.Open)]
public void ValidateSomething
(ValidationContext context, IClassifier elementToValidate)
{...}
모든 유효성 검사 메서드의 특성 및 매개 변수는 다음과 같습니다.
[Export(typeof(System.Action <ValidationContext, object>))] |
MEF(Managed Extensibility Framework)를 사용하여 메서드를 유효성 검사 제약 조건으로 정의합니다. |
[ValidationMethod (ValidationCategories.Menu)] |
유효성 검사가 수행되는 시기를 지정합니다.둘 이상의 옵션을 결합하려면 비트 OR(|)를 사용하십시오. Menu = 유효성 검사 메뉴에 의해 호출됩니다. Save = 모델을 저장할 때 호출됩니다. Open = 모델을 열 때 호출됩니다.Load = 모델을 저장할 때 호출되지만, 유효성 검사 위반이 발생할 경우에는 모델을 다시 열 수 없다는 경고가 사용자에게 표시됩니다.모델이 구문 분석되기 전 로드될 때 호출됩니다. |
public void ValidateSomething (ValidationContext context, IElement element) |
두 번째 매개 변수 IElement를 제약 조건을 적용할 요소의 형식으로 바꿉니다.제약 조건 메서드는 지정된 형식의 모든 요소에 대해 호출됩니다. 메서드 이름은 중요하지 않습니다. |
두 번째 매개 변수에 서로 다른 형식을 사용하여 유효성 검사 메서드를 필요한 만큼 정의할 수 있습니다.유효성 검사가 호출되면 이 매개 변수 형식에 맞는 각 모델 요소에 대해 각 유효성 검사 메서드가 호출됩니다.
유효성 검사 오류 보고
오류 보고서를 만들려면 다음과 같이 ValidationContext에서 제공하는 메서드를 사용합니다.
context.LogError("error string", errorCode, elementsWithError);
"error string"은 Visual Studio 오류 목록에 나타납니다.
errorCode는 오류의 고유 식별자를 나타내는 문자열입니다.
elementsWithError는 모델에서 요소를 식별합니다.사용자가 오류 보고서를 두 번 클릭하면 이 요소를 나타내는 모양이 선택됩니다.
LogError(),LogWarning() 및 LogMessage()는 오류 목록의 각 섹션에 메시지를 배치합니다.
유효성 검사 메서드가 적용되는 방식
유효성 검사는 관계나 클래스의 특성 및 작업의 매개 변수와 같은 더 큰 요소의 구성 요소를 포함하여 모델의 모든 요소에 적용됩니다.
각 유효성 검사 메서드는 두 번째 매개 변수에 지정된 형식에 맞는 각 요소에 적용됩니다.예를 들어 두 번째 매개 변수로 IUseCase가 지정된 유효성 검사 메서드와 이 형식의 상위 형식 IElement가 지정된 또 다른 유효성 검사 메서드를 정의한 경우에는 모델의 각 사용 사례에 두 메서드가 모두 적용됩니다.
형식의 계층 구조는 모델 요소 형식에 요약되어 있습니다.
관계를 따라 요소에 액세스할 수도 있습니다.예를 들어 다음과 같이 IClass에 대한 유효성 검사 메서드를 정의한 경우 이 형식이 소유하는 속성을 반복할 수 있습니다.
public void ValidateTypeName(ValidationContext context, IClass c)
{
foreach (IProperty property in c.OwnedAttributes)
{
if (property.Name.Length < 3)
{
context.LogError(
string.Format(
"Property name {0} is too short",
property.Name),
"001", property);
}
}
}
모델에 대한 유효성 검사 메서드 만들기
유효성 검사 메서드가 각 유효성 검사 실행 중 정확히 한 번만 호출되도록 하려는 경우 다음과 같이 IModel의 유효성을 검사할 수 있습니다.
using Microsoft.VisualStudio.Uml.AuxiliaryConstructs; ...
[Export(typeof(System.Action<ValidationContext, object>))]
[ValidationMethod(ValidationCategories.Menu)]
public void ValidateModel(ValidationContext context, IModel model)
{ foreach (IElement element in model.OwnedElements)
{ ...
모양 및 다이어그램의 유효성 검사
유효성 검사 메서드의 주요 목적은 모델의 유효성을 검사하는 것이므로 다이어그램 및 모양과 같은 표시 요소에 대해서는 유효성 검사 메서드가 호출되지 않습니다.하지만 다이어그램 컨텍스트를 사용하면 현재 다이어그램에 액세스할 수 있습니다.
다음과 같이 유효성 검사 클래스에서 DiagramContext를 가져온 속성으로 선언합니다.
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Presentation;
...
[Import]
public IDiagramContext DiagramContext { get; set; }
유효성 검사 메서드에서는 다음과 같이 DiagramContext를 사용하여 현재 포커스 다이어그램(있는 경우)에 액세스할 수 있습니다.
[Export(typeof(System.Action<ValidationContext, object>))]
[ValidationMethod(ValidationCategories.Menu)]
public void ValidateModel(ValidationContext context, IModel model)
{
IDiagram focusDiagram = DiagramContext.CurrentDiagram;
if (focusDiagram != null)
{
foreach (IShape<IUseCase> useCaseShape in
focusDiagram.GetChildShapes<IUseCase>())
{ ...
LogError에는 모양을 전달할 수 없으므로 오류를 로깅하려면 다음과 같이 모양이 나타내는 모델 요소를 가져와야 합니다.
IUseCase useCase = useCaseShape.Element;
context.LogError(... , usecase);
여러 개의 유효성 검사 조정
예를 들어 다이어그램 메뉴에서 사용자에 의해 유효성 검사가 호출되는 경우 각 유효성 검사 메서드가 각 모델 요소에 적용됩니다.즉, 유효성 검사 프레임워크를 한 번 호출한 상태에서 같은 메서드를 서로 다른 요소에 여러 번 적용할 수 있습니다.
이는 요소 간의 관계를 다루는 유효성 검사의 경우 문제가 될 수 있습니다.예를 들어 사용 사례에서 시작하고 include 관계를 트래버스하여 루프가 없는지 확인하는 유효성 검사를 작성할 수 있습니다.그러나 모델에서 include 링크를 많이 포함하는 각 사용 사례에 메서드가 적용되면 모델의 같은 영역을 반복적으로 처리할 가능성이 있습니다.
이 문제를 방지하기 위해 유효성 검사가 실행되는 동안 정보가 유지되는 컨텍스트 캐시를 사용할 수 있습니다.컨텍스트 캐시를 사용하면 유효성 검사 메서드의 각 실행 사이에 정보를 전달할 수 있습니다.예를 들어 이번 유효성 검사를 실행하는 동안 이미 처리한 요소의 목록을 저장할 수 있습니다.캐시는 각 유효성 검사 실행을 시작할 때 만들어지고 서로 다른 유효성 검사 실행 사이에 정보를 전달하는 데 사용할 수 없습니다.
context.SetCacheValue<T> (name, value) |
값을 저장합니다. |
context.TryGetCacheValue<T> (name, out value) |
값을 가져옵니다.성공하면 true를 반환합니다. |
context.GetValue<T>(name) |
값을 가져옵니다. |
Context.GetValue<T>() |
지정된 형식의 값을 가져옵니다. |
확장 설치 및 제거
자신이 사용하는 컴퓨터와 다른 컴퓨터에 둘 다 Visual Studio 확장을 설치할 수 있습니다.
확장을 설치하려면
컴퓨터에서 VSIX 프로젝트에 의해 빌드된 .vsix 파일을 찾습니다.
솔루션 탐색기, VSIX 프로젝트 바로 가기 메뉴에서 선택 Windows 탐색기에서 폴더 열기.
bin\*\YourProject.vsix 파일을 찾습니다.
확장을 설치할 대상 컴퓨터에 .vsix 파일을 복사합니다.대상 컴퓨터는 현재 사용 중인 컴퓨터일 수도 있고 다른 컴퓨터일 수도 있습니다.
- 대상 컴퓨터에는 source.extension.vsixmanifest에 지정한 Visual Studio 버전 중 하나가 있어야 합니다.
대상 컴퓨터에서 열에 .vsix 파일.
Visual Studio Extension 설치 관리자가 열리고 확장이 설치됩니다.
Visual Studio를 시작하거나 다시 시작합니다.
확장을 제거하려면
에 도구 메뉴를 선택 확장 관리자.
설치된 확장을 확장합니다.
확장명을 선택 하 고 선택 제거.
드물기는 하지만 잘못된 확장은 로드되지 않으며 오류 창에 보고서가 만들어지지만 확장 관리자에는 나타나지 않습니다.이 경우 다음 위치에서 파일을 삭제하여 확장을 제거할 수 있습니다. 여기서 %LocalAppData%는 대개 DriveName:\Users\UserName\AppData\Local입니다.
%LocalAppData%\Microsoft\VisualStudio\11.0\Extensions
예제
이 예제에서는 요소 간의 종속성 관계에서 루프를 찾습니다.
저장 명령과 유효성 검사 메뉴 명령 둘 다에서 유효성을 검사합니다.
/// <summary>
/// Verify that there are no loops in the dependency relationsips.
/// In our project, no element should be a dependent of itself.
/// </summary>
/// <param name="context">Validation context for logs.</param>
/// <param name="element">Element to start validation from.</param>
[Export(typeof(System.Action<ValidationContext, object>))]
[ValidationMethod(ValidationCategories.Menu
| ValidationCategories.Save | ValidationCategories.Open)]
public void NoDependencyLoops(ValidationContext context, INamedElement element)
{
// The validation framework will call this method
// for every element in the model. But when we follow
// the dependencies from one element, we will validate others.
// So we keep a list of the elements that we don't need to validate again.
// The list is kept in the context cache so that it is passed
// from one execution of this method to another.
List<INamedElement> alreadySeen = null;
if (!context.TryGetCacheValue("No dependency loops", out alreadySeen))
{
alreadySeen = new List<INamedElement>();
context.SetCacheValue("No dependency loops", alreadySeen);
}
NoDependencyLoops(context, element,
new INamedElement[0], alreadySeen);
}
/// <summary>
/// Log an error if there is any loop in the dependency relationship.
/// </summary>
/// <param name="context">Validation context for logs.</param>
/// <param name="element">The element to be validated.</param>
/// <param name="dependants">Elements we've followed in this recursion.</param>
/// <param name="alreadySeen">Elements that have already been validated.</param>
/// <returns>true if no error was detected</returns>
private bool NoDependencyLoops(ValidationContext context,
INamedElement element, INamedElement[] dependants,
List<INamedElement> alreadySeen)
{
if (dependants.Contains(element))
{
context.LogError(string.Format("{0} should not depend on itself", element.Name),
"Fabrikam.UML.NoGenLoops", // unique code for this error
dependants.SkipWhile(e => e != element).ToArray());
// highlight elements that are in the loop
return false;
}
INamedElement[] dependantsPlusElement =
new INamedElement[dependants.Length + 1];
dependants.CopyTo(dependantsPlusElement, 0);
dependantsPlusElement[dependantsPlusElement.Length - 1] = element;
if (alreadySeen.Contains(element))
{
// We have already validated this when we started
// from another element during this validation run.
return true;
}
alreadySeen.Add(element);
foreach (INamedElement supplier in element.GetDependencySuppliers())
{
if (!NoDependencyLoops(context, supplier,
dependantsPlusElement, alreadySeen))
return false;
}
return true;
}