Skip to content

Commit

Permalink
Support Unicode Attribute
Browse files Browse the repository at this point in the history
Add Unicode annotation and the capability of generating UnicodeAttribute when scaffolding.
  • Loading branch information
HuyLuong committed Sep 12, 2020
1 parent b647bd1 commit 5cdae72
Show file tree
Hide file tree
Showing 7 changed files with 234 additions and 0 deletions.
28 changes: 28 additions & 0 deletions src/EFCore.Abstractions/UnicodeAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;

namespace Microsoft.EntityFrameworkCore
{
/// <summary>
/// Configures the property as capable of persisting unicode characters. Can only be set on System.String properties.
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false)]
public sealed class UnicodeAttribute : Attribute
{
/// <summary>
/// A value indicating whether the property can contain unicode characters or not.
/// </summary>
public bool IsUnicode { get; }

/// <summary>
/// Initializes a new instance of the <see cref="UnicodeAttribute" /> class.
/// </summary>
/// <param name="isUnicode">A value indicating whether the property can contain unicode characters or not.</param>
public UnicodeAttribute(bool isUnicode = true)
{
IsUnicode = isUnicode;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ protected virtual void GeneratePropertyDataAnnotations([NotNull] IProperty prope
GenerateRequiredAttribute(property);
GenerateColumnAttribute(property);
GenerateMaxLengthAttribute(property);
GenerateUnicodeAttribute(property);

var annotations = _annotationCodeGenerator
.FilterIgnoredAnnotations(property.GetAnnotations())
Expand Down Expand Up @@ -365,6 +366,29 @@ private void GenerateMaxLengthAttribute(IProperty property)
}
}

private void GenerateUnicodeAttribute(IProperty property)
{
if (property.ClrType != typeof(string))
{
return;
}

var isUnicode = property.IsUnicode();

if (isUnicode.HasValue)
{
if (!isUnicode.Value)
{
var unicodeAttribute = new AttributeWriter(nameof(UnicodeAttribute));

unicodeAttribute.AddParameter(_code.Literal(false));

_sb.AppendLine(unicodeAttribute.ToString());
}
else _sb.AppendLine(new AttributeWriter(nameof(UnicodeAttribute)).ToString());
}
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ public virtual ConventionSet CreateConventionSet()
var stringLengthAttributeConvention = new StringLengthAttributeConvention(Dependencies);
var timestampAttributeConvention = new TimestampAttributeConvention(Dependencies);
var backingFieldAttributeConvention = new BackingFieldAttributeConvention(Dependencies);
var unicodeAttributeConvention = new UnicodeAttributeConvention(Dependencies);

conventionSet.PropertyAddedConventions.Add(backingFieldAttributeConvention);
conventionSet.PropertyAddedConventions.Add(backingFieldConvention);
Expand All @@ -126,6 +127,7 @@ public virtual ConventionSet CreateConventionSet()
conventionSet.PropertyAddedConventions.Add(keyAttributeConvention);
conventionSet.PropertyAddedConventions.Add(keyDiscoveryConvention);
conventionSet.PropertyAddedConventions.Add(foreignKeyPropertyDiscoveryConvention);
conventionSet.PropertyAddedConventions.Add(unicodeAttributeConvention);

conventionSet.EntityTypePrimaryKeyChangedConventions.Add(foreignKeyPropertyDiscoveryConvention);
conventionSet.EntityTypePrimaryKeyChangedConventions.Add(valueGeneratorConvention);
Expand Down
41 changes: 41 additions & 0 deletions src/EFCore/Metadata/Conventions/UnicodeAttributeConvention.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Reflection;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure;

namespace Microsoft.EntityFrameworkCore.Metadata.Conventions
{
/// <summary>
/// A convention that configures the Unicode based on the <see cref="UnicodeAttribute" /> applied on the property.
/// </summary>
public class UnicodeAttributeConvention : PropertyAttributeConventionBase<UnicodeAttribute>
{
/// <summary>
/// Creates a new instance of <see cref="UnicodeAttributeConvention" />.
/// </summary>
/// <param name="dependencies"> Parameter object containing dependencies for this convention. </param>
public UnicodeAttributeConvention([NotNull] ProviderConventionSetBuilderDependencies dependencies)
: base(dependencies)
{
}

/// <summary>
/// Called after a property is added to the entity type with an attribute on the associated CLR property or field.
/// </summary>
/// <param name="propertyBuilder"> The builder for the property. </param>
/// <param name="attribute"> The attribute. </param>
/// <param name="clrMember"> The member that has the attribute. </param>
/// <param name="context"> Additional information associated with convention execution. </param>
protected override void ProcessPropertyAdded(
IConventionPropertyBuilder propertyBuilder,
UnicodeAttribute attribute,
MemberInfo clrMember,
IConventionContext context)
{
propertyBuilder.IsUnicode(unicode: attribute.IsUnicode, fromDataAnnotation: true);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -846,6 +846,56 @@ public partial class Entity
});
}

[ConditionalFact]
public void UnicodeAttribute_is_generated_for_property()
{
Test(
modelBuilder => modelBuilder
.Entity(
"Entity",
x =>
{
x.Property<int>("Id");
x.Property<string>("A").HasMaxLength(34).IsUnicode();
x.Property<string>("B").HasMaxLength(34).IsUnicode(false);
}),
new ModelCodeGenerationOptions { UseDataAnnotations = true },
code =>
{
AssertFileContents(
@"using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Microsoft.EntityFrameworkCore;
#nullable disable
namespace TestNamespace
{
public partial class Entity
{
[Key]
public int Id { get; set; }
[StringLength(34)]
[Unicode]
public string A { get; set; }
[StringLength(34)]
[Unicode(false)]
public string B { get; set; }
}
}
",
code.AdditionalFiles.Single(f => f.Path == "Entity.cs"));
},
model =>
{
var entitType = model.FindEntityType("TestNamespace.Entity");
Assert.True(entitType.GetProperty("A").IsUnicode());
Assert.False(entitType.GetProperty("B").IsUnicode());
});
}

[ConditionalFact]
public void Properties_are_sorted_in_order_of_definition_in_table()
{
Expand Down
24 changes: 24 additions & 0 deletions test/EFCore.Specification.Tests/DataAnnotationTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2375,6 +2375,30 @@ public virtual void TimestampAttribute_throws_if_value_in_database_changed()
});
}

[ConditionalFact]
public virtual void Unicode_annotation_is_enabled()
{
var modelBuilder = CreateModelBuilder();

modelBuilder.Entity<UnicodeAnnotationClass>();

Validate(modelBuilder);

Assert.True(GetProperty<UnicodeAnnotationClass>(modelBuilder, "PersonFirstName").IsUnicode());
Assert.False(GetProperty<UnicodeAnnotationClass>(modelBuilder, "PersonLastName").IsUnicode());
}

protected class UnicodeAnnotationClass
{
public int Id { get; set; }

[Unicode]
public string PersonFirstName { get; set; }

[Unicode(false)]
public string PersonLastName { get; set; }
}

[ConditionalFact]
public virtual void OwnedEntityTypeAttribute_configures_one_reference_as_owned()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,56 @@ public void BackingFieldAttribute_does_not_override_configuration_from_explicit_

#endregion

#region UnicodeAttribute
[ConditionalFact]
public void UnicodeAttribute_overrides_configuration_from_convention_source()
{
var entityTypeBuilder = CreateInternalEntityTypeBuilder<A>();

var propertyBuilder = entityTypeBuilder.Property(typeof(string), "UnicodeProperty", ConfigurationSource.Explicit);

propertyBuilder.IsUnicode(false, ConfigurationSource.Convention);

RunConvention(propertyBuilder);

Assert.True(propertyBuilder.Metadata.IsUnicode());
}

[ConditionalFact]
public void UnicodeAttribute_does_not_override_configuration_from_explicit_source()
{
var entityTypeBuilder = CreateInternalEntityTypeBuilder<A>();

var propertyBuilder = entityTypeBuilder.Property(typeof(string), "UnicodeProperty", ConfigurationSource.Explicit);

propertyBuilder.IsUnicode(false, ConfigurationSource.Explicit);

RunConvention(propertyBuilder);

Assert.False(propertyBuilder.Metadata.IsUnicode());
}

[ConditionalFact]
public void UnicodeAttribute_sets_unicode_with_conventional_builder()
{
var modelBuilder = CreateModelBuilder();
var entityTypeBuilder = modelBuilder.Entity<A>();

Assert.True(entityTypeBuilder.Property(e => e.UnicodeProperty).Metadata.IsUnicode());
Assert.False(entityTypeBuilder.Property(e => e.NonUnicodeProperty).Metadata.IsUnicode());
}

[ConditionalFact]
public void UnicodeAttribute_on_field_sets_unicode_with_conventional_builder()
{
var modelBuilder = CreateModelBuilder();
var entityTypeBuilder = modelBuilder.Entity<F>();

Assert.True(entityTypeBuilder.Property<string>(nameof(F.UnicodeProperty)).Metadata.IsUnicode());
Assert.False(entityTypeBuilder.Property<string>(nameof(F.NonUnicodeProperty)).Metadata.IsUnicode());
}
#endregion

[ConditionalFact]
public void Property_attribute_convention_runs_for_private_property()
{
Expand Down Expand Up @@ -595,6 +645,9 @@ private static void RunConvention(InternalPropertyBuilder propertyBuilder)

new KeyAttributeConvention(dependencies)
.ProcessPropertyAdded(propertyBuilder, context);

new UnicodeAttributeConvention(dependencies)
.ProcessPropertyAdded(propertyBuilder, context);
}

private void RunConvention(InternalEntityTypeBuilder entityTypeBuilder)
Expand Down Expand Up @@ -628,6 +681,12 @@ private class A
[Required]
public string Name { get; set; }

[Unicode]
public string UnicodeProperty { get; set; }

[Unicode(false)]
public string NonUnicodeProperty { get; set; }

[Key]
public int MyPrimaryKey { get; set; }

Expand Down Expand Up @@ -683,6 +742,12 @@ public class F
[Required]
public string Name;

[Unicode]
public string UnicodeProperty { get; set; }

[Unicode(false)]
public string NonUnicodeProperty { get; set; }

[Key]
public int MyPrimaryKey;

Expand Down

0 comments on commit 5cdae72

Please sign in to comment.