3

I have a FunctionApp running that exposes some entities and they all have the same CRUD operations that follow the same pattern and I was wondering if there was a way I could generalize this in the code. But I'm struggling a bit since everything is declared using attributes and they need to reference constants and those cannot be overridden in a derived class.

I would ideally like to have a base class like this, however I face the problem with the constants.

public abstract class EntityFunctions<T>
{
    protected const string someConstant = "Derived?";
    protected const string someOtherConstant = "Derived?";
    protected readonly ICrudService<T> _crudService;

    public EntityFunctions(ICrudService<T> crudService){
        _crudService = crudService;
    }

    [FunctionName("Create" + someConstant)]
    public async Task<string> Create([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = someOtherConstant)] T request)
    {
        return _crudService.Create(request);
    }
}

public class MyEntityFunctions : EntityFunctions<MyEntity>{
...
}

Is there a way around this? Some other way to ensure unique function-names and define "base"-routes. Or can functions be registered in another manner?

Matt Immer
  • 327
  • 1
  • 13
  • So you are trying to create httpTriggers dynamically? with same logic inside them? what is the scenario? – Aram.B Jul 19 '22 at 09:08
  • Hi @Aram.B. I have updated the example. Yes the logic on the "top level" is the same. Simply delegating the request to other services that takes care of the actual logic. I'm not sure if dynamically is the correct word - at least I'm not trying to do it runtime. Rather I would just like to avoid duplicating this multiple times. – Matt Immer Jul 19 '22 at 10:11
  • Hey Matt, I might be missing something but maybe the question is, what is the point then with having unique func names and routes? And maybe what is the reason to have it in the base class? Notice! I am just brainstorming here :) – Aram.B Jul 19 '22 at 13:39
  • 1
    Have a look at functionmokey, may suit your use case: https://github.com/JamesRandall/FunctionMonkey – Thomas Jul 19 '22 at 20:36
  • @Aram.B thanks for you input! Well I'd say it is because I have different entities that I want to expose on different endpoints /api/myentity1/id, /api/myentity2/id etc. They all need to have different routes and types but generally all follow the same pattern. That's why I wanted to reuse :) – Matt Immer Jul 20 '22 at 08:27
  • 1
    @Thomas Oh cool! Never heard of it before. It is a bit much to pull into my current project but will definitely consider it for future projects! – Matt Immer Jul 20 '22 at 08:28
  • I've asked a similar question here: https://stackoverflow.com/questions/73669611/azure-functions-inherit-httptrigger-functions-routes-from-base-class but in my case I'm not looking to template something - rather just directly inherit from a base class and expose it. Doesn't seem to be working - all ideas appreciated! – John Reilly Sep 10 '22 at 05:58

1 Answers1

0

While FunctionMonkey seems like an interesting project, it is a bit much to pull into my current project. Instead I managed to use T4 templates to do some code generation since my needs are quite simple. Example:

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".Generated.cs" #>
namespace Mynamespace
{
<#
var entities = new List<(string, string)>() { 
("MyEntity1", "my-entity1"), 
("MyEntity1", "my-entity1")
};

foreach (var (entityType, path) in entities)
{
#>
#region <#= entityType #>
    public class Base<#= entityType #>Functions
    {
        protected readonly ICrudService<<#= entityType #>> _crudService;

        public Base<#= entityType #>Functions(ICrudService<<#= entityType #>> crudService)
        {
            _crudService = crudService;
        }

        [FunctionName("Get<#= entityType #>")]
        public async Task<<#= entityType #>> Get([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "<#= path #>/{ik}")] HttpRequest req, string id)
        {
            return await _crudService.Get(id);
        }
    }
#endregion
<#
}    
#>
}
Matt Immer
  • 327
  • 1
  • 13