Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,9 @@ public static PropertyInfo GetImplementingProperty(this Type derivedType, Proper

var derivedProperties = derivedType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

return derivedProperties.First(propertyInfo.GetMethod == accessor
return derivedProperties.FirstOrDefault(propertyInfo.GetMethod == accessor
? p => p.GetMethod == implementingAccessor
: p => p.SetMethod == implementingAccessor);
: p => p.SetMethod == implementingAccessor) ?? propertyInfo;
}

public static MethodInfo GetConcreteMethod(this Type derivedType, MethodInfo methodInfo)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SELECT [i].[Id]
FROM [Item] AS [i]
WHERE CONVERT(varchar(11), [i].[Id]) = N'123'
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
SELECT CONVERT(varchar(11), [i].[Id])
FROM [Item] AS [i]
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using EntityFrameworkCore.Projectables.FunctionalTests.Helpers;
using Microsoft.EntityFrameworkCore;
using VerifyXunit;
using Xunit;

#nullable disable

namespace EntityFrameworkCore.Projectables.FunctionalTests
{
[UsesVerify]
public class ExplicitInterfaceImplementationTests
{
public interface IStringId
{
string Id { get; }
}

public class Item : IStringId
{
public int Id { get; set; }

// Explicit interface implementation without [Projectable]
// This tests that GetImplementingProperty handles this scenario
string IStringId.Id => Id.ToString();

[Projectable]
public string FormattedId => Id.ToString();
}

[Fact]
public Task ProjectOverExplicitInterfaceImplementation()
{
using var dbContext = new SampleDbContext<Item>();

var query = dbContext.Set<Item>()
.Select(x => x.FormattedId);

return Verifier.Verify(query.ToQueryString());
}

[Fact]
public Task FilterOnExplicitInterfaceImplementation()
{
using var dbContext = new SampleDbContext<Item>();

var query = dbContext.Set<Item>()
.Where(x => x.FormattedId == "123");

return Verifier.Verify(query.ToQueryString());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// <auto-generated/>
#nullable disable
using System;
using EntityFrameworkCore.Projectables;
using Foo;

namespace EntityFrameworkCore.Projectables.Generated
{
[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]
static class Foo_Item_FormattedId
{
static global::System.Linq.Expressions.Expression<global::System.Func<global::Foo.Item, string>> Expression()
{
return (global::Foo.Item @this) => ((global::Foo.IStringId)@this).Id;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1902,6 +1902,40 @@ public Task GenericTypesWithConstraints()
return Verifier.Verify(result.GeneratedTrees[0].ToString());
}

[Fact]
public Task ExplicitInterfaceImplementation()
{
var compilation = CreateCompilation(@"
using System;
using EntityFrameworkCore.Projectables;

namespace Foo {
public interface IStringId
{
string Id { get; }
}

public class Item : IStringId
{
public int Id { get; set; }

// Explicit interface implementation without [Projectable]
string IStringId.Id => Id.ToString();

[Projectable]
public string FormattedId => ((IStringId)this).Id;
}
}
");

var result = RunGenerator(compilation);

Assert.Empty(result.Diagnostics);
Assert.Single(result.GeneratedTrees);

return Verifier.Verify(result.GeneratedTrees[0].ToString());
}

#region Helpers

Compilation CreateCompilation(string source, bool expectedToCompile = true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,17 @@ public override void VirtualMethod(int arg1) { }
public override void GenericVirtualMethod<TArg>(TArg arg1) { }
}

interface IStringId
{
string Id { get; }
}

class ItemWithExplicitInterfaceImplementation : IStringId
{
public int Id { get; set; }
string IStringId.Id => Id.ToString();
}

[Fact]
public void GetNestedTypePath_OuterType_Returns1Entry()
{
Expand Down Expand Up @@ -120,5 +131,41 @@ public void GetOverridingMethod_DerivedTypeGenericVirtualMethod_FindsOverridingM

Assert.Equal(derivedMethod, resolvedMethod);
}

[Fact]
public void GetImplementingProperty_ExplicitInterfaceImplementation_DoesNotThrow()
{
// This test verifies that when a class explicitly implements an interface property
// (e.g., string IStringId.Id => Id.ToString();), GetImplementingProperty doesn't throw
// an InvalidOperationException
var interfaceType = typeof(IStringId);
var interfaceProperty = interfaceType.GetProperty("Id")!;
var concreteType = typeof(ItemWithExplicitInterfaceImplementation);

// This should not throw InvalidOperationException
var result = concreteType.GetImplementingProperty(interfaceProperty);

// The result should be the explicit interface implementation property
Assert.NotNull(result);
Assert.NotEqual(interfaceProperty, result);
Assert.Contains("IStringId.Id", result.Name);
}

[Fact]
public void GetConcreteProperty_ExplicitInterfaceImplementation_DoesNotThrow()
{
// This test verifies the same scenario but through GetConcreteProperty which is the entry point
var interfaceType = typeof(IStringId);
var interfaceProperty = interfaceType.GetProperty("Id")!;
var concreteType = typeof(ItemWithExplicitInterfaceImplementation);

// This should not throw InvalidOperationException
var result = concreteType.GetConcreteProperty(interfaceProperty);

// The result should be the explicit interface implementation property
Assert.NotNull(result);
Assert.NotEqual(interfaceProperty, result);
Assert.Contains("IStringId.Id", result.Name);
}
}
}