mirror of
https://github.com/zoriya/EntityFrameworkCore.Projectables.git
synced 2025-12-06 05:56:10 +00:00
Merge pull request #83 from am-creations/fix-generic-constraints
Add support for notnull, struct, class and new() generic constraints
This commit is contained in:
@@ -137,26 +137,48 @@ namespace EntityFrameworkCore.Projectables.Generator
|
||||
descriptor.ClassTypeParameterList = descriptor.ClassTypeParameterList.AddParameters(
|
||||
SyntaxFactory.TypeParameter(additionalClassTypeParameter.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat))
|
||||
);
|
||||
|
||||
|
||||
// See https://github.com/dotnet/roslyn/blob/d7e010bbe5b1d37837417fc5e79ecb2fd9b7b487/src/VisualStudio/CSharp/Impl/ObjectBrowser/DescriptionBuilder.cs#L340
|
||||
if (!additionalClassTypeParameter.ConstraintTypes.IsDefaultOrEmpty)
|
||||
{
|
||||
descriptor.ClassConstraintClauses ??= SyntaxFactory.List<TypeParameterConstraintClauseSyntax>();
|
||||
|
||||
var parameters = new List<TypeConstraintSyntax>();
|
||||
|
||||
if (additionalClassTypeParameter.HasReferenceTypeConstraint)
|
||||
{
|
||||
parameters.Add(MakeTypeConstraint("class"));
|
||||
}
|
||||
|
||||
if (additionalClassTypeParameter.HasValueTypeConstraint)
|
||||
{
|
||||
parameters.Add(MakeTypeConstraint("struct"));
|
||||
}
|
||||
|
||||
if (additionalClassTypeParameter.HasNotNullConstraint)
|
||||
{
|
||||
parameters.Add(MakeTypeConstraint("notnull"));
|
||||
}
|
||||
|
||||
parameters.AddRange(additionalClassTypeParameter
|
||||
.ConstraintTypes
|
||||
.Select(c =>
|
||||
MakeTypeConstraint(c.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat))
|
||||
)
|
||||
);
|
||||
|
||||
if (additionalClassTypeParameter.HasConstructorConstraint)
|
||||
{
|
||||
parameters.Add(MakeTypeConstraint("new()"));
|
||||
}
|
||||
|
||||
descriptor.ClassConstraintClauses = descriptor.ClassConstraintClauses.Value.Add(
|
||||
SyntaxFactory.TypeParameterConstraintClause(
|
||||
SyntaxFactory.IdentifierName(additionalClassTypeParameter.Name),
|
||||
SyntaxFactory.SeparatedList<TypeParameterConstraintSyntax>(
|
||||
additionalClassTypeParameter
|
||||
.ConstraintTypes
|
||||
.Select(c => SyntaxFactory.TypeConstraint(
|
||||
SyntaxFactory.IdentifierName(c.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat))
|
||||
))
|
||||
)
|
||||
SyntaxFactory.SeparatedList<TypeParameterConstraintSyntax>(parameters)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// todo: add additional type constraints
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,5 +267,7 @@ namespace EntityFrameworkCore.Projectables.Generator
|
||||
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
private static TypeConstraintSyntax MakeTypeConstraint(string constraint) => SyntaxFactory.TypeConstraint(SyntaxFactory.IdentifierName(constraint));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,4 +111,4 @@ namespace EntityFrameworkCore.Projectables.Services
|
||||
return base.VisitMember(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// <auto-generated/>
|
||||
#nullable disable
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using EntityFrameworkCore.Projectables;
|
||||
using Foo;
|
||||
|
||||
namespace EntityFrameworkCore.Projectables.Generated
|
||||
{
|
||||
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
|
||||
static class Foo_Entity_FullName<T, TEnum>
|
||||
where T : global::Foo.TypedObject<TEnum> where TEnum : struct, global::System.Enum
|
||||
{
|
||||
static global::System.Linq.Expressions.Expression<global::System.Func<global::Foo.Entity<T, TEnum>, string>> Expression()
|
||||
{
|
||||
return (global::Foo.Entity<T, TEnum> @this) => $"{@this.FirstName} {@this.LastName} {@this.SomeSubobject.SomeProp}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// <auto-generated/>
|
||||
#nullable disable
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using EntityFrameworkCore.Projectables;
|
||||
using Foo;
|
||||
|
||||
namespace EntityFrameworkCore.Projectables.Generated
|
||||
{
|
||||
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
|
||||
static class Foo_Entity_FullName<T>
|
||||
{
|
||||
static global::System.Linq.Expressions.Expression<global::System.Func<global::Foo.Entity<T>, string>> Expression()
|
||||
{
|
||||
return (global::Foo.Entity<T> @this) => $"{@this.FirstName} {@this.LastName} {@this.SomeSubobject}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ namespace EntityFrameworkCore.Projectables.Generated
|
||||
{
|
||||
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
|
||||
static class _EntityBase_GetId<TId>
|
||||
where TId : global::System.ICloneable
|
||||
where TId : global::System.ICloneable, new()
|
||||
{
|
||||
static global::System.Linq.Expressions.Expression<global::System.Func<TId>> Expression()
|
||||
{
|
||||
|
||||
@@ -921,6 +921,72 @@ namespace Foo {
|
||||
return Verifier.Verify(result.GeneratedTrees[0].ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public Task GenericClassesWithContraints_AreRewritten()
|
||||
{
|
||||
var compilation = CreateCompilation(@"
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using EntityFrameworkCore.Projectables;
|
||||
|
||||
namespace Foo {
|
||||
public class TypedObject<TEnum> where TEnum : struct, System.Enum
|
||||
{
|
||||
public TEnum SomeProp { get; set; }
|
||||
}
|
||||
|
||||
public abstract class Entity<T, TEnum>where T : TypedObject<TEnum> where TEnum : struct, System.Enum
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string FirstName { get; set; }
|
||||
public string LastName { get; set; }
|
||||
public T SomeSubobject { get; set; }
|
||||
|
||||
[Projectable]
|
||||
public string FullName => $""{FirstName} {LastName} {SomeSubobject.SomeProp}"";
|
||||
}
|
||||
}
|
||||
");
|
||||
var result = RunGenerator(compilation);
|
||||
|
||||
Assert.Empty(result.Diagnostics);
|
||||
Assert.Single(result.GeneratedTrees);
|
||||
|
||||
return Verifier.Verify(result.GeneratedTrees[0].ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public Task GenericClassesWithTypeContraints_AreRewritten()
|
||||
{
|
||||
var compilation = CreateCompilation(@"
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using EntityFrameworkCore.Projectables;
|
||||
|
||||
namespace Foo {
|
||||
public abstract class Entity<T> where T : notnull
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string FirstName { get; set; }
|
||||
public string LastName { get; set; }
|
||||
public T SomeSubobject { get; set; }
|
||||
|
||||
[Projectable]
|
||||
public string FullName => $""{FirstName} {LastName} {SomeSubobject}"";
|
||||
}
|
||||
}
|
||||
");
|
||||
|
||||
var result = RunGenerator(compilation);
|
||||
|
||||
Assert.Empty(result.Diagnostics);
|
||||
// Assert.Single(result.GeneratedTrees);
|
||||
|
||||
return Verifier.Verify(result.GeneratedTrees[0].ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public Task DeclarationTypeNamesAreGettingFullyQualified()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user