Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Unicode Attribute #22514

Merged
merged 2 commits into from
Oct 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
/// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
public sealed class UnicodeAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="UnicodeAttribute" /> class.
/// </summary>
/// <param name="unicode"> A value indicating whether the property can contain unicode characters or not. </param>
public UnicodeAttribute(bool unicode = true)
{
IsUnicode = unicode;
}

/// <summary>
/// A value indicating whether the property can contain unicode characters or not.
/// </summary>
public bool IsUnicode { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Utilities;

namespace Microsoft.EntityFrameworkCore.Scaffolding.Internal
Expand Down Expand Up @@ -291,6 +292,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 @@ -369,6 +371,25 @@ private void GenerateMaxLengthAttribute(IProperty property)
}
}

private void GenerateUnicodeAttribute(IProperty property)
{
if (property.ClrType != typeof(string))
smitpatel marked this conversation as resolved.
Show resolved Hide resolved
{
return;
}

var unicode = property.IsUnicode();
if (unicode.HasValue)
{
var unicodeAttribute = new AttributeWriter(nameof(UnicodeAttribute));
smitpatel marked this conversation as resolved.
Show resolved Hide resolved
if (!unicode.Value)
{
unicodeAttribute.AddParameter(_code.Literal(false));
}
_sb.AppendLine(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(attribute.IsUnicode, fromDataAnnotation: true);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// 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;
using System.Linq;
using Microsoft.EntityFrameworkCore.Internal;
using Xunit;
Expand Down Expand Up @@ -846,6 +847,60 @@ 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();
RaymondHuy marked this conversation as resolved.
Show resolved Hide resolved
smitpatel marked this conversation as resolved.
Show resolved Hide resolved
x.Property<string>("B").HasMaxLength(34).IsUnicode(false);
x.Property<string>("C").HasMaxLength(34);
}),
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]
RaymondHuy marked this conversation as resolved.
Show resolved Hide resolved
public string A { get; set; }
[StringLength(34)]
[Unicode(false)]
public string B { get; set; }
[StringLength(34)]
public string C { 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());
Assert.Null(entitType.GetProperty("C").IsUnicode());
});
}

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

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

modelBuilder.Entity<UnicodeAnnotationClass>(b =>
{
b.Property(e => e.PersonMiddleName);
b.Property(e => e.PersonAddress);
});

Validate(modelBuilder);

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

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

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

[Unicode]
public string PersonFirstName { get; set; }
smitpatel marked this conversation as resolved.
Show resolved Hide resolved

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

[Unicode]
public string PersonMiddleName;

[Unicode(false)]
public string PersonAddress;
smitpatel marked this conversation as resolved.
Show resolved Hide resolved
}

[ConditionalFact]
public virtual void OwnedEntityTypeAttribute_configures_one_reference_as_owned()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,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.UnicodeField)).Metadata.IsUnicode());
Assert.False(entityTypeBuilder.Property<string>(nameof(F.NonUnicodeField)).Metadata.IsUnicode());
}
#endregion

[ConditionalFact]
public void Property_attribute_convention_runs_for_private_property()
{
Expand Down Expand Up @@ -596,6 +646,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 @@ -629,6 +682,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 @@ -684,6 +743,12 @@ public class F
[Required]
public string Name;

[Unicode]
public string UnicodeField;

[Unicode(false)]
public string NonUnicodeField;

[Key]
public int MyPrimaryKey;

Expand Down