Merge pull request #23 from koenbeuk/issue-21

Support for const and static in projectables
This commit is contained in:
Koen
2022-04-21 00:02:00 +08:00
committed by GitHub
16 changed files with 375 additions and 9 deletions

View File

@@ -9,7 +9,7 @@ namespace EntityFrameworkCore.Projectables.Generator
{
public static class Diagnostics
{
public static readonly DiagnosticDescriptor RequiresExpressionBodyDefinition = new(
public static readonly DiagnosticDescriptor RequiresExpressionBodyDefinition = new DiagnosticDescriptor(
id: "EFP0001",
title: "Method or property should expose an expression body definition",
messageFormat: "Method or property '{0}' should expose an expression body definition",
@@ -17,7 +17,7 @@ namespace EntityFrameworkCore.Projectables.Generator
DiagnosticSeverity.Error,
isEnabledByDefault: true);
public static readonly DiagnosticDescriptor NullConditionalRewriteUnsupported = new(
public static readonly DiagnosticDescriptor NullConditionalRewriteUnsupported = new DiagnosticDescriptor(
id: "EFP0002",
title: "Method or property is not configured to support null-conditional expressions",
messageFormat: "'{0}' has a null-conditional expression exposed but is not configured to rewrite this (Consider configuring a strategy using the NullConditionalRewriteSupport property on the Projectable attribute)",

View File

@@ -122,15 +122,17 @@ namespace EntityFrameworkCore.Projectables.Generator
public override SyntaxNode? VisitIdentifierName(IdentifierNameSyntax node)
{
var symbolInfo = _semanticModel.GetSymbolInfo(node);
var symbol = _semanticModel.GetSymbolInfo(node).Symbol;
if (symbolInfo.Symbol is not null)
if (symbol is not null)
{
if (symbolInfo.Symbol is IMethodSymbol methodSymbol && methodSymbol.IsExtensionMethod)
if (symbol is IMethodSymbol methodSymbol && methodSymbol.IsExtensionMethod)
{
// Ignore extension methods
}
else if (symbolInfo.Symbol.Kind is SymbolKind.Property or SymbolKind.Method or SymbolKind.Field)
else if (symbol.Kind is SymbolKind.Property or SymbolKind.Method or SymbolKind.Field)
{
// We may need to rewrite this expression such that it refers to our @this argument
bool rewrite = true;
if (node.Parent is MemberAccessExpressionSyntax parentMemberAccessNode)
@@ -147,7 +149,7 @@ namespace EntityFrameworkCore.Projectables.Generator
else if (targetSymbolInfo.Symbol?.ContainingType is not null)
{
if (!_compilation.HasImplicitConversion(targetSymbolInfo.Symbol.ContainingType, _targetTypeSymbol) ||
!SymbolEqualityComparer.Default.Equals(symbolInfo.Symbol.ContainingType, _targetTypeSymbol))
!SymbolEqualityComparer.Default.Equals(symbol.ContainingType, _targetTypeSymbol))
{
rewrite = false;
}
@@ -164,13 +166,18 @@ namespace EntityFrameworkCore.Projectables.Generator
if (rewrite)
{
var expressionSyntax = symbol.IsStatic
? SyntaxFactory.ParseTypeName(_targetTypeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat))
: SyntaxFactory.IdentifierName("@this");
return SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
SyntaxFactory.IdentifierName("@this"),
expressionSyntax,
node
);
}
}
else if (symbolInfo.Symbol.Kind is SymbolKind.NamedType && node.Parent?.Kind() is not SyntaxKind.QualifiedName)
else if (symbol.Kind is SymbolKind.NamedType && node.Parent?.Kind() is not SyntaxKind.QualifiedName)
{
var typeInfo = _semanticModel.GetTypeInfo(node);

View File

@@ -0,0 +1,3 @@
SELECT [e].[Id], [e].[Price]
FROM [Entity] AS [e]
WHERE [e].[Price] > 1.0E0

View File

@@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Data.SqlTypes;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EntityFrameworkCore.Projectables.Extensions;
using EntityFrameworkCore.Projectables.FunctionalTests.Helpers;
using Microsoft.EntityFrameworkCore;
using ScenarioTests;
using VerifyXunit;
using Xunit;
namespace EntityFrameworkCore.Projectables.FunctionalTests
{
[UsesVerify]
public class StaticMembersTests
{
public static class Constants
{
public static readonly double ExpensiveThreshold = 1;
}
public record Entity
{
public int Id { get; set; }
public double Price { get; set; }
[Projectable]
public bool IsExpensive => Price > Constants.ExpensiveThreshold;
}
[Fact]
public Task FilterOnProjectableProperty()
{
using var dbContext = new SampleDbContext<Entity>(Infrastructure.CompatibilityMode.Full);
var query = dbContext.Set<Entity>()
.Where(x => x.IsExpensive);
return Verifier.Verify(query.ToQueryString());
}
}
}

View File

@@ -0,0 +1,18 @@
using System;
using System.Linq;
using System.Collections.Generic;
using EntityFrameworkCore.Projectables;
using Foo;
namespace EntityFrameworkCore.Projectables.Generated
#nullable disable
{
public static class Foo_Foo_IdWithBar
{
public static System.Linq.Expressions.Expression<System.Func<global::Foo.Foo, int>> Expression()
{
return (global::Foo.Foo @this) =>
@this.Id + global::Foo.Foo.Bar;
}
}
}

View File

@@ -0,0 +1,18 @@
using System;
using System.Linq;
using System.Collections.Generic;
using EntityFrameworkCore.Projectables;
using Foo;
namespace EntityFrameworkCore.Projectables.Generated
#nullable disable
{
public static class Foo_Foo_IdWithBar
{
public static System.Linq.Expressions.Expression<System.Func<global::Foo.Foo, int>> Expression()
{
return (global::Foo.Foo @this) =>
@this.Id + global::Foo.Constants.Bar;
}
}
}

View File

@@ -0,0 +1,18 @@
using System;
using System.Linq;
using System.Collections.Generic;
using EntityFrameworkCore.Projectables;
using Foo;
namespace EntityFrameworkCore.Projectables.Generated
#nullable disable
{
public static class Foo_Foo_IdWithBar
{
public static System.Linq.Expressions.Expression<System.Func<global::Foo.Foo, int>> Expression()
{
return (global::Foo.Foo @this) =>
@this.Id + global::Foo.Foo.Bar;
}
}
}

View File

@@ -0,0 +1,18 @@
using System;
using System.Linq;
using System.Collections.Generic;
using EntityFrameworkCore.Projectables;
using Foo;
namespace EntityFrameworkCore.Projectables.Generated
#nullable disable
{
public static class Foo_Foo_IdWithBar
{
public static System.Linq.Expressions.Expression<System.Func<global::Foo.Foo, int>> Expression()
{
return (global::Foo.Foo @this) =>
@this.Id + @this.Bar;
}
}
}

View File

@@ -0,0 +1,18 @@
using System;
using System.Linq;
using System.Collections.Generic;
using EntityFrameworkCore.Projectables;
using Foo;
namespace EntityFrameworkCore.Projectables.Generated
#nullable disable
{
public static class Foo_Foo_IdWithBar
{
public static System.Linq.Expressions.Expression<System.Func<global::Foo.Foo, bool>> Expression()
{
return (global::Foo.Foo @this) =>
@this.Id > global::Foo.Constants.Bar;
}
}
}

View File

@@ -0,0 +1,18 @@
using System;
using System.Linq;
using System.Collections.Generic;
using EntityFrameworkCore.Projectables;
using Foo;
namespace EntityFrameworkCore.Projectables.Generated
#nullable disable
{
public static class Foo_Foo_IdWithBar
{
public static System.Linq.Expressions.Expression<System.Func<global::Foo.Foo, int>> Expression()
{
return (global::Foo.Foo @this) =>
@this.Id + global::Foo.Constants.Bar;
}
}
}

View File

@@ -0,0 +1,18 @@
using System;
using System.Linq;
using System.Collections.Generic;
using EntityFrameworkCore.Projectables;
using Foo;
namespace EntityFrameworkCore.Projectables.Generated
#nullable disable
{
public static class Foo_Foo_IdWithBar
{
public static System.Linq.Expressions.Expression<System.Func<global::Foo.Foo, int>> Expression()
{
return (global::Foo.Foo @this) =>
@this.Id + @this.Bar;
}
}
}

View File

@@ -0,0 +1,18 @@
using System;
using System.Linq;
using System.Collections.Generic;
using EntityFrameworkCore.Projectables;
using Foo;
namespace EntityFrameworkCore.Projectables.Generated
#nullable disable
{
public static class Foo_Foo_IdWithBar
{
public static System.Linq.Expressions.Expression<System.Func<global::Foo.Foo, int>> Expression()
{
return (global::Foo.Foo @this) =>
@this.Id + global::Foo.Foo.Bar;
}
}
}

View File

@@ -0,0 +1,18 @@
using System;
using System.Linq;
using System.Collections.Generic;
using EntityFrameworkCore.Projectables;
using Foo;
namespace EntityFrameworkCore.Projectables.Generated
#nullable disable
{
public static class Foo_Foo_IdWithBar
{
public static System.Linq.Expressions.Expression<System.Func<global::Foo.Foo, int>> Expression()
{
return (global::Foo.Foo @this) =>
@this.Id + global::Foo.Constants.Bar;
}
}
}

View File

@@ -988,6 +988,155 @@ namespace Foo {
return Verifier.Verify(result.GeneratedTrees[0].ToString());
}
[Fact]
public Task StaticMembers()
{
var compilation = CreateCompilation(@"
using System;
using System.Linq;
using System.Collections.Generic;
using EntityFrameworkCore.Projectables;
namespace Foo {
public class Foo {
public static int Bar { get; set; }
public int Id { get; set; }
[Projectable]
public int IdWithBar() => Id + Bar;
}
}
");
var result = RunGenerator(compilation);
Assert.Empty(result.Diagnostics);
Assert.Single(result.GeneratedTrees);
return Verifier.Verify(result.GeneratedTrees[0].ToString());
}
[Fact]
public Task StaticMembers2()
{
var compilation = CreateCompilation(@"
using System;
using System.Linq;
using System.Collections.Generic;
using EntityFrameworkCore.Projectables;
namespace Foo {
public static class Constants {
public static readonly int Bar = 1;
}
public class Foo {
public int Id { get; set; }
[Projectable]
public int IdWithBar() => Id + Constants.Bar;
}
}
");
var result = RunGenerator(compilation);
Assert.Empty(result.Diagnostics);
Assert.Single(result.GeneratedTrees);
return Verifier.Verify(result.GeneratedTrees[0].ToString());
}
[Fact]
public Task ConstMember()
{
var compilation = CreateCompilation(@"
using System;
using System.Linq;
using System.Collections.Generic;
using EntityFrameworkCore.Projectables;
namespace Foo {
public class Foo {
public const int Bar = 1;
public int Id { get; set; }
[Projectable]
public int IdWithBar() => Id + Bar;
}
}
");
var result = RunGenerator(compilation);
Assert.Empty(result.Diagnostics);
Assert.Single(result.GeneratedTrees);
return Verifier.Verify(result.GeneratedTrees[0].ToString());
}
[Fact]
public Task ConstMember2()
{
var compilation = CreateCompilation(@"
using System;
using System.Linq;
using System.Collections.Generic;
using EntityFrameworkCore.Projectables;
namespace Foo {
public static class Constants {
public const int Bar = 1;
}
public class Foo {
public int Id { get; set; }
[Projectable]
public int IdWithBar() => Id + Constants.Bar;
}
}
");
var result = RunGenerator(compilation);
Assert.Empty(result.Diagnostics);
Assert.Single(result.GeneratedTrees);
return Verifier.Verify(result.GeneratedTrees[0].ToString());
}
[Fact]
public Task ConstMember3()
{
var compilation = CreateCompilation(@"
using System;
using System.Linq;
using System.Collections.Generic;
using EntityFrameworkCore.Projectables;
namespace Foo {
public class Foo {
public const int Bar = 1;
public int Id { get; set; }
[Projectable]
public int IdWithBar() => Id + Foo.Bar;
}
}
");
var result = RunGenerator(compilation);
Assert.Empty(result.Diagnostics);
Assert.Single(result.GeneratedTrees);
return Verifier.Verify(result.GeneratedTrees[0].ToString());
}
[Fact]
public Task RelationalProperty()
{