mirror of
https://github.com/zoriya/EntityFrameworkCore.Projectables.git
synced 2025-12-06 05:56:10 +00:00
Merge pull request #23 from koenbeuk/issue-21
Support for const and static in projectables
This commit is contained in:
@@ -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)",
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
SELECT [e].[Id], [e].[Price]
|
||||
FROM [Entity] AS [e]
|
||||
WHERE [e].[Price] > 1.0E0
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user