C# 如何将function.json添加到现有的.NET函数2.0

C# 如何将function.json添加到现有的.NET函数2.0,c#,azure,azure-functions,serverless,C#,Azure,Azure Functions,Serverless,当我使用func new--name MyHttpTrigger--template“HttpTrigger”添加新函数时,此函数中没有创建函数.json,当我尝试将其添加到当前目录并运行func start--build时,我遇到以下错误: 找不到作业函数。试着公开你的作业类和方法。如果您正在使用绑定扩展(例如Azure存储、ServiceBus、计时器等),请确保已在启动代码中调用扩展的注册方法(例如builder.AddAzureStorage()、builder.AddServiceBu

当我使用
func new--name MyHttpTrigger--template“HttpTrigger”
添加新函数时,此函数中没有创建
函数.json
,当我尝试将其添加到当前目录并运行
func start--build
时,我遇到以下错误:

找不到作业函数。试着公开你的作业类和方法。如果您正在使用绑定扩展(例如Azure存储、ServiceBus、计时器等),请确保已在启动代码中调用扩展的注册方法(例如builder.AddAzureStorage()、builder.AddServiceBus()、builder.AddTimers()等)。 您可以在这里找到我的
function.json
内容:

{
  "disabled": false,
  "bindings": [
    {
      "authLevel": "anonymous",
      "name": "req",
      "type": "httpTrigger",
      "direction": "in"
    },
    {
      "name": "res",
      "type": "http",
      "direction": "out"
    }
  ]
}
透水httpTrigger函数
namespace final
{
公共静态类httpTrigger
{
[函数名(“httpTrigger”)]
公共静态异步任务运行(
[HttpTrigger(AuthorizationLevel.Function,“get”,“post”,Route=null)]HttpRequest请求,
ILogger日志)
{
LogInformation(“C#HTTP触发器函数处理了一个请求。”);
字符串名称=请求查询[“名称”];
string requestBody=等待新的StreamReader(req.Body).ReadToEndAsync();
动态数据=JsonConvert.DeserializeObject(requestBody);
名称=名称??数据?.name;
返回名称!=null
?(ActionResult)新的OkObjectResult($“你好,{name}”)
:new BadRequestObjectResult(“请在查询字符串或请求正文中传递名称”);
}
}
}
新的httpTrigger函数
namespace final
{
公共静态类httpTrigger
{
公共静态异步任务运行(HttpRequest请求,ILogger日志)
{
LogInformation(“C#HTTP触发器函数处理了一个请求。”);
字符串名称=请求查询[“名称”];
string requestBody=等待新的StreamReader(req.Body).ReadToEndAsync();
动态数据=JsonConvert.DeserializeObject(requestBody);
名称=名称??数据?.name;
返回名称!=null
?(ActionResult)新的OkObjectResult($“你好,{name}”)
:new BadRequestObjectResult(“请在查询字符串或请求正文中传递名称”);
}
}
}

简短回答 构建过程负责根据附加到方法的属性为.cs文件中定义的函数生成
function.json
文件。函数文档中有几个示例,例如,您不需要添加自己的
function.json

请参阅“详细答案”部分,以详细了解其幕后工作原理。如果您看到为已编译的C#函数描述的所有预期生成输出,并且函数运行时仍然没有找到任何函数,请检查您是否正在针对函数应用程序结构的顶级目录运行
func start--build

长答案 你所描述的行为是故意的。听起来您已经习惯了脚本语言使用的函数的文件夹结构,例如.csx(C#script)文件。下面的示例定义了两个函数,
MyFunction
MySecondFunction

FunctionApp
| - bin
| - MyFunction
| | - function.json
| | - run.csx
| - MySecondFunction
| | - function.json
| | - run.csx
| ...
| host.json
| local.settings.json
最初,函数运行时只识别这个文件夹结构,而C#函数只能用C#script编写。(|)之后,添加了常规的C#支持。我将参考常规C#as compiled C#,以强调它与C#script之间的区别

与编译的C#函数应用程序相同的示例具有如下文件夹结构:

FunctionApp
| - bin
| - obj
| - host.json
| - local.settings.json
| - FunctionApp.csproj
| - MyFunction.cs
| - MySecondFunction.cs
FunctionApp
| - bin
| | - Debug
| | | - net461
| | | | - bin
| | | | - MyFunction
| | | | | - function.json
| | | | - MySecondFunction
| | | | | - function.json
| | | - host.json
| | | - local.settings.json
| | | - netstandard2.0
| | | | - …
| - obj
| - host.json
| - local.settings.json
| - FunctionApp.csproj
| - MyFunction.cs
| - MySecondFunction.cs
{
  "generatedBy": "Microsoft.NET.Sdk.Functions-1.0.13",
  "configurationSource": "attributes",
  "bindings": [
    {
      "type": "httpTrigger",
      "methods": [
        "post"
      ],
      "authLevel": "function",
      "name": "req"
    }
  ],
  "disabled": false,
  "scriptFile": "../bin/VSSample.dll",
  "entryPoint": "VSSample.HttpStart.Run"
}
如果您构建此项目并展开
FunctionApp/bin
文件夹,您将看到如下内容:

FunctionApp
| - bin
| - obj
| - host.json
| - local.settings.json
| - FunctionApp.csproj
| - MyFunction.cs
| - MySecondFunction.cs
FunctionApp
| - bin
| | - Debug
| | | - net461
| | | | - bin
| | | | - MyFunction
| | | | | - function.json
| | | | - MySecondFunction
| | | | | - function.json
| | | - host.json
| | | - local.settings.json
| | | - netstandard2.0
| | | | - …
| - obj
| - host.json
| - local.settings.json
| - FunctionApp.csproj
| - MyFunction.cs
| - MySecondFunction.cs
{
  "generatedBy": "Microsoft.NET.Sdk.Functions-1.0.13",
  "configurationSource": "attributes",
  "bindings": [
    {
      "type": "httpTrigger",
      "methods": [
        "post"
      ],
      "authLevel": "function",
      "name": "req"
    }
  ],
  "disabled": false,
  "scriptFile": "../bin/VSSample.dll",
  "entryPoint": "VSSample.HttpStart.Run"
}
netstandard2.0
文件夹将包含与
net461
文件夹类似的内容;它们只是不同的框架构建目标。)

请注意,编译的C#function应用程序文件夹结构中的
FunctionApp/bin/Debug/net461与C#script应用程序文件夹结构中的
FunctionApp
之间的相似性。这是因为C#(不是C#脚本)函数应用程序的生成过程使用.cs文件(例如
HttpTrigger`)中方法的属性来确定定义了哪些函数,并创建原始文件夹结构作为其生成输出

当Azure Functions运行时启动时(例如通过
func host start
),它不会查看
FunctionApp
来找出存在哪些函数并连接绑定。它查看
functionpp/bin/Debug/net461/MyFunction

唯一的区别是每个函数的文件夹。在已编译的C#function应用程序中,每个函数的文件夹都缺少C#script函数应用程序中的.csx文件。仔细查看编译后的C#function应用程序的
functionpp/bin/Debug/net461/MyFunction
文件夹中的
function.json
,您会看到如下内容:

FunctionApp
| - bin
| - obj
| - host.json
| - local.settings.json
| - FunctionApp.csproj
| - MyFunction.cs
| - MySecondFunction.cs
FunctionApp
| - bin
| | - Debug
| | | - net461
| | | | - bin
| | | | - MyFunction
| | | | | - function.json
| | | | - MySecondFunction
| | | | | - function.json
| | | - host.json
| | | - local.settings.json
| | | - netstandard2.0
| | | | - …
| - obj
| - host.json
| - local.settings.json
| - FunctionApp.csproj
| - MyFunction.cs
| - MySecondFunction.cs
{
  "generatedBy": "Microsoft.NET.Sdk.Functions-1.0.13",
  "configurationSource": "attributes",
  "bindings": [
    {
      "type": "httpTrigger",
      "methods": [
        "post"
      ],
      "authLevel": "function",
      "name": "req"
    }
  ],
  "disabled": false,
  "scriptFile": "../bin/VSSample.dll",
  "entryPoint": "VSSample.HttpStart.Run"
}
与为C#script函数创建的
function.json
相比,编译后的C#function的
function.json
有几个额外的字段。以下是每个函数对函数运行时的指示:

  • generatedBy
    :此
    函数.json
    是在编译过程中生成的,不是手工编写的
  • configurationSource
    :此
    函数.json
    是从C#属性生成的
  • scriptFile
    :包含与此函数对应的编译代码的DLL的位置,相对于此
    函数.json
    文件的位置
  • 入口点
    :编译的DLL中函数的方法签名
长话短说,编译后的C#函数应用程序在运行时依赖于与C#脚本函数应用程序相同的文件夹结构,但它是由构建过程生成的,而不是由开发人员构建的。编写函数的开发人员