Asp.net core WebAPI support

This section describes how to use the entity and entity collection classes of the generated code in an Asp.net Core service scenario. This information is for .Net core / .Net 5+ users. If you're looking for ways to use the generated code in a .NET Remoting / WCF Webservice scenario, please see the legacy topics WebAPI / WCF / XML Webservices support and .NET remoting support.

Important!

The example code/service below doesn't contain any security oriented code. In your production systems you have to validate every request whether they're allowed to make that request. This security oriented code is outside the scope of this documentation, however it's essential you secure your service so only the right applications can access it.

.Net Core 3.x / .NET 5+: Asp.net Core with WebAPI

In Asp.net core WebAPI services the entities aren't exposed directly to the outside world. Instead you define one or more derived models using the LLBLGen Pro designer, and generate them as DTO classes, either as read-only (if your service doesn't accept data from the outside) or read/write.

To get started, in your project in the LLBLGen Pro designer, create per entity a new derived element, optionally include related elements and denormalize them if needed. Choose for derived model target framework the DTO classes framework. When generating code you get, besides the two Adapter projects, also two projects: the DTO classes project and a DTO persistence project.

When creating an Asp.net core service using e.g. a template in your IDE, be sure to generate a WebAPI project. Your startup class of the service should look something like this:

namespace Service
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            this.Configuration = configuration;
        }


        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            // Configure the LLBLGen Pro Runtime here with calls to RuntimeConfiguration
            RuntimeConfiguration.AddConnectionString("ConnectionString.SQL Server (SqlClient)", 
                                                     this.Configuration.GetConnectionString("ConnectionString.SQL Server (SqlClient)"));
            RuntimeConfiguration.ConfigureDQE<SQLServerDQEConfiguration>(c =>
                                                             {
                                                                 // add more here...
                                                                 c.AddDbProviderFactory(typeof(Microsoft.Data.SqlClient.SqlClientFactory));
                                                             });
            services.AddControllers();
            services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo {Title = "Service", Version = "v1"}); });
        }


        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if(env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseSwagger();
                app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Service v1"));
            }

            app.UseHttpsRedirection();
            app.UseRouting();
            app.UseAuthorization();
            app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
        }
        
        private IConfiguration Configuration { get; }
    }
}

The connection string is added to the appsettings.json file of the Asp.net core project, and looks something like this:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "ConnectionStrings": {
    "ConnectionString.SQL Server (SqlClient)": "data source=myserver;initial catalog=mydatabase;integrated security=SSPI;persist security info=False"
  },
  "AllowedHosts": "*"
}

To handle requests, you define controllers. Below is an example controller which contains two methods, one to return all orders for a given customer, and one method to update an order with the information in the DTO specified. The methods don't validate incoming requests so you have to add that yourself, using the well known Asp.net core systems for security APIs. The API below is more of an illustration how LLBLGen Pro works within an Asp.net core service.

The methods use QuerySpec but you can also use Linq if you prefer. The type Order is the derived element created in the LLBLGen Pro designer and derives from the entity OrderEntity. The QuerySpec queries below work on the OrderEntity instances, and the ProjectToOrder() method projects these instances to Order instances.

namespace NWSvc.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class OrderController : ControllerBase
    {
        [HttpGet]
        public async Task<IEnumerable<Order>> GetAsync(string customerId)
        {
            // Use a queryspec query which is directly projected to the DTO class hierarchy
            // at hand and returned in a list. A limit is specified as the OrderBy will end up in 
            // a subquery which requires a limit. 
            var qf = new QueryFactory();
            var q = qf.Order.Where(OrderFields.CustomerId == customerId)
                            .Limit(10000)
                            .OrderBy(OrderFields.OrderId.Ascending()).ProjectToOrder(qf);
            
            using(var adapter = new DataAccessAdapter())
            {
                // asp.net core doesn't have a synchronization context so we don't need to 
                // call ConfigureAwait(false)
                return await adapter.FetchQueryAsync(q);
            }
        }

        [HttpPut]
        public bool Update(Order newValues)
        {
            // TODO make sure the call is from an app / user allowed to make the call
            // TODO validate the newValues object whether it's valid. 
            
            using(var adapter = new DataAccessAdapter())
            {
                // First fetch the entity, which we'll then update
                var qf = new QueryFactory();
                var q = qf.Order.Where(OrderFields.OrderId.Equal(newValues.OrderId));
                OrderEntity orderToUpdate = adapter.FetchFirst(q);
                if(orderToUpdate == null)
                {
                    return false;
                }
                // Now update the entity with the values in the DTO. 
                // All values that are different will be updated
                orderToUpdate.UpdateFromOrder(newValues);
                // Entity has been updated, persist it and return the result.
                return adapter.SaveEntity(orderToUpdate);
            }
        }
    }
}

This methods' return values are converted to Json by Asp.net core WebAPI and are directly usable by the client.