Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
roji committed Feb 5, 2019
1 parent b70e29a commit 5cc9ea4
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// 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 System.Reflection;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Utilities;

namespace Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal
{
/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public class NonNullableReferencePropertyConvention : IPropertyAddedConvention, IPropertyFieldChangedConvention
{
private const string NullableAttributeFullName = "System.Runtime.CompilerServices.Nullable";

/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public virtual InternalPropertyBuilder Apply(InternalPropertyBuilder propertyBuilder)
{
Check.NotNull(propertyBuilder, nameof(propertyBuilder));

var memberInfo = propertyBuilder.Metadata.GetIdentifyingMemberInfo();
if (memberInfo == null)
{
return propertyBuilder;
}

if (Attribute.GetCustomAttributes(memberInfo) is Attribute[] attributes &&
attributes.FirstOrDefault(a => a.GetType().FullName == NullableAttributeFullName) is Attribute attribute)
{
throw new NotImplementedException("Blocking on https://github.com/dotnet/roslyn/issues/30143");
}

return propertyBuilder;
}

/// <summary>
/// This API supports the Entity Framework Core infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
public virtual bool Apply(InternalPropertyBuilder propertyBuilder, FieldInfo oldFieldInfo)
{
Apply(propertyBuilder);
return true;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
using System.ComponentModel.DataAnnotations;
using Microsoft.EntityFrameworkCore.InMemory.Storage.Internal;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.TestUtilities;
using Xunit;

#nullable enable

namespace Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal
{
public class NonNullableReferencePropertyConventionTest
{
[Theory]
[InlineData(nameof(A.NullAwareNonNullable), false)]
[InlineData(nameof(A.NullAwareNullable), true)]
[InlineData(nameof(A.NullObliviousNonNullable), true)]
[InlineData(nameof(A.NullObliviousNullable), true)]
[InlineData(nameof(A.RequiredAndNullable), false)]
public void Nullability(string propertyName, bool isNullable)
{
var modelBuilder = CreateModelBuilder();
var entityTypeBuilder = modelBuilder.Entity<A>();

Assert.Equal(isNullable, entityTypeBuilder.Property(propertyName).Metadata.IsNullable);
}

[Fact]
public void RequiredAttribute_prevails_over_nullability()
{
var entityTypeBuilder = CreateInternalEntityTypeBuilder<A>();
var propertyBuilder = entityTypeBuilder.Property(nameof(A.RequiredAndNullable), typeof(string), ConfigurationSource.Explicit);

new RequiredPropertyAttributeConvention().Apply(propertyBuilder);

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

private InternalEntityTypeBuilder CreateInternalEntityTypeBuilder<T>()
{
var conventionSet = new ConventionSet();
conventionSet.EntityTypeAddedConventions.Add(
new PropertyDiscoveryConvention(
CreateTypeMapper()));

var modelBuilder = new InternalModelBuilder(new Model(conventionSet));

return modelBuilder.Entity(typeof(T), ConfigurationSource.Explicit);
}

private static ITypeMappingSource CreateTypeMapper()
=> TestServiceFactory.Instance.Create<InMemoryTypeMappingSource>();

private class A
{
public int Id { get; set; }

public string NullAwareNonNullable { get; set; } = "";
public string? NullAwareNullable { get; set; }

#nullable disable
#pragma warning disable CS8632 // The annotation for nullable reference types should only be used in code within a '#nullable' context.
public string NullObliviousNonNullable { get; set; }
public string? NullObliviousNullable { get; set; }
#pragma warning restore CS8632 // The annotation for nullable reference types should only be used in code within a '#nullable' context.
#nullable enable

[Required]
public string? RequiredAndNullable { get; set; }
}

private static ModelBuilder CreateModelBuilder() => InMemoryTestHelpers.Instance.CreateConventionBuilder();

private class MyContext : DbContext
{
protected internal override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseInMemoryDatabase(nameof(MyContext));
}
}
}

0 comments on commit 5cc9ea4

Please sign in to comment.