Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Core deferred binding feature #1676

Merged
merged 7 commits into from
Jul 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions DotNetWorker.sln
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Worker.Extensions.Http.AspN
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Worker.Extensions.SignalRService.Tests", "test\Worker.Extensions.SignalRService.Tests\Worker.Extensions.SignalRService.Tests.csproj", "{286F9EE3-00AE-4EFA-BFD8-A2E58BC809D2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Worker.Extensions.Tests", "test\Worker.Extensions.Tests\Worker.Extensions.Tests.csproj", "{17BDCE12-6964-4B87-B2AC-68CE270A3E9A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -320,6 +322,10 @@ Global
{286F9EE3-00AE-4EFA-BFD8-A2E58BC809D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{286F9EE3-00AE-4EFA-BFD8-A2E58BC809D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{286F9EE3-00AE-4EFA-BFD8-A2E58BC809D2}.Release|Any CPU.Build.0 = Release|Any CPU
{17BDCE12-6964-4B87-B2AC-68CE270A3E9A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{17BDCE12-6964-4B87-B2AC-68CE270A3E9A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{17BDCE12-6964-4B87-B2AC-68CE270A3E9A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{17BDCE12-6964-4B87-B2AC-68CE270A3E9A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -376,6 +382,7 @@ Global
{B13C9E5F-0E4B-413E-90AE-20B84B100364} = {AA4D318D-101B-49E7-A4EC-B34165AEDB33}
{1F6B7CF6-0CC8-4C7F-825F-74B0BEC1CF0A} = {A7B4FF1E-3DF7-4F28-9333-D0961CDDF702}
{286F9EE3-00AE-4EFA-BFD8-A2E58BC809D2} = {FD7243E4-BF18-43F8-8744-BA1D17ACF378}
{17BDCE12-6964-4B87-B2AC-68CE270A3E9A} = {FD7243E4-BF18-43F8-8744-BA1D17ACF378}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {497D2ED4-A13E-4BCA-8D29-F30CA7D0EA4A}
Expand Down
4 changes: 2 additions & 2 deletions extensions/Worker.Extensions.Abstractions/release_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
- My change description (#PR/#issue)
-->

### Microsoft.Azure.Functions.Worker.Extensions.Abstractions <version>
### Microsoft.Azure.Functions.Worker.Extensions.Abstractions 1.3.0

- <event>
- Add SupportsDeferredBinding attribute
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using System;

namespace Microsoft.Azure.Functions.Worker.Extensions.Abstractions
{
/// <summary>
/// Specifies if a converter supports deferred binding when generating function metadata.
/// This is to be used on converters that support deferred binding.
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class SupportsDeferredBindingAttribute : Attribute
{
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<AssemblyName>Microsoft.Azure.Functions.Worker.Extensions.Abstractions</AssemblyName>
<RootNamespace>Microsoft.Azure.Functions.Worker.Extensions.Abstractions</RootNamespace>
<Description>Abstractions for Azure Functions .NET Worker</Description>
<MinorProductVersion>2</MinorProductVersion>
<MinorProductVersion>3</MinorProductVersion>
<PatchProductVersion>0</PatchProductVersion>
</PropertyGroup>

Expand Down
12 changes: 7 additions & 5 deletions release_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@
- My change description (#PR/#issue)
-->

### Microsoft.Azure.Functions.Worker (metapackage) <version>
### Microsoft.Azure.Functions.Worker (metapackage) 1.18.0

- <entry>

### Microsoft.Azure.Functions.Worker.Core <version>
### Microsoft.Azure.Functions.Worker.Core 1.14.0

- Unsealed `InputConverterAttribute`. Implementers can now derive from this type to map attributes to custom converters.
- Unsealed `InputConverterAttribute`. Implementers can now derive from this type to map attributes to custom converters. (#1712)
- Add support for deferred binding (#1676)
- Add binding attribute to converter context (#1660)

### Microsoft.Azure.Functions.Worker.Grpc <version>
### Microsoft.Azure.Functions.Worker.Grpc 1.12.0

- bumping grpc packages (#1719)
- Add support for deferred binding (#1676)
5 changes: 4 additions & 1 deletion sdk/Sdk.Generators/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,14 @@ internal static class Types
internal const string RetryAttribute = "Microsoft.Azure.Functions.Worker.RetryAttribute";
internal const string FixedDelayRetryAttribute = "Microsoft.Azure.Functions.Worker.FixedDelayRetryAttribute";
internal const string ExponentialBackoffRetryAttribute = "Microsoft.Azure.Functions.Worker.ExponentialBackoffRetryAttribute";
internal const string InputConverterAttributeType = "Microsoft.Azure.Functions.Worker.Converters.InputConverterAttribute";
internal const string SupportedTargetTypeAttributeType = "Microsoft.Azure.Functions.Worker.Converters.SupportedTargetTypeAttribute";
internal const string SupportsDeferredBindingAttributeType = "Microsoft.Azure.Functions.Worker.Extensions.Abstractions.SupportsDeferredBindingAttribute";

// System types
internal const string IEnumerableOfKeyValuePair = "System.Collections.Generic.IEnumerable`1<System.Collections.Generic.KeyValuePair`2<TKey,TValue>>";
}

internal static class FunctionMetadataBindingProps {
internal const string ReturnBindingName = "$return";
internal const string HttpResponseBindingName = "HttpResponse";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace Microsoft.Azure.Functions.Worker.Sdk.Generators
Expand Down Expand Up @@ -279,6 +279,12 @@ private bool TryGetParameterInputAndTriggerBindings(MethodDeclarationSyntax meth
DataType dataType = _dataTypeParser.GetDataType(parameterSymbol.Type);

bool cardinalityValidated = false;
bool supportsDeferredBinding = false;

if (SupportsDeferredBinding(attribute, parameterSymbol.Type.ToString()))
{
supportsDeferredBinding = true;
}

if (_cardinalityParser.IsCardinalitySupported(attribute))
{
Expand All @@ -300,7 +306,7 @@ private bool TryGetParameterInputAndTriggerBindings(MethodDeclarationSyntax meth

string bindingName = parameter.Identifier.ValueText;

if (!TryCreateBindingDict(attribute, bindingName, parameter.Identifier.GetLocation(), out IDictionary<string, object>? bindingDict))
if (!TryCreateBindingDict(attribute, bindingName, parameter.Identifier.GetLocation(), out IDictionary<string, object>? bindingDict, supportsDeferredBinding))
{
bindingsList = null;
return false;
Expand Down Expand Up @@ -338,6 +344,77 @@ private bool TryGetParameterInputAndTriggerBindings(MethodDeclarationSyntax meth
return true;
}

private bool SupportsDeferredBinding(AttributeData bindingAttribute, string bindingType)
{
var advertisedAttributes = bindingAttribute?.AttributeClass?.GetAttributes();

if (advertisedAttributes != null)
{
foreach (var advertisedAttribute in advertisedAttributes)
{
if (SymbolEqualityComparer.Default.Equals(advertisedAttribute.AttributeClass, _knownFunctionMetadataTypes.InputConverterAttributeType))
{
foreach (var converter in advertisedAttribute.ConstructorArguments)
{
if (DoesConverterSupportDeferredBinding(converter, bindingType))
{
return true;
}
}
}
}
}
return false;
}

private bool DoesConverterSupportDeferredBinding(TypedConstant converter, string bindingType)
{
var converterType = converter.Value as ITypeSymbol;
var converterAdvertisedAttributes = converterType?.GetAttributes().ToList();

if (converterAdvertisedAttributes is not null)
{
bool converterAdvertisesDeferredBindingSupport = converterAdvertisedAttributes.Any(
a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, _knownFunctionMetadataTypes.SupportsDeferredBindingAttributeType));

if (converterAdvertisesDeferredBindingSupport)
{
bool converterAdvertisesTypes = converterAdvertisedAttributes.Any(
a => SymbolEqualityComparer.Default.Equals(a.AttributeClass, _knownFunctionMetadataTypes.SupportedTargetTypeAttributeType));

if (!converterAdvertisesTypes)
{
// If a converter advertises deferred binding but does not explictly advertise any types then DeferredBinding will be supported for all the types
return true;
}

return DoesConverterSupportTargetType(converterAdvertisedAttributes, bindingType);
}
}

return false;
}

private bool DoesConverterSupportTargetType(List<AttributeData> converterAdvertisedAttributes, string bindingType)
{
foreach (AttributeData attribute in converterAdvertisedAttributes)
{
if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, _knownFunctionMetadataTypes.SupportedTargetTypeAttributeType))
{
foreach (var element in attribute.ConstructorArguments)
{
if (string.Equals(element.Type?.GetFullName(), typeof(Type).FullName, StringComparison.Ordinal)
&& string.Equals(element.Value?.ToString(), bindingType, StringComparison.Ordinal))
{
return true;
}
}
}
}

return false;
}

/// <summary>
/// Checks for and returns any bindings found in the Return Type of the method
/// </summary>
Expand Down Expand Up @@ -475,7 +552,7 @@ private bool TryGetReturnTypePropertyBindings(ITypeSymbol returnTypeSymbol, bool
return httpBinding;
}

private bool TryCreateBindingDict(AttributeData bindingAttrData, string bindingName, Location? bindingLocation, out IDictionary<string, object>? bindings)
private bool TryCreateBindingDict(AttributeData bindingAttrData, string bindingName, Location? bindingLocation, out IDictionary<string, object>? bindings, bool supportsDeferredBinding = false)
{
// Get binding info as a dictionary with keys as the property name and value as the property value
if (!TryGetAttributeProperties(bindingAttrData, bindingLocation, out IDictionary<string, object?>? attributeProperties))
Expand All @@ -501,6 +578,11 @@ private bool TryCreateBindingDict(AttributeData bindingAttrData, string bindingN
{ "direction", bindingDirection }
};

if (supportsDeferredBinding)
{
bindings.Add("properties", new Dictionary<string, string>() { { "SupportsDeferredBinding", "True" } });
}

// Add additional bindingInfo to the anonymous type because some functions have more properties than others
foreach (var prop in attributeProperties!) // attributeProperties won't be null here b/c we would've exited this method earlier if it was during TryGetAttributeProperties check
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ namespace Microsoft.Azure.Functions.Worker.Sdk.Generators
private readonly Lazy<INamedTypeSymbol?> _bindingCapabilitiesAttribute;
private readonly Lazy<INamedTypeSymbol?> _fixedDelayRetryAttribute;
private readonly Lazy<INamedTypeSymbol?> _exponentialBackoffRetryAttribute;
private readonly Lazy<INamedTypeSymbol?> _inputConverterAttributeType;
private readonly Lazy<INamedTypeSymbol?> _supportedTargetTypeAttributeType;
private readonly Lazy<INamedTypeSymbol?> _supportsDeferredBindingAttributeType;

internal KnownFunctionMetadataTypes(Compilation compilation)
{
Expand All @@ -34,7 +37,9 @@ internal KnownFunctionMetadataTypes(Compilation compilation)
_bindingCapabilitiesAttribute = new Lazy<INamedTypeSymbol?>(() => compilation.GetTypeByMetadataName(Constants.Types.BindingCapabilitiesAttribute));
_fixedDelayRetryAttribute = new Lazy<INamedTypeSymbol?>(() => compilation.GetTypeByMetadataName(Constants.Types.FixedDelayRetryAttribute));
_exponentialBackoffRetryAttribute = new Lazy<INamedTypeSymbol?>(() => compilation.GetTypeByMetadataName(Constants.Types.ExponentialBackoffRetryAttribute));

_inputConverterAttributeType = new Lazy<INamedTypeSymbol?>(() => compilation.GetTypeByMetadataName(Constants.Types.InputConverterAttributeType));
_supportedTargetTypeAttributeType = new Lazy<INamedTypeSymbol?>(() => compilation.GetTypeByMetadataName(Constants.Types.SupportedTargetTypeAttributeType));
_supportsDeferredBindingAttributeType = new Lazy<INamedTypeSymbol?>(() => compilation.GetTypeByMetadataName(Constants.Types.SupportsDeferredBindingAttributeType));
}

public INamedTypeSymbol? BindingAttribute { get => _bindingAttribute.Value; }
Expand All @@ -58,5 +63,11 @@ internal KnownFunctionMetadataTypes(Compilation compilation)
public INamedTypeSymbol? FixedDelayRetryAttribute { get => _fixedDelayRetryAttribute.Value; }

public INamedTypeSymbol? ExponentialBackoffRetryAttribute { get => _exponentialBackoffRetryAttribute.Value; }

public INamedTypeSymbol? InputConverterAttributeType { get => _inputConverterAttributeType.Value; }

public INamedTypeSymbol? SupportedTargetTypeAttributeType { get => _supportedTargetTypeAttributeType.Value; }

public INamedTypeSymbol? SupportsDeferredBindingAttributeType { get => _supportsDeferredBindingAttributeType.Value; }
}
}
2 changes: 2 additions & 0 deletions sdk/Sdk/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ internal static class Constants
internal const string ExponentialBackoffRetryAttributeType = "Microsoft.Azure.Functions.Worker.ExponentialBackoffRetryAttribute";
internal const string BindingPropertyNameAttributeType = "Microsoft.Azure.Functions.Worker.Extensions.Abstractions.BindingPropertyNameAttribute";
internal const string SupportsDeferredBindingAttributeType = "Microsoft.Azure.Functions.Worker.Extensions.Abstractions.SupportsDeferredBindingAttribute";
internal const string InputConverterAttributeType = "Microsoft.Azure.Functions.Worker.Converters.InputConverterAttribute";
internal const string SupportedTargetTypeAttributeType = "Microsoft.Azure.Functions.Worker.Converters.SupportedTargetTypeAttribute";

// System types
internal const string IEnumerableType = "System.Collections.IEnumerable";
Expand Down