여러 엔터티 형식에서 동일한 방식으로 측면을 구성해야 하는 경우 다음 기술을 통해 코드 중복을 줄이고 논리를 통합할 수 있습니다.
아래에 나와 있는 코드 조각이 포함된 전체 샘플 프로젝트를 참조하세요.
OnModelCreating의 대량 구성
ModelBuilder에서 반환된 모든 작성기 객체는 모델을 구성하는 객체에 대한 낮은 수준의 액세스를 제공하는 Model 또는 Metadata 속성을 노출합니다. 특히 모델의 특정 개체를 반복하고 공통 구성을 적용할 수 있는 메서드가 있습니다.
다음 예제에서 모델에는 사용자 지정 값 형식 Currency이 포함됩니다.
public readonly struct Currency
{
public Currency(decimal amount)
=> Amount = amount;
public decimal Amount { get; }
public override string ToString()
=> $"${Amount}";
}
현재 EF 공급자가 데이터베이스 형식에 매핑하는 방법을 모르기 때문에 이 형식의 속성은 기본적으로 검색되지 않습니다. 이 코드 조각은 형식의 OnModelCreating 모든 속성을 추가하고 지원되는 형식 Currencydecimal에 값 변환기를 구성합니다.
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
foreach (var propertyInfo in entityType.ClrType.GetProperties())
{
if (propertyInfo.PropertyType == typeof(Currency))
{
entityType.AddProperty(propertyInfo)
.SetValueConverter(typeof(CurrencyConverter));
}
}
}
public class CurrencyConverter : ValueConverter<Currency, decimal>
{
public CurrencyConverter()
: base(
v => v.Amount,
v => new Currency(v))
{
}
}
메타데이터 API의 단점
-
Fluent API와 달리 모델에 대한 모든 수정은 명시적으로 수행해야 합니다. 예를 들어 일부
Currency속성이 규칙에 따라 탐색으로 구성된 경우 먼저 CLR 속성을 참조하는 탐색을 제거한 후 엔터티 형식 속성을 추가해야 합니다. #9117 이 향상됩니다. - 규칙은 변경 후 실행됩니다. 관례에 의해 발견된 탐색을 제거하면 관례가 다시 실행되어 탐색이 다시 추가될 수 있습니다. 이러한 일이 발생하지 않도록 하려면 반환된 개체를 호출 DelayConventions() 하고 나중에 삭제하여 속성이 추가될 때까지 규칙을 지연하거나 CLR 속성을 무시됨으로 표시해야 합니다 AddIgnored.
- 이 반복이 수행되고 구성이 적용되지 않은 후에 엔터티 형식이 추가될 수 있습니다. 일반적으로 이 코드는 끝에
OnModelCreating배치하여 방지할 수 있지만 상호 종속된 구성 집합이 두 개 있는 경우 일관되게 적용할 수 있는 순서가 없을 수 있습니다.
사전 설정 구성
EF Core를 사용하면 지정된 CLR 형식에 대해 매핑 구성을 한 번 지정할 수 있습니다. 그런 다음 해당 구성이 검색될 때 모델에서 해당 형식의 모든 속성에 적용됩니다. 모델 빌드 규칙을 실행하기 전에 모델의 측면을 구성하기 때문에 이를 "사전 규칙 모델 구성"이라고 합니다. 이러한 구성은 ConfigureConventions에서 파생된 형식을 DbContext를 재정의하여 적용됩니다.
이 예제에서는 값 변환기를 갖도록 형식 Currency 의 모든 속성을 구성하는 방법을 보여줍니다.
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
configurationBuilder
.Properties<Currency>()
.HaveConversion<CurrencyConverter>();
}
그리고 이 예제에서는 형식 string의 모든 속성에서 일부 패싯을 구성하는 방법을 보여 줍니다.
configurationBuilder
.Properties<string>()
.AreUnicode(false)
.HaveMaxLength(1024);
비고
호출 ConfigureConventions 에서 지정된 형식은 기본 형식, 인터페이스 또는 제네릭 형식 정의일 수 있습니다. 일치하는 모든 구성은 가장 덜 구체적인 순서대로 적용됩니다.
- 인터페이스
- 기본 형식
- 제네릭 형식 정의
- null을 허용하지 않는 값 형식
- 정확한 유형
중요합니다
사전 규칙 구성은 일치하는 개체가 모델에 추가되는 즉시 적용되는 명시적 구성과 동일합니다. 모든 규칙 및 데이터 주석을 재정의합니다. 예를 들어 위의 구성을 사용하면 모든 문자열 외래 키 속성이 주 키와 MaxLength 일치하지 않는 경우에도 유니코드가 아닌 1024로 만들어집니다.
형식을 무시함
사전 규칙 구성을 통해 형식을 무시할 수 있으며, 그 형식이 규칙에 의해 엔터티 형식이나 엔터티 형식의 속성으로 발견되지 않도록 방지할 수 있습니다.
configurationBuilder
.IgnoreAny(typeof(IList<>));
기본 형식 매핑
일반적으로 EF는 이 형식의 속성에 대한 값 변환기를 지정한 경우 공급자가 지원하지 않는 형식의 상수로 쿼리를 변환할 수 있습니다. 그러나 이 형식의 속성을 포함하지 않는 쿼리에서는 EF가 올바른 값 변환기를 찾을 수 있는 방법이 없습니다. 이 경우 공급자 형식 매핑을 추가하거나 재정의하도록 호출 DefaultTypeMapping 할 수 있습니다.
configurationBuilder
.DefaultTypeMapping<Currency>()
.HasConversion<CurrencyConverter>();
사전 규칙 구성의 제한 사항
- 이 방법을 사용하여 많은 측면을 구성할 수 없습니다. #6787 이 더 많은 형식으로 확장됩니다.
- 현재 구성은 CLR 유형에 의해서만 결정됩니다. #20418 은 사용자 지정 조건자를 허용합니다.
- 이 구성은 모델을 만들기 전에 수행됩니다. 적용할 때 충돌이 발생하면 예외 스택 추적에 메서드가
ConfigureConventions포함되지 않으므로 원인을 찾기가 더 어려울 수 있습니다.
관습
EF Core 모델 빌드 규칙은 빌드 중인 모델에 대한 변경 내용에 따라 트리거되는 논리를 포함하는 클래스입니다. 이렇게 하면 명시적 구성이 수행되고, 매핑 특성이 적용되고, 다른 규칙이 실행되므로 모델을 up-to-date로 유지합니다. 이에 참여하기 위해 모든 규칙은 해당 메서드가 트리거되는 시기를 결정하는 하나 이상의 인터페이스를 구현합니다. 예를 들어, IEntityTypeAddedConvention를 구현하는 컨벤션은 새 엔터티 형식이 모델에 추가될 때마다 트리거됩니다. 마찬가지로, IForeignKeyAddedConvention과 IKeyAddedConvention을 모두 구현하는 규약은 키 또는 외래 키가 모델에 추가될 때마다 트리거됩니다.
모델 빌드 규칙은 모델 구성을 제어하는 강력한 방법이지만 복잡하고 제대로 하기 어려울 수 있습니다. 대부분의 경우 사전 규칙 모델 구성 을 대신 사용하여 속성 및 형식에 대한 공통 구성을 쉽게 지정할 수 있습니다.
새 규칙 추가
예: 판별자 속성의 길이 제한
계층별 테이블 상속 매핑 전략에는 지정된 행에 표시되는 형식을 지정하기 위해 판별자 열이 필요합니다. 기본적으로 EF는 판별자의 바인딩되지 않은 문자열 열을 사용하여 판별자 길이에 대해 작동합니다. 그러나 판별자 문자열의 최대 길이를 제한하면 보다 효율적인 스토리지 및 쿼리를 수행할 수 있습니다. 이렇게 할 새 규칙을 만들어 보겠습니다.
EF Core 모델 빌드 규칙은 빌드할 때 모델의 변경 내용에 따라 트리거됩니다. 이렇게 하면 명시적 구성이 수행되고, 매핑 특성이 적용되고, 다른 규칙이 실행되므로 모델을 up-to-date로 유지합니다. 이에 참여하기 위해 모든 규칙은 규칙이 트리거되는 시기를 결정하는 하나 이상의 인터페이스를 구현합니다. 예를 들어, IEntityTypeAddedConvention를 구현하는 컨벤션은 새 엔터티 형식이 모델에 추가될 때마다 트리거됩니다. 마찬가지로, IForeignKeyAddedConvention과 IKeyAddedConvention을 모두 구현하는 규약은 키 또는 외래 키가 모델에 추가될 때마다 트리거됩니다.
한 지점에서 모델에 대한 구성이 나중에 변경되거나 제거될 수 있으므로 구현할 인터페이스를 아는 것은 어려울 수 있습니다. 예를 들어 규칙에 따라 키를 만들 수 있지만 나중에 다른 키를 명시적으로 구성할 때 대체됩니다.
판별자 길이 규칙을 처음 구현하여 좀 더 구체적으로 만들어 보겠습니다.
public class DiscriminatorLengthConvention1 : IEntityTypeBaseTypeChangedConvention
{
public void ProcessEntityTypeBaseTypeChanged(
IConventionEntityTypeBuilder entityTypeBuilder,
IConventionEntityType? newBaseType,
IConventionEntityType? oldBaseType,
IConventionContext<IConventionEntityType> context)
{
var discriminatorProperty = entityTypeBuilder.Metadata.FindDiscriminatorProperty();
if (discriminatorProperty != null
&& discriminatorProperty.ClrType == typeof(string))
{
discriminatorProperty.Builder.HasMaxLength(24);
}
}
}
이 관습은 IEntityTypeBaseTypeChangedConvention를 구현하며, 이는 엔터티 유형의 매핑된 상속 계층이 변경될 때마다 트리거된다는 의미입니다. 그런 다음 이 규칙은 계층 구조에 대한 문자열 판별자 속성을 찾아 구성합니다.
이 관례는 Add에서 ConfigureConventions를 호출하여 사용됩니다.
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
configurationBuilder.Conventions.Add(_ => new DiscriminatorLengthConvention1());
}
비고
규칙의 인스턴스를 직접 추가하는 대신 메서드는 Add 규칙의 인스턴스를 만들기 위한 팩터리를 허용합니다. 이렇게 하면 관례가 EF Core 내부 서비스 공급자의 종속성을 활용할 수 있습니다. 이 규칙에는 종속성이 없으므로 서비스 공급자 매개 변수의 이름은 사용되지 _않음을 나타냅니다.
모델을 빌드하고 엔터티 형식을 Post 살펴보면 이것이 작동했음을 보여 하며 판별자 속성은 이제 최대 길이 24로 구성됩니다.
Discriminator (no field, string) Shadow Required AfterSave:Throw MaxLength(24)
하지만 이제 다른 판별자 속성을 명시적으로 구성하면 어떻게 되나요? 다음은 그 예입니다.
modelBuilder.Entity<Post>()
.HasDiscriminator<string>("PostTypeDiscriminator")
.HasValue<Post>("Post")
.HasValue<FeaturedPost>("Featured");
모델의 디버그 뷰 를 살펴보면 판별자 길이가 더 이상 구성되지 않습니다.
PostTypeDiscriminator (no field, string) Shadow Required AfterSave:Throw
이는 사용자 지정 판별자를 추가했을 때 규칙에서 구성한 판별자 속성이 나중에 제거되었기 때문입니다. 판별자 변경에 대응하기 위해 규칙에 다른 인터페이스를 구현하여 이 문제를 해결할 수 있지만 구현할 인터페이스를 파악하는 것은 쉽지 않습니다.
다행히도 더 쉬운 방법이 있습니다. 대부분의 경우, 최종 모델이 올바르기만 하면, 모델이 만드는 동안 어떻게 보이는지는 중요하지 않습니다. 또한 적용하려는 구성은 다른 규칙을 트리거하여 반응할 필요가 없는 경우가 많습니다. 따라서 우리의 표준은 IModelFinalizingConvention을 구현할 수 있습니다. 모델 종료 규칙은 다른 모든 모델 빌드가 완료된 후에 실행되므로 모델의 거의 최종 상태에 액세스할 수 있습니다. 대화형 규칙은 각 모델 변경에 반응하고 메서드 실행의 어떤 시점에서도 모델이 up-to-date 상태인지 확인하는 것과는 대조됩니다. 모델 완성 규칙은 일반적으로 전체 모델을 따라가면서 모델 요소를 구성합니다. 따라서 이 경우 모델에서 모든 판별자를 찾아 구성합니다.
public class DiscriminatorLengthConvention2 : IModelFinalizingConvention
{
public void ProcessModelFinalizing(IConventionModelBuilder modelBuilder, IConventionContext<IConventionModelBuilder> context)
{
foreach (var entityType in modelBuilder.Metadata.GetEntityTypes()
.Where(entityType => entityType.BaseType == null))
{
var discriminatorProperty = entityType.FindDiscriminatorProperty();
if (discriminatorProperty != null
&& discriminatorProperty.ClrType == typeof(string))
{
discriminatorProperty.Builder.HasMaxLength(24);
}
}
}
}
이 새 규칙을 사용하여 모델을 빌드한 후에는 이제 판별자 길이가 사용자 지정되었음에도 불구하고 올바르게 구성된 것을 확인할 수 있습니다.
PostTypeDiscriminator (no field, string) Shadow Required AfterSave:Throw MaxLength(24)
한 단계 더 나아가 최대 길이를 가장 긴 판별자 값의 길이로 구성할 수 있습니다.
public class DiscriminatorLengthConvention3 : IModelFinalizingConvention
{
public void ProcessModelFinalizing(IConventionModelBuilder modelBuilder, IConventionContext<IConventionModelBuilder> context)
{
foreach (var entityType in modelBuilder.Metadata.GetEntityTypes()
.Where(entityType => entityType.BaseType == null))
{
var discriminatorProperty = entityType.FindDiscriminatorProperty();
if (discriminatorProperty != null
&& discriminatorProperty.ClrType == typeof(string))
{
var maxDiscriminatorValueLength =
entityType.GetDerivedTypesInclusive().Select(e => ((string)e.GetDiscriminatorValue()!).Length).Max();
discriminatorProperty.Builder.HasMaxLength(maxDiscriminatorValueLength);
}
}
}
}
이제 판별자 열 최대 길이는 사용 중인 가장 긴 판별자 값인 "추천"의 길이인 8입니다.
PostTypeDiscriminator (no field, string) Shadow Required AfterSave:Throw MaxLength(8)
예: 모든 문자열 속성의 기본 길이
문자열 속성에 대한 기본 최대 길이를 설정하는 종료 규칙을 사용할 수 있는 또 다른 예제를 살펴보겠습니다 . 규칙은 이전 예제와 매우 유사합니다.
public class MaxStringLengthConvention : IModelFinalizingConvention
{
public void ProcessModelFinalizing(IConventionModelBuilder modelBuilder, IConventionContext<IConventionModelBuilder> context)
{
foreach (var property in modelBuilder.Metadata.GetEntityTypes()
.SelectMany(
entityType => entityType.GetDeclaredProperties()
.Where(
property => property.ClrType == typeof(string))))
{
property.Builder.HasMaxLength(512);
}
}
}
이 규칙은 매우 간단합니다. 모델의 모든 문자열 속성을 찾아 최대 길이를 512로 설정합니다. 에 대한 Post속성의 디버그 뷰를 보면 이제 모든 문자열 속성의 최대 길이가 512임을 알 수 있습니다.
EntityType: Post
Properties:
Id (int) Required PK AfterSave:Throw ValueGenerated.OnAdd
AuthorId (no field, int?) Shadow FK Index
BlogId (no field, int) Shadow Required FK Index
Content (string) Required MaxLength(512)
Discriminator (no field, string) Shadow Required AfterSave:Throw MaxLength(512)
PublishedOn (DateTime) Required
Title (string) Required MaxLength(512)
비고
사전 규칙 구성에서 동일한 작업을 수행할 수 있지만 규칙을 사용하면 적용 가능한 속성을 추가로 필터링하고 데이터 주석이 구성을 재정의할 수 있습니다.
마지막으로, 이 예제를 나가기 전에 동시에 두 가지를 모두 MaxStringLengthConventionDiscriminatorLengthConvention3 사용하면 어떻게 되나요? 즉, 모델 종료 규칙이 추가된 순서대로 실행되므로 추가되는 순서에 따라 달라집니다. 따라서 마지막으로 추가된 경우 MaxStringLengthConvention 마지막에 실행되고 판별자 속성의 최대 길이가 512로 설정됩니다. 따라서 이 경우 다른 모든 문자열 속성을 512로 유지하면서 판별자 속성에 대한 기본 최대 길이를 재정의할 수 있도록 마지막을 추가하는 DiscriminatorLengthConvention3 것이 좋습니다.
기존 규칙 바꾸기
때로는 기존 규칙을 완전히 제거하는 대신 기본적으로 동일한 작업을 수행하지만 동작이 변경된 규칙으로 대체하려고 합니다. 이는 기존 규칙이 적절하게 트리거해야 하는 인터페이스를 이미 구현하기 때문에 유용합니다.
예: 동의 기반 속성 매핑
EF Core는 규칙에 따라 모든 공용 읽기-쓰기 속성을 매핑합니다. 엔터티 형식이 정의된 방식에는 적합하지 않을 수 있습니다. 이를 변경하기 위해, PropertyDiscoveryConvention에서 명시적으로 매핑되거나 OnModelCreating라는 새 속성으로 표시되지 않는 한 속성을 매핑하지 않는 Persist 자체 구현으로 대체할 수 있습니다.
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public sealed class PersistAttribute : Attribute
{
}
새 규칙은 다음과 같습니다.
public class AttributeBasedPropertyDiscoveryConvention : PropertyDiscoveryConvention
{
public AttributeBasedPropertyDiscoveryConvention(ProviderConventionSetBuilderDependencies dependencies)
: base(dependencies)
{
}
public override void ProcessEntityTypeAdded(
IConventionEntityTypeBuilder entityTypeBuilder,
IConventionContext<IConventionEntityTypeBuilder> context)
=> Process(entityTypeBuilder);
public override void ProcessEntityTypeBaseTypeChanged(
IConventionEntityTypeBuilder entityTypeBuilder,
IConventionEntityType? newBaseType,
IConventionEntityType? oldBaseType,
IConventionContext<IConventionEntityType> context)
{
if ((newBaseType == null
|| oldBaseType != null)
&& entityTypeBuilder.Metadata.BaseType == newBaseType)
{
Process(entityTypeBuilder);
}
}
private void Process(IConventionEntityTypeBuilder entityTypeBuilder)
{
foreach (var memberInfo in GetRuntimeMembers())
{
if (Attribute.IsDefined(memberInfo, typeof(PersistAttribute), inherit: true))
{
entityTypeBuilder.Property(memberInfo);
}
else if (memberInfo is PropertyInfo propertyInfo
&& Dependencies.TypeMappingSource.FindMapping(propertyInfo) != null)
{
entityTypeBuilder.Ignore(propertyInfo.Name);
}
}
IEnumerable<MemberInfo> GetRuntimeMembers()
{
var clrType = entityTypeBuilder.Metadata.ClrType;
foreach (var property in clrType.GetRuntimeProperties()
.Where(p => p.GetMethod != null && !p.GetMethod.IsStatic))
{
yield return property;
}
foreach (var property in clrType.GetRuntimeFields())
{
yield return property;
}
}
}
}
팁 (조언)
기본 제공 규칙을 바꿀 때 새 규칙 구현은 기존 규칙 클래스에서 상속되어야 합니다. 일부 규칙에는 관계형 또는 공급자별 구현이 있으며, 이 경우 새 규칙 구현은 사용 중인 데이터베이스 공급자에 대한 가장 구체적인 기존 규칙 클래스에서 상속되어야 합니다.
그런 다음, 다음의 메서드를 Replace 사용하여 규칙이 등록됩니다.ConfigureConventions
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
configurationBuilder.Conventions.Replace<PropertyDiscoveryConvention>(
serviceProvider => new AttributeBasedPropertyDiscoveryConvention(
serviceProvider.GetRequiredService<ProviderConventionSetBuilderDependencies>()));
}
팁 (조언)
기존 규칙에 종속성 개체가 나타내는 ProviderConventionSetBuilderDependencies 종속성이 있는 경우입니다. 이러한 항목은 GetRequiredService를 사용하여 내부 서비스 공급자로부터 가져와 규칙 생성자에 전달됩니다.
이 규칙은 [Persist]로 표시된 필드와 속성을 매핑할 수 있도록 허용합니다. 즉, 모델에서 프라이빗 필드를 숨겨진 키로 사용할 수 있습니다.
예를 들어 다음 엔터티 형식을 고려합니다.
public class LaundryBasket
{
[Persist]
[Key]
private readonly int _id;
[Persist]
public int TenantId { get; init; }
public bool IsClean { get; set; }
public List<Garment> Garments { get; } = new();
}
public class Garment
{
public Garment(string name, string color)
{
Name = name;
Color = color;
}
[Persist]
[Key]
private readonly int _id;
[Persist]
public int TenantId { get; init; }
[Persist]
public string Name { get; }
[Persist]
public string Color { get; }
public bool IsClean { get; set; }
public LaundryBasket? Basket { get; set; }
}
이러한 엔터티 형식에서 빌드된 모델은 다음과 같습니다.
Model:
EntityType: Garment
Properties:
_id (_id, int) Required PK AfterSave:Throw ValueGenerated.OnAdd
Basket_id (no field, int?) Shadow FK Index
Color (string) Required
Name (string) Required
TenantId (int) Required
Navigations:
Basket (LaundryBasket) ToPrincipal LaundryBasket Inverse: Garments
Keys:
_id PK
Foreign keys:
Garment {'Basket_id'} -> LaundryBasket {'_id'} ToDependent: Garments ToPrincipal: Basket ClientSetNull
Indexes:
Basket_id
EntityType: LaundryBasket
Properties:
_id (_id, int) Required PK AfterSave:Throw ValueGenerated.OnAdd
TenantId (int) Required
Navigations:
Garments (List<Garment>) Collection ToDependent Garment Inverse: Basket
Keys:
_id PK
일반적으로 IsClean 매핑되었을 수 있지만, 매핑 [Persist]되지 않았으므로 이제 매핑되지 않은 속성으로 처리됩니다.
팁 (조언)
속성을 추가로 구성하기 위해 매핑된 후에 실행해야 하는 기존 모델 종료 규칙이 있기 때문에 이 규칙을 모델 종료 규칙으로 구현할 수 없습니다.
규칙 구현 고려 사항
EF Core는 모든 구성이 어떻게 만들어졌는지 추적합니다. 이것은 ConfigurationSource 열거형으로 표시됩니다. 다양한 종류의 구성은 다음과 같습니다.
-
Explicit: 모델 요소가 에서 명시적으로 구성되었습니다.OnModelCreating -
DataAnnotation: 모델 요소가 CLR 형식의 매핑 특성(즉, 데이터 주석)을 사용하여 구성되었습니다. -
Convention: 모델 요소가 모델 빌드 규칙에 의해 구성되었습니다.
관례는 DataAnnotation 또는 Explicit로 표시된 구성을 재정의해서는 안 됩니다. 이 작업은 규칙 작성기(예 IConventionPropertyBuilder: 속성에서 Builder 가져온 규칙 작성기)를 사용하여 수행됩니다. 다음은 그 예입니다.
property.Builder.HasMaxLength(512);
규칙 작성기를 호출 HasMaxLength 하면 매핑 특성 또는 OnModelCreating에 의해 아직 구성되지 않은 경우에만 최대 길이가 설정됩니다.
이와 같은 작성기 메서드에는 두 번째 매개 변수 fromDataAnnotation도 있습니다. 규칙이 매핑 특성을 대신하여 구성을 만드는 경우 이 true 설정을 지정합니다. 다음은 그 예입니다.
property.Builder.HasMaxLength(512, fromDataAnnotation: true);
이를 통해 ConfigurationSource의 값을 DataAnnotation으로 설정합니다. 즉, OnModelCreating에서 명시적인 매핑을 통해서는 값을 재정의할 수 있지만, 비매핑 특성 규칙에 의해서는 불가능합니다.
현재 구성을 재정의할 수 없는 경우 메서드가 반환 null됩니다. 추가 구성을 수행해야 하는 경우 이를 고려해야 합니다.
property.Builder.HasMaxLength(512)?.IsUnicode(false);
유니코드 구성을 재정의할 수 없는 경우 최대 길이는 계속 설정됩니다. 두 호출이 모두 성공하는 경우에만 패싯을 구성해야 하는 경우 다음을 호출 CanSetMaxLengthCanSetIsUnicode하여 선제적으로 확인할 수 있습니다.
public class MaxStringLengthNonUnicodeConvention : IModelFinalizingConvention
{
public void ProcessModelFinalizing(IConventionModelBuilder modelBuilder, IConventionContext<IConventionModelBuilder> context)
{
foreach (var property in modelBuilder.Metadata.GetEntityTypes()
.SelectMany(
entityType => entityType.GetDeclaredProperties()
.Where(
property => property.ClrType == typeof(string))))
{
var propertyBuilder = property.Builder;
if (propertyBuilder.CanSetMaxLength(512)
&& propertyBuilder.CanSetIsUnicode(false))
{
propertyBuilder.HasMaxLength(512)!.IsUnicode(false);
}
}
}
}
여기서는 HasMaxLength 호출이 null을 반환하지 않는다는 것을 확신할 수 있습니다.
HasMaxLength에서 반환된 작성기 인스턴스를 사용하는 것이 좋습니다. 이는 propertyBuilder와 다를 수 있기 때문입니다.
비고
다른 규칙은 규칙이 변경된 직후에 트리거되지 않으며 모든 규칙이 현재 변경 처리가 완료될 때까지 지연됩니다.
컨벤션 컨텍스트 (IConventionContext)
모든 규칙 메서드에도 매개 변수가 있습니다 IConventionContext<TMetadata> . 특정 경우에 유용할 수 있는 메서드를 제공합니다.
예: NotMappedAttribute 규칙
이 관례는 모델에 추가된 형식에서 NotMappedAttribute을 찾아 해당 엔터티 형식을 모델에서 제거하려고 시도합니다. 그러나 엔터티 형식이 모델에서 제거되면 구현 ProcessEntityTypeAdded 하는 다른 규칙을 더 이상 실행할 필요가 없습니다. 이 작업은 다음을 호출 StopProcessing()하여 수행할 수 있습니다.
public virtual void ProcessEntityTypeAdded(
IConventionEntityTypeBuilder entityTypeBuilder,
IConventionContext<IConventionEntityTypeBuilder> context)
{
var type = entityTypeBuilder.Metadata.ClrType;
if (!Attribute.IsDefined(type, typeof(NotMappedAttribute), inherit: true))
{
return;
}
if (entityTypeBuilder.ModelBuilder.Ignore(entityTypeBuilder.Metadata.Name, fromDataAnnotation: true) != null)
{
context.StopProcessing();
}
}
IConventionModel
규칙에 전달된 모든 작성기 개체는 Metadata 모델을 구성하는 개체에 대한 낮은 수준의 액세스를 제공하는 속성을 노출합니다. 특히 모델에서 특정 개체를 반복하고 예제: 모든 문자열 속성의 기본 길이와 같이 공통 구성을 적용할 수 있는 메서드가 있습니다. 이 API는 IMutableModel에 표시된 것과 유사합니다.
주의
작성기는 지정된 구성이 Fluent API 또는 데이터 주석을 사용하여 이미 지정된 항목을 재정의하는지 여부를 확인하므로 속성으로 Builder 노출된 작성기에서 메서드를 호출하여 항상 구성을 수행하는 것이 좋습니다.
대량 구성에 각 방법을 사용하는 경우
다음과 같은 경우 메타데이터 API 를 사용합니다.
- 구성은 특정 시간에 적용해야 하며 모델의 이후 변경 내용에 반응하지 않습니다.
- 모델 빌드 속도는 매우 중요합니다. 메타데이터 API는 안전 검사가 적기 때문에 다른 방법보다 약간 더 빠를 수 있지만 컴파일된 모델을 사용하면 시작 시간이 훨씬 더 빨라집니다.
다음과 같은 경우 사전 규칙 모델 구성 을 사용합니다.
- 적용 가능성 조건은 형식에만 따라 달라지기 때문에 간단합니다.
- 지정된 형식의 속성이 모델에 추가되고 데이터 주석 및 규칙을 재정의하는 시점에 구성을 적용해야 합니다.
최종화 관례를 다음과 같은 경우에 사용하십시오.
- 적용 가능성 조건은 복잡합니다.
- 구성은 데이터 주석으로 지정된 항목을 재정의해서는 안 됩니다.
- 여러 관습이 서로 의존합니다. 종료 규칙은 추가된 순서대로 실행되므로 나중에 규칙을 종료하여 변경한 내용에 대응할 수 없습니다.
- 논리는 여러 컨텍스트 간에 공유됩니다. 대화형 규칙은 다른 방법보다 안전합니다.
.NET