Skip to content

Commit 0bf842b

Browse files
committed
Restructure AggregationBinder and ComputeBinder for extensibility (OData#1378)
1 parent 699f053 commit 0bf842b

File tree

52 files changed

+7606
-910
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+7606
-910
lines changed

src/Microsoft.AspNetCore.OData/Common/TypeHelper.cs

+203-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using System;
99
using System.Collections;
1010
using System.Collections.Generic;
11+
using System.Diagnostics;
1112
using System.Diagnostics.CodeAnalysis;
1213
using System.Diagnostics.Contracts;
1314
using System.Linq;
@@ -40,7 +41,141 @@ public static bool IsDynamicTypeWrapper(this Type type)
4041

4142
public static bool IsSelectExpandWrapper(this Type type, out Type entityType) => IsTypeWrapper(typeof(SelectExpandWrapper<>), type, out entityType);
4243

43-
public static bool IsComputeWrapper(this Type type, out Type entityType) => IsTypeWrapper(typeof(ComputeWrapper<>), type, out entityType);
44+
/// <summary>
45+
/// Determines whether the specified type is a <see cref="ComputeWrapper{T}"/> or a custom implementation
46+
/// that inherits from <see cref="DynamicTypeWrapper"/> and implements both <see cref="IComputeWrapper{T}"/>
47+
/// and <see cref="IGroupByWrapper{TContainer, TWrapper}"/>.
48+
/// </summary>
49+
/// <param name="typeToCheck">The type to check.</param>
50+
/// <param name="entityType">The entity type if the specified type is a <see cref="ComputeWrapper{T}"/> or a custom implementation; otherwise, null.</param>
51+
/// <returns>
52+
/// <c>true</c> if the specified type is a <see cref="ComputeWrapper{T}"/> or a custom implementation
53+
/// that meets the criteria; otherwise, <c>false</c>.
54+
/// </returns>
55+
public static bool IsComputeWrapper(this Type typeToCheck, out Type entityType)
56+
{
57+
entityType = null;
58+
if (typeToCheck == null)
59+
{
60+
return false;
61+
}
62+
63+
bool isComputeWrapper = false;
64+
65+
if (typeToCheck.IsGenericType)
66+
{
67+
Type genericTypeDefinition = typeToCheck.GetGenericTypeDefinition();
68+
69+
// Default implementation
70+
if (genericTypeDefinition == typeof(ComputeWrapper<>))
71+
{
72+
Debug.Assert(
73+
typeof(DynamicTypeWrapper).IsAssignableFrom(genericTypeDefinition)
74+
&& genericTypeDefinition.ImplementsInterface(typeof(IComputeWrapper<>))
75+
&& genericTypeDefinition.ImplementsInterface(typeof(IGroupByWrapper<,>)),
76+
"ComputeWrapper<T> must inherit from DynamicTypeWrapper and implement IComputeWrapper<T> and IGroupByWrapper<TContainer, TWrapper>");
77+
78+
isComputeWrapper = true;
79+
}
80+
// Custom implementation
81+
// Must inherit from DynamicTypeWrapper
82+
// Must implement IComputeWrapper<T> and IGroupByWrapper<TContainer, TWrapper>
83+
else if (typeof(DynamicTypeWrapper).IsAssignableFrom(genericTypeDefinition)
84+
&& genericTypeDefinition.ImplementsInterface(typeof(IComputeWrapper<>))
85+
&& genericTypeDefinition.ImplementsInterface(typeof(IGroupByWrapper<,>)))
86+
{
87+
isComputeWrapper = true;
88+
}
89+
90+
if (isComputeWrapper)
91+
{
92+
entityType = typeToCheck.GetGenericArguments()[0];
93+
}
94+
}
95+
96+
return isComputeWrapper;
97+
}
98+
99+
/// <summary>
100+
/// Determines whether the specified type is a <see cref="FlatteningWrapper{T}"/> or a custom implementation
101+
/// that inherits from <see cref="DynamicTypeWrapper"/> and implements both <see cref="IFlatteningWrapper{T}"/>
102+
/// and <see cref="IGroupByWrapper{TContainer, TWrapper}"/>.
103+
/// </summary>
104+
/// <param name="typeToCheck">The type to check.</param>
105+
/// <returns>
106+
/// <c>true</c> if the specified type is a <see cref="FlatteningWrapper{T}"/> or a custom implementation
107+
/// that meets the criteria; otherwise, <c>false</c>.
108+
/// </returns>
109+
public static bool IsFlatteningWrapper(this Type typeToCheck)
110+
{
111+
if (typeToCheck == null)
112+
{
113+
return false;
114+
}
115+
116+
if (typeToCheck.IsGenericType)
117+
{
118+
Type genericTypeDefinition = typeToCheck.GetGenericTypeDefinition();
119+
120+
Func<bool> isFlatteningWrapperFunc = () => typeof(DynamicTypeWrapper).IsAssignableFrom(genericTypeDefinition)
121+
&& genericTypeDefinition.ImplementsInterface(typeof(IFlatteningWrapper<>))
122+
&& genericTypeDefinition.ImplementsInterface(typeof(IGroupByWrapper<,>));
123+
// Default implementation
124+
if (genericTypeDefinition == typeof(FlatteningWrapper<>))
125+
{
126+
Debug.Assert(
127+
typeof(DynamicTypeWrapper).IsAssignableFrom(genericTypeDefinition)
128+
&& genericTypeDefinition.ImplementsInterface(typeof(IFlatteningWrapper<>))
129+
&& genericTypeDefinition.ImplementsInterface(typeof(IGroupByWrapper<,>)),
130+
"FlatteningWrapper<T> must inherit from DynamicTypeWrapper and implement IFlatteningWrapper<T> and IGroupByWrapper<TContainer, TWrapper>");
131+
132+
return true;
133+
}
134+
135+
// Custom implementation
136+
// Must inherit from DynamicTypeWrapper
137+
// Must implement IFlatteningWrapper<T> and IGroupByWrapper<TContainer, TWrapper>
138+
return typeof(DynamicTypeWrapper).IsAssignableFrom(genericTypeDefinition)
139+
&& genericTypeDefinition.ImplementsInterface(typeof(IFlatteningWrapper<>))
140+
&& genericTypeDefinition.ImplementsInterface(typeof(IGroupByWrapper<,>));
141+
}
142+
143+
return false;
144+
}
145+
146+
/// <summary>
147+
/// Determines whether the specified type is a <see cref="GroupByWrapper"/> or a custom implementation
148+
/// that inherits from <see cref="DynamicTypeWrapper"/> and implements <see cref="IGroupByWrapper{TContainer, TWrapper}"/>.
149+
/// </summary>
150+
/// <param name="typeToCheck">The type to check.</param>
151+
/// <returns>
152+
/// <c>true</c> if the specified type is a <see cref="GroupByWrapper"/> or a custom implementation
153+
/// that meets the criteria; otherwise, <c>false</c>.
154+
/// </returns>
155+
public static bool IsGroupByWrapper(this Type typeToCheck)
156+
{
157+
if (typeToCheck == null || typeToCheck.IsValueType || typeToCheck == typeof(string))
158+
{
159+
return false;
160+
}
161+
162+
// Default implementation
163+
if (typeof(GroupByWrapper).IsAssignableFrom(typeToCheck))
164+
{
165+
Debug.Assert(
166+
typeof(DynamicTypeWrapper).IsAssignableFrom(typeToCheck)
167+
&& typeToCheck.ImplementsInterface(typeof(IGroupByWrapper<,>)),
168+
"GroupByWrapper must inherit from DynamicTypeWrapper and implement IGroupByWrapper<TContainer, TWrapper>");
169+
170+
return true;
171+
}
172+
173+
// Custom implementation
174+
// Must inherit from DynamicTypeWrapper
175+
// Must implement IGroupByWrapper<TContainer, TWrapper>
176+
return typeof(DynamicTypeWrapper).IsAssignableFrom(typeToCheck) &&
177+
typeToCheck.ImplementsInterface(typeof(IGroupByWrapper<,>));
178+
}
44179

45180
private static bool IsTypeWrapper(Type wrappedType, Type type, out Type entityType)
46181
{
@@ -431,4 +566,71 @@ private static Type GetInnerGenericType(Type interfaceType)
431566

432567
return null;
433568
}
569+
570+
/// <summary>
571+
/// Determines whether the specified type inherits from a given generic base type.
572+
/// </summary>
573+
/// <param name="typeToCheck">The type to examine.</param>
574+
/// <param name="genericBaseType">The open generic type definition to check against (e.g., typeof(Base&lt;&gt;)).</param>
575+
/// <returns><c>true</c> if <paramref name="typeToCheck"/> inherits from <paramref name="genericBaseType"/>; otherwise, <c>false</c>.</returns>
576+
public static bool InheritsFromGenericBase(this Type typeToCheck, Type genericBaseType)
577+
{
578+
if (typeToCheck == null || genericBaseType == null || !genericBaseType.IsGenericTypeDefinition)
579+
return false;
580+
581+
Type baseType = typeToCheck.BaseType;
582+
583+
while (baseType != null && baseType != typeof(object))
584+
{
585+
if (baseType.IsGenericType && baseType.GetGenericTypeDefinition() == genericBaseType)
586+
{
587+
return true;
588+
}
589+
590+
baseType = baseType.BaseType;
591+
}
592+
593+
return false;
594+
}
595+
596+
/// <summary>
597+
/// Determines whether the specified target type implements the given interface type.
598+
/// </summary>
599+
/// <param name="targetType">The type to check for implementation of the interface.</param>
600+
/// <param name="interfaceType">The interface type to check against.</param>
601+
/// <returns>
602+
/// <c>true</c> if the target type implements the specified interface type; otherwise, <c>false</c>.
603+
/// </returns>
604+
/// <remarks>
605+
/// This method supports both generic and non-generic interfaces. For generic interfaces, it checks if the target type
606+
/// implements any interface that matches the generic type definition of the specified interface type.
607+
/// </remarks>
608+
public static bool ImplementsInterface(this Type targetType, Type interfaceType)
609+
{
610+
if (targetType == null || interfaceType == null)
611+
{
612+
return false;
613+
}
614+
615+
if (interfaceType.IsGenericTypeDefinition) // Generic interface (e.g., I<>)
616+
{
617+
Type[] implementedInterfaces = targetType.GetInterfaces();
618+
for (int i = 0; i < implementedInterfaces.Length; i++)
619+
{
620+
Type implementedInterface = implementedInterfaces[i];
621+
622+
if (implementedInterface.IsGenericType &&
623+
implementedInterface.GetGenericTypeDefinition() == interfaceType)
624+
{
625+
return true;
626+
}
627+
}
628+
}
629+
else // Non-generic interface
630+
{
631+
return interfaceType.IsAssignableFrom(targetType);
632+
}
633+
634+
return false;
635+
}
434636
}

0 commit comments

Comments
 (0)