4

I'm not able to bind an input parameter of type blob to either string/TextReader without using the [BlobAttribute] in a C# implementation (not CSX).

The error I'm getting is:

Microsoft.Azure.WebJobs.Host: Error indexing method 'Functions.Harvester'. 
Microsoft.Azure.WebJobs.Host: Cannot bind parameter 'configReader' to type 
TextReader. Make sure the parameter Type is supported by the binding. If 
you're using binding extensions (e.g. ServiceBus, Timers, etc.) make sure 
you've called the registration method for the extension(s) in your startup 
code (e.g. config.UseServiceBus(), config.UseTimers(), etc.).

function.config:

"bindings": [
    {
      "type": "timerTrigger",
      "schedule": "0 */5 * * * *",
      "useMonitor": true,
      "runOnStartup": false,
      "direction": "in",
      "name": "myTimer"
    },
    {
      "type": "blob",
      "name": "configReader",
      "path": "secured/app.config.json",
      "connection": "XXX",
      "direction": "in"
    }
  ],

Function signature (NOT BINDING configReader):

[FunctionName("Harvester")]
 public static async Task Run(
   [TimerTrigger("0 */5 * * * *")]TimerInfo myTimer,
   TraceWriter log,
   TextReader configReader)

This would work though (BINDING configReader:

[FunctionName("Harvester")]
 public static async Task Run(
   [TimerTrigger("0 */5 * * * *")]TimerInfo myTimer,
   TraceWriter log,
   [Blob("secured/app.config.json", FileAccess.Read)]TextReader configReader)

Any idea on how to get it working without specifying the blob path in BlobAttribute. I'd ideally keep the Blob config outside of the code, that way my function would become more portable.

Janusz Nowak
  • 2,595
  • 1
  • 17
  • 36
marius-O
  • 395
  • 3
  • 15
  • Are you using VS 2017 tooling? If you are, can you check in the output folder for a folder called `Harvester` that contains a `function.json` file. Can you check if it has a property called `configurationSource` and what value it has? – ahmelsayed Aug 22 '17 at 16:15
  • I'm using VS2017 tooling, checked the file but there's no such property called `configurationSource`. Were you by any chance referring to `configReader`? If so, that's as described in my original question (btw the listing above comes from the azure UI). – marius-O Aug 22 '17 at 16:38
  • no, `configurationSource` is a new property that was introduced with the 2017 tooling to tell the function runtime to use attributes vs config file. Can you check the version of `` in your csproj? the latest is [1.0.2](https://www.nuget.org/packages/Microsoft.NET.Sdk.Functions), though note that this should put `configurationSource` to be `attributes`, you'll need to change that to `config` to tell the runtime to use the config instead of the attributes. Then you'll need to include that json file in your project – ahmelsayed Aug 22 '17 at 17:02
  • Your question was about the `Microsoft.NET.Sdk.Functions` package, and indeed I was on `1.0.0-alpha3`. Switched to `1.0.2` and the `configurationSrouce` got rendered. Thanks for that! If I include the JSON file in the project, does it need to point to the location in my output folder (bin\Debug...)? I've included it as a file in my source tree, but it still generates `configurationSource: 'attributes'` – marius-O Aug 22 '17 at 17:20
  • can you check this gif :) _it's easier this way_ https://ahmelsayed.com/content/images/2017/08/2017-08-22_10-51-30.gif – ahmelsayed Aug 22 '17 at 17:54
  • 1
    Thanks for sharing that, I can confirm it works as expected. Please escalate your comment as an answer and I'll feedback as such. Thanks! – marius-O Aug 22 '17 at 19:08

1 Answers1

6

The issue turned out to be with the latest runtime supporting a new property (configurationSource) in function.json

This tells the runtime to use either config (which is function.json) or C# attributes for function config.

essentially allowing you to either define your function like this

Now you can either define your function as

[FunctionName("Harvester")]
public static async Task Run(
    [TimerTrigger]TimerInfo myTimer,
    TraceWriter log,
    TextReader configReader)
{
}

along with a function.json that looks like this

{
  "generatedBy": "Microsoft.NET.Sdk.Functions-1.0.0.0",
  "configurationSource": "config",
  "bindings": [
    {
      "type": "timerTrigger",
      "schedule": "0 */5 * * * *",
      "useMonitor": true,
      "runOnStartup": false,
      "direction": "in",
      "name": "myTimer"
    },
    {
      "type": "blob",
      "name": "configReader",
      "path": "secured/app.config.json",
      "connection": "XXX",
      "direction": "in"
    }
  ],
  "disabled": false,
  "scriptFile": "...",
  "entryPoint": "..."
}

or like this

[FunctionName("Harvester")]
public static async Task Run(
    [TimerTrigger("0 */5 * * * *")]TimerInfo myTimer,
    TraceWriter log,
    [Blob("secured/app.config.json", FileAccess.Read)]TextReader configReader)
{
}

with a simpler config like this

{
  "generatedBy": "Microsoft.NET.Sdk.Functions-1.0.0.0",
  "configurationSource": "attributes",
  "bindings": [
    {
      "type": "timerTrigger",
      "name": "myTimer"
    },
  ],
  "scriptFile": "...",
  "entryPoint": "..."
}

note the value of configurationSource in both examples.

The tooling for Visual Studio 2017 does the latter by default. If you wanna change your function.json to include all your config and change the configurationSource you will need to include the file in your project and mark it as always copy. This GIF shows how to do that.

ahmelsayed
  • 7,125
  • 3
  • 28
  • 40