All About ASP.NET and ASP.NET Core 2 Hosting BLOG

Tutorial and Articles about ASP.NET and the latest ASP.NET Core

ASP.NET Core Hosting :: How to Create CRUD App Using Blazor and ASP.NET Core

clock April 12, 2019 07:43 by author Jervis

Blazor is an experimental .NET web framework using C#/Razor and HTML that runs in the browser via WebAssembly. It simplifies the process of creating single page application (SPA) and at the same time also brings the power of .NET on the table. It runs in the browser on a real .NET runtime (Mono) implemented in WebAssembly that executes normal .NET assemblies. In this post, we’ll see how to set up blazor and create a CRUD app using blazor and ASP.NET Core.

Creating a Blazor App

To create a blazor app, open Visual Studio 2017 “Preview” version, hit Ctrl+Shift+N and select the ASP.NET Core Web Application (.NET Core) project type from the templates. When you click Ok, you will get the following prompt,

If you don’t see these blazor templates, make sure .NET Core and ASP.NET Core 2.0 are selected at the top. As you can see, there are 2 blazor templates. The standalone “Blazor” template will create a static blazor app and “Blazor (ASP.NET Core Hosted)” will create an ASP.NET Core hosted blazor app. Choose “Blazor (ASP.NET Core Hosted)” to create the blazor app. Now, our first Blazor app will be created. In the solution explorer, you will find 3 projects created.

  • <Project Name>.Client: Standard blazor application.
  • <Project Name>.Server: It’s an ASP.NET Core Web API project.
  • <Project Name>.Shared: It’s a .NET standard class library project used for holding the shared resources.

You should run the app to make sure that there are no errors. Press Ctrl-F5 to run the app without the debugger. Running with the debugger (F5) is not supported at this time. Next thing is to extend this app to create a CRUD app.

Create the CRUD App

Before we move on, take a look at the following image to get an idea of what we are building.

As you can see, we are building a ToDo List app supporting all the CRUD operations and a listing of the ToDo items. We’ll use Entity Framework Core in-memory provider to store the ToDo list. So let’s begin.

First, create a ToDoList class in the Shared project with the following code. It’s a simple class with only two members.

public class ToDoList
{
    public int ID { get; set; }

    public string Item { get; set; }
}

Next, create EF DBContext to interact with the database. To do that, create a new folder named “Data” in the Server project. Add a new class file named ToDoListContext with the following code.

public class ToDoListContext : DbContext
{
    public ToDoListContext(DbContextOptions<ToDoListContext> options) : base(options) { }

    public DbSet<ToDoList> ToDoLists { get; set; }
}

We’ll need to configure InMemory db provider. To do that, Open Startup.cs class and navigate to the ConfigureServices method to configure it (Line no. 4).

public void ConfigureServices(IServiceCollection services)
{
    // Use Entity Framework in-memory provider for this sample
    services.AddDbContext<ToDoListContext>(options => options.UseInMemoryDatabase("ToDoList"));

    services.AddMvc().AddJsonOptions(options =>
    {
        options.SerializerSettings.ContractResolver = new DefaultContractResolver();
    });

    services.AddResponseCompression(options =>
    {
        options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[]
        {
            MediaTypeNames.Application.Octet,
            WasmMediaTypeNames.Application.Wasm,
        });
    });
}

Next, we’ll need an API controller for all CRUD operations. To do that, create a new controller named ToDoListController inside the Controller folder in the Server project with the following code.

[Produces("application/json")]
[Route("api/ToDo")]
public class ToDoListController : Controller
{
    private readonly ToDoListContext _context;

    public ToDoListController(ToDoListContext context)
    {
        _context = context;
    }

    // GET: api/ToDo
    [HttpGet]
    public IEnumerable<ToDoList> GetToDo()
    {
        return _context.ToDoLists;
    }

    // POST: api/ToDo
    [HttpPost]
    public async Task<IActionResult> PostToDo([FromBody] ToDoList todo)
    {
        if (!ModelState.IsValid)
            return BadRequest(ModelState);

        _context.ToDoLists.Add(todo);
        await _context.SaveChangesAsync();
        return CreatedAtAction("GetToDo", new { id = todo.ID }, todo);
    }

    // PUT: api/ToDo/5
    [HttpPut("{id}")]
    public async Task<IActionResult> PutToDo([FromRoute] int id, [FromBody] ToDoList todo)
    {
        if (!ModelState.IsValid)
            return BadRequest(ModelState);

        if (id != todo.ID)
            return BadRequest();

        _context.Entry(todo).State = EntityState.Modified;
        try
        {
            await _context.SaveChangesAsync();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!ToDoExists(id))
                return NotFound();
            else
                throw;
        }
        return NoContent();
    }

    // DELETE: api/ToDo/5
    [HttpDelete("{id}")]
    public async Task<IActionResult> DeleteToDo([FromRoute] int id)
    {
        var ToDo = await _context.ToDoLists.SingleOrDefaultAsync(m => m.ID == id);
        if (ToDo == null)
            return NotFound();

        _context.ToDoLists.Remove(ToDo);
        await _context.SaveChangesAsync();
        return Ok(ToDo);
    }

    private bool ToDoExists(int id)
    {
        return _context.ToDoLists.Any(e => e.ID == id);
    }
}

Here, the controller has API for all CRUD operations with proper validations. Quite familiar piece of code, isn’t it?

Blazor App Code

Next, we need to add the ToDo component to the Client App. The default app is already having Home, Counter and Fetch Data component. To add ToDo component, right-click on Pages folder and select Add > New Item. Select Web from the left panel, then select “Razor View” from templates panel and name it ToDo.cshtml. Put the following code in the file.

@page "/todo"
@using ASPNETBlazorCRUDApp.Shared
@using Microsoft.AspNetCore.Blazor.Browser.Interop
@inject HttpClient Http

<h1>ToDo List</h1>

<div>
    <div class="row">
        <div class="col-sm-1">
            <p>Item:</p>
        </div>
        <div class="col-sm-4">
            <input id="todoName" placeholder="Item Name" @bind(itemName)>
        </div>
    </div>
    <br />
    <div class="row">
        <div class="col-sm-1">
            <button class="btn btn-info" id="btnAdd" @onclick(async () => await AddToDo())>Add</button>
        </div>
        <div class="col-sm-2">
            @if (todos != null && todos.Count > 0)
            {
                <button class="btn btn-danger" @onclick(async () => await DeleteAllToDos())>Delete All</button>
            }
        </div>
    </div>
</div>
<br /><br />
@if (todos == null)
{
    <p><em>Loading...</em></p>
}
else
{
    @if (todos.Count > 0)
    {
        <table class='table table-striped table-bordered table-hover table-condensed' style="width:80%;">
            <thead>
                <tr>
                    <th style="width: 40%">Name</th>
                    <th style="width: 40%">Edit</th>
                    <th style="width: 20%">Delete</th>
                </tr>
            </thead>
            <tbody>
                @foreach (var todo in todos)
                {
                    <tr>
                        <td>
                            <span id="spn_@todo.ID">@todo.Item</span>
                            <input id="txt_@todo.ID" @bind(UpdateItemName) style="display:none;">
                        </td>
                        <td>
                            <button id="btnEdit_@todo.ID" class="btn btn-primary" @onclick(() => EditToDo(todo.ID, todo.Item))>Edit</button>
                            <button id="btnUpdate_@todo.ID" style="display:none;" class="btn btn-success" @onclick(async () => await UpdateToDo(todo.ID))>Update</button>
                            <button id="btnCancel_@todo.ID" style="display:none;" class="btn btn-primary" @onclick(() => CancelToDo(todo.ID))>Cancel</button>
                        </td>
                        <td><button class="btn btn-danger" @onclick(async () => await DeleteToDo(todo.ID))>Delete</button></td>
                    </tr>
                }
            </tbody>
        </table>
    }
}

<script>
    Blazor.registerFunction('ShowControl', (id, item, bShow) => {
        if (bShow) {
            var txtInput = document.getElementById("txt_" + id);
            document.getElementById("spn_" + id).style.display = "none";
            txtInput.style.display = "";
            txtInput.value = item;
            txtInput.focus();
            document.getElementById("btnEdit_" + id).style.display = "none";
            document.getElementById("btnUpdate_" + id).style.display = "";
            document.getElementById("btnCancel_" + id).style.display = "";
        }
        else {
            document.getElementById("spn_" + id).style.display = "";
            document.getElementById("txt_" + id).style.display = "none";
            document.getElementById("btnEdit_" + id).style.display = "";
            document.getElementById("btnUpdate_" + id).style.display = "none";
            document.getElementById("btnCancel_" + id).style.display = "none";
        }
        return true;
    });
</script>
@functions {
        string itemName;
        string UpdateItemName;

        IList<ToDoList> todos = new List<ToDoList>();

protected override async Task OnInitAsync()
    {
        await Refresh();
    }

    private async Task Refresh()
    {
        todos = await Http.GetJsonAsync<ToDoList[]>("/api/ToDo");
        StateHasChanged();
    }

    private async Task AddToDo()
    {
        if (!string.IsNullOrEmpty(itemName))
        {
            await Http.SendJsonAsync(HttpMethod.Post, "/api/ToDo", new ToDoList
            {
                Item = itemName,
            });

            itemName = "";
            await Refresh();
        }
    }

    private async Task UpdateToDo(int id)
    {
        if (!string.IsNullOrEmpty(UpdateItemName))
        {
           await Http.SendJsonAsync(HttpMethod.Put, $"/api/ToDo/{id}", new ToDoList
            {
                ID = id,
                Item = UpdateItemName,
            });

            await Refresh();
            //UpdateItemName = "";

            RegisteredFunction.Invoke<bool>("ShowControl", id.ToString(), "", false);
        }
    }

    private async Task DeleteToDo(int id)
    {
        await Http.DeleteAsync($"/api/ToDo/{id}");
        await Refresh();
    }

    private void EditToDo(int id, string itemName)
    {
        RegisteredFunction.Invoke<bool>("ShowControl", id.ToString(), itemName, true);
    }

    private void CancelToDo(int id)
    {
        RegisteredFunction.Invoke<bool>("ShowControl", id.ToString(), "", false);
    }

    private async Task DeleteAllToDos()
    {
        foreach (var c in todos)
        {
            await Http.DeleteAsync($"/api/ToDo/{c.ID}");
        }

        await Refresh();
    }
}

Let’s understand this code. The above code can be divided into 3 sections (HTML, JavaScript and C#). We’ll take a look at each of them in details. before, we do that, let’s understand the very first 4 lines.

@page "/todo"
@using ASPNETBlazorCRUDApp.Shared
@using Microsoft.AspNetCore.Blazor.Browser.Interop
@inject HttpClient Http

  • We are defining the route for this page using @page directive. So appending “/todo” to base URL will redirect to this component.
  • Including the ASPNETBlazorCRUDApp.Shared namespace as ToDoList class is used.
  • The namespace Microsoft.AspNetCore.Blazor.Browser.Interop is included to call JavaScript from the Blazor code. Currently, WebAssembly and therefore Mono and Blazor have no direct access to the browser’s DOM API. Read this post for a detailed information.
  • We are also injecting HTTPClient dependency to call the server-side API.

Now,let’s understand the each section.

HTML

The HTML part is quite simple if you are familiar with Razor syntax. It has a table and a couple of buttons. The @bind is used for two-way data-binding. Read more about data binding here. The @onclick is used to call the method on the click of the button.

JavaScript

Blazor directly can’t call JavaScript function. From learn-blazor.com

To call other JS libraries or your own custom JS/TS code from .NET, the current approach is to register a named function in a JavaScript/TypeScript file, e.g.:

// This is JavaScript
Blazor.registerFunction('doPrompt', message => {
  return prompt(message);
});

… and then wrap it for calls from .NET:

// This is C#
public static bool DoPrompt(string message)
{
    return RegisteredFunction.Invoke<bool>("doPrompt", message);
} 

The JavaScript function named ShowControl defined in the code, controls the visibility of the HTML elements. This is called from C# code on click of Edit, Update and Cancel button.

C#

At the bottom of the page, there is a @functions section which contains code for making server-side web api call for CRUD operations, calling JavaScript and refreshing the ToDo list. The only new thing here is the way C# makes a call to JavaScript ShowControl function on Edit, Update and Cancel button. Other than that it’s simple and familiar code.

Lastly, we need to add the “ToDo” component link in the navigation bar. To do that, open the NavMenu.html file located inside the Shared folder in the client app and add the following code just after the “Fetch Data” link.

<li>
    <NavLink href="/todo">
        <span class='glyphicon glyphicon-list-alt'></span> To Do
    </NavLink>
</li>

That’s it. Run the application and you should see the following.

 

Final Thoughts

These are still early days for Blazor, but we can start building component-based web apps that run in the browser with .NET. Initially, you would find difficult to make progress. Microsoft has promised to bring many new features and improvements planned for future updates. Let’s hope that soon there will be an official documentation and great community support. In this post, we learned about the new .NET framework – Blazor and also created a simple CRUD application using Blazor.

I am really excited to play with this, so more blog post coming up on Blazor. Thank you for reading. Keep visiting this blog and share this in your network. Please put your thoughts and feedback in the comments section.



ASP.NET Core Hosting :: How to Produce HTTP Response in ASP.NET Core Outside of MVC Controllers

clock March 26, 2019 13:05 by author Jervis

ASP.NET Core 2.1 introduced support for a little (or, should I say, not at all) documented feature called IActionResultExecutor<T>. It allows us to use some of the action results -those that we are used to from MVC controllers – outside of the controller context, so for example from a middleware component.

Controller helper methods

The most important of the ActionResult family is the ObjectResult – which internally handles content negotiation – so determines what media type is suitable for the response, and serializes the response accordingly using the selected formatter (JSON, XML, Protobuf or whatever you support). The typical controller, using an ObjectResult might look like this:

[HttpGet("contacts")]
public IActionResult Get()
{
    var contacts = new[]
    {
        new Contact { Name = "Jervis", City = "Dallas" },
        new Contact { Name = "Not Jervis", City = "Not Dallas" }
    };
    // will do content negotiation
    return new ObjectResult(contacts);
}

We could also return a POCO directly from the action, but the framework would still end up using ObjectResult to process that, so ultimately it is still the same thing.

[HttpGet("contacts")]
public IEnumerable<Contact> Get()
{
    var contacts = new[]
    {
        new Contact { Name = "Jervis", City = "Dallas" },
        new Contact { Name = "Not Jervis", City = "Not Dallas" }
    };
    // will do content negotiation
    return contacts;
}

Finally, and this is what this post is about, you could replace our usage of ObjectResult, or the POCO, with a call to Ok() on the base controller:

[HttpGet("contacts")]
public IActionResult Get()
{
    var contacts = new[]
    {
        new Contact { Name = "Jervis", City = "Dallas" },
        new Contact { Name = "Not Jervis", City = "Not Dallas" }
    };
    // will do content negotiation
    return Ok(contacts);
}

This is really still the same as before, because Ok() actually uses ObjectResult under the hood, it’s just expressed in a slightly different way. Now, if you have done any work with MVC controllers, I am sure you are used to those helper methods that are there on the the framework’s ControllerBase, like our Ok() or other – for example Unauthorized() or File(), to name just two of them.

They come in many, many variants (the base controller is 2700+ lines of code…), and are used as shortcuts into the many ActionResults that the framework offers. You can find a full list here. From my experience with various ASP.NET Core MVC projects, I would say that these helper methods are extremely popular among developers – they make the code very concise. They also hide certain complexity level of dealing with producing the HTTP responses, yet still make it very obvious to understand what is going on.

Controller feeling, without a controller

What we recently introduced into WebApiContrib.Core is a similar set of methods as found on the base controller, but ported as extension methods on top of HttpContext. This is done as a combination of IActionResultExecutor<T> features and “manual” response creation. Meaning we either mimic the behavior of the method from base controller by manually crafting the HTTP response, setting headers, status codes and so on, or we really reach into the IActionResultExecutor<T> infrastructure and invoke the relevant action result from the MVC framework.

The end result is very neat, and you get very similar helper method set that you can now enjoy from your non-controller code – primarily middleware but potentially some other places too.

So imagine you are creating a “lightweight” HTTP endpoint using the new Endpoint Routing feature. You can now use the new helper methods to produce the response. Here is a full sample application setup:

public static async Task Main(string[] args) =>
    await WebHost.CreateDefaultBuilder(args)
        .ConfigureServices(s =>
        {
            s.AddRouting();            

            // necessary to wire in ActionResults
            // and content negotiation
            // you can manually register other formatters here
            // for example Messagepack or Protobuf
            s.AddMvc(); 

            // note: due to the current state of ASP.NET Core 3.0 (preview3)
            // you need to manually call: s.AddMvc().AddNewtonsoftJson()
            // to use JSON formatter. This will be fixed in the future in the framework
        })
        .Configure(app =>
        {
            app.UseRouting(r =>
            {
                r.MapGet("contacts", async context =>
                {
                    var contacts = new[]
                    {
                        new Contact { Name = "Jervis", City = "Dallas" },
                        new Contact { Name = "No Jervis", City = "Not Dallas" }
                    }; 

                    // from WebApiContrib.Core
                    // will do content negotiation
                    await context.Ok(contacts);
                });
            });
        }).Build().RunAsync();

In order to make this work, the only thing that is needed, is that it’s necessary to have a call to services.AddMvc() in the DI container setup, as the action result and the executor infrastructure is bootstrapped there.

Other than that, this Ok() extension method on HttpContext will behave exactly the same as the Ok() on base controller – including performing the full content negotiation. The example uses endpoint routing from ASP.NET Core 3.0, but it would work from any place in ASP.NET Core request processing pipeline, for example with an IRouter in ASP.NET Core 2.1 or any middleware.

Another example we could quickly look at here, is returning a file stream – instead of dealing with it manually, including all the complexity of async reading of the stream or stuff related to Content-Disposition headers and so on, we can simply use an extension method now:

app.UseRouting(r =>
{
    r.MapGet("download", async context =>
    {
        // some file path
        var path = Path.GetFullPath(Path.Combine("files", "myfile.pdf")); 

        // from WebApiContrib.Core
        await context.PhysicalFile(path, "application/pdf");
    });
});

In this particular case, the helper method would end up using an action result, a PhysicalFileActionResult, which will take care of reading the file in a non-blocking way and make sure all HTTP response details are correctly handled. And just like before, PhysicalFile() mimics a corresponding method from the MVC base controller.

The full list of the available extension methods can be found below. And I really encourage you to try them – they are part of WebApiContrib.Core 2.2.0.

Task Accepted(this HttpContext c, Uri uri, object value);
Task Accepted(this HttpContext c, string uri, object value);
Task Accepted(this HttpContext c, string uri);
Task Accepted(this HttpContext c, Uri uri);
Task Accepted(this HttpContext c, object value);
Task Accepted(this HttpContext c);
Task BadRequest(this HttpContext c);
Task BadRequest(this HttpContext c, object error);
Task BadRequest(this HttpContext c, ModelStateDictionary modelState);
Task Conflict(this HttpContext c, object error);
Task Conflict(this HttpContext c, ModelStateDictionary modelState);
Task Conflict(this HttpContext c);
Task Content(this HttpContext c, string content, MediaTypeHeaderValue contentType);
Task Content(this HttpContext c, string content, string contentType, Encoding contentEncoding);
Task Content(this HttpContext c, string content, string contentType);
Task Content(this HttpContext c, string content);
Task Created(this HttpContext c, string uri, object value);
Task Created(this HttpContext c, Uri uri, object value);
Task File(this HttpContext c, string virtualPath, string contentType);
Task File(this HttpContext c, Stream fileStream, string contentType, DateTimeOffset? lastModified, EntityTagHeaderValue entityTag, bool enableRangeProcessing);
Task File(this HttpContext c, Stream fileStream, string contentType, string fileDownloadName, DateTimeOffset? lastModified, EntityTagHeaderValue entityTag);
Task File(this HttpContext c, Stream fileStream, string contentType, string fileDownloadName, DateTimeOffset? lastModified, EntityTagHeaderValue entityTag, bool enableRangeProcessing);
Task File(this HttpContext c, string virtualPath, string contentType, string fileDownloadName);
Task File(this HttpContext c, string virtualPath, string contentType, string fileDownloadName, bool enableRangeProcessing);
Task File(this HttpContext c, string virtualPath, string contentType, DateTimeOffset? lastModified, EntityTagHeaderValue entityTag);
Task File(this HttpContext c, string virtualPath, string contentType, DateTimeOffset? lastModified, EntityTagHeaderValue entityTag, bool enableRangeProcessing);
Task File(this HttpContext c, Stream fileStream, string contentType, DateTimeOffset? lastModified, EntityTagHeaderValue entityTag);
Task File(this HttpContext c, string virtualPath, string contentType, bool enableRangeProcessing);
Task File(this HttpContext c, Stream fileStream, string contentType, string fileDownloadName, bool enableRangeProcessing);
Task File(this HttpContext c, byte[] fileContents, string contentType, string fileDownloadName);
Task File(this HttpContext c, Stream fileStream, string contentType, bool enableRangeProcessing);
Task File(this HttpContext c, Stream fileStream, string contentType);
Task File(this HttpContext c, byte[] fileContents, string contentType, string fileDownloadName, DateTimeOffset? lastModified, EntityTagHeaderValue entityTag, bool enableRangeProcessing);
Task File(this HttpContext c, byte[] fileContents, string contentType, string fileDownloadName, DateTimeOffset? lastModified, EntityTagHeaderValue entityTag);
Task File(this HttpContext c, byte[] fileContents, string contentType, DateTimeOffset? lastModified, EntityTagHeaderValue entityTag, bool enableRangeProcessing);
Task File(this HttpContext c, byte[] fileContents, string contentType, DateTimeOffset? lastModified, EntityTagHeaderValue entityTag);
Task File(this HttpContext c, byte[] fileContents, string contentType, string fileDownloadName, bool enableRangeProcessing);
Task File(this HttpContext c, string virtualPath, string contentType, string fileDownloadName, DateTimeOffset? lastModified, EntityTagHeaderValue entityTag);
Task File(this HttpContext c, byte[] fileContents, string contentType, bool enableRangeProcessing);
Task File(this HttpContext c, byte[] fileContents, string contentType);
Task File(this HttpContext c, Stream fileStream, string contentType, string fileDownloadName);
Task File(this HttpContext c, string virtualPath, string contentType, string fileDownloadName, DateTimeOffset? lastModified, EntityTagHeaderValue entityTag, bool enableRangeProcessing);
Task Forbid(this HttpContext c);
Task LocalRedirect(this HttpContext c, string localUrl);
Task LocalRedirectPermanent(this HttpContext c, string localUrl);
Task LocalRedirectPermanentPreserveMethod(this HttpContext c, string localUrl);
Task LocalRedirectPreserveMethod(this HttpContext c, string localUrl);
Task NoContent(this HttpContext c);
Task NotFound(this HttpContext c, object value);
Task NotFound(this HttpContext c);
Task Ok(this HttpContext c);
Task Ok(this HttpContext c, object value);
Task PhysicalFile(this HttpContext c, string physicalPath, string contentType, string fileDownloadName, DateTimeOffset? lastModified, EntityTagHeaderValue entityTag, bool enableRangeProcessing);
Task PhysicalFile(this HttpContext c, string physicalPath, string contentType, string fileDownloadName, DateTimeOffset? lastModified, EntityTagHeaderValue entityTag);
Task PhysicalFile(this HttpContext c, string physicalPath, string contentType, DateTimeOffset? lastModified, EntityTagHeaderValue entityTag, bool enableRangeProcessing);
Task PhysicalFile(this HttpContext c, string physicalPath, string contentType);
Task PhysicalFile(this HttpContext c, string physicalPath, string contentType, DateTimeOffset? lastModified, EntityTagHeaderValue entityTag);
Task PhysicalFile(this HttpContext c, string physicalPath, string contentType, string fileDownloadName, bool enableRangeProcessing);
Task PhysicalFile(this HttpContext c, string physicalPath, string contentType, string fileDownloadName);
Task PhysicalFile(this HttpContext c, string physicalPath, string contentType, bool enableRangeProcessing);
Task Redirect(this HttpContext c, string url);
Task RedirectPermanent(this HttpContext c, string url);
Task RedirectPermanentPreserveMethod(this HttpContext c, string url);
Task RedirectPreserveMethod(this HttpContext c, string url);
Task StatusCode(this HttpContext c, int statusCode);
Task StatusCode(this HttpContext c, int statusCode, object value);
Task Unauthorized(this HttpContext c);
Task Unauthorized(this HttpContext c, object value);
Task UnprocessableEntity(this HttpContext c, object error);
Task UnprocessableEntity(this HttpContext c, ModelStateDictionary modelState);
Task UnprocessableEntity(this HttpContext c);
Task ValidationProblem(this HttpContext c, ValidationProblemDetails descriptor);
Task ValidationProblem(this HttpContext c, ModelStateDictionary modelStateDictionary);
Task WriteActionResult<TResult>(this HttpContext c, TResult result) where TResult : IActionResult;



ASP.NET Core Hosting :: How to Use StructureMap with ASP.NET Core

clock January 8, 2019 10:26 by author Jervis

This example shows how to use Structuremap dependency injection framework with ASP.NET Core instead of framework-level dependency injection.

ADDING STRUCTUREMAP TO ASP.NET CORE PROJECT

For Structuremap support in ASP.NET Core application we need two NuGet packages

  • StructureMap - core StructureMap package
  • StructureMap.Microsoft.DependencyInjection - adds support for ASP.NET Core

These packages are enough for getting StructureMap up and running.

DEMO SERVICES

For demo purposes let's define primitive messaging service interface and couple of implementations.

public interface IMessagingService
{
    string GetMessage();
}

public class BuiltInDiMessagingService : IMessagingService
{
    public string GetMessage()
    {
        return "Hello from built-in dependency injection!";
    }
}

public class StructuremapMessagingService : IMessagingService
{
    public string GetMessage()
    {
        return "Hello from Structuremap!";
    }
}

We need two implementations to demonstrate how built-in dependency injection is replaced by StructureMap.

DEFINING STRUCTUREMAP REGISTRY

StructureMap uses registry classes for defining dependencies. Direct definitions are also supported but for more complex applications we will write registries anyway. Here is our registry class.

public class MyStructuremapRegistry : Registry
{
    public MyStructuremapRegistry()
    {
        For<IMessagingService>().LifecycleIs(Lifecycles.Container)
                                .Use<StructuremapMessagingService>();
    }
}

ATTACHING STRUCTUREMAP TO ASP.NET CORE APPLICATION

StructureMap is attached to ASP.NET Core when application is starting up. We have to make three updates to ConfigureServices() method of StartUp class:

  • initialize and configure StructureMap container
  • make ConfigureServices return IServiceProvider
  • return IServiceProvider by StructureMap

public IServiceProvider ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    services.AddTransient<IMessagingService, BuiltInDiMessagingService>();

    var container = new Container();

    container.Configure(config =>
    {
        config.AddRegistry(new MyStructuremapRegistry());
        config.Populate(services);
    });

    return container.GetInstance<IServiceProvider>();
}

Notice that there is also dependecy definition for framework-level dependency injection. Let's see which implementation wins.

TRYING OUT STRUCTUREMAP WITH ASP.NET CORE 2.0

Let's make some minor updates to Home controller and Index view to get message from injected service and display it on home page of sample application.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using ASPNETCoreTemplate.Services;
using Microsoft.AspNetCore.Mvc;

namespace ASPNETCoreTemplate.Controllers
{
    public class HomeController : Controller
    {
        private readonly IMessagingService _messagingService;

        public HomeController (IMessagingService messagingService)
        {
            _messagingService = messagingService;
        }

        public IActionResult Index()
        {
            ViewData["Message"] = _messagingService.GetMessage();

            return View();
        }

        public IActionResult Error()
        {
            return View();
        }
    }
}



ASP.NET Core Hosting :: How to Fix Error "An error occurred while starting the application" in ASP.NET Core

clock November 9, 2018 09:43 by author Jervis

Previously, we have written tutorial about how to fix 502.5 error that you face when publishing your ASP.NET Core application. Another issue that you might face when publishing your .net Core application is:

“An error occurred while starting the application.  .NET Framework <version number> | Microsoft.AspNetCore.Hosting version <version number> | Microsoft Windows <version number>”

It looks like:

What happened?

It basically means something really bad happened with your app.  Some things that might have gone wrong:

  • You might not have the correct .NET Core version installed on the server.
  • You might be missing DLL’s
  • Something went wrong in your Program.cs or Startup.cs before any exception handling kicked in

Event Viewer (Probably) Won’t Show You Anything

If you’re running on Windows and behind IIS, you might immediately go to the Event Viewer to see what happened based on your previous ASP.NET knowledge.  You’ll notice that the error is not there.  This is because Event Logging must be wired up explicitly and you’ll need to use the Microsoft.Extensions.Logging.EventLog package, and depending on the error, you might not have a chance to even catch it to log to the Event Viewer.

How to figure out what happened (if running on IIS)

Instead of the Event Viewer, if you’re running behind IIS, we can log the request out to a file.  To do that:

  1. Open your web.config
  2. Change stdoutLogEnabled=true
  3. Create a logs folder
    - Unfortunately, the AspNetCoreModule doesn’t create the folder for you by default. If you forget to create the logs folder, an error will be logged to the Event Viewer that says: Warning: Could not create stdoutLogFile \\?\YourPath\logs\stdout_timestamp.log, ErrorCode = -2147024893.
    -
    The “stdout” part of  the value “.\logs\stdout” actually references the filename not the folder.  Which is a bit confusing.
  4. Run your request again, then open the \logs\stdout_*.log file

Note – you will want to turn this off after you’re done troubleshooting, as it is a performance hit.

So your web.config’s aspNetCore element should look something like this

 <aspNetCore processPath=”.\YourProjectName.exe” stdoutLogEnabled=”true” stdoutLogFile=”.\logs\stdout” />

Doing this will log all the requests out to this file and when the exception occurs, it will give you the full stack trace of what happened in the \logs\stdout_*.log file

 

Hope this helps. In case, you need ASP.NET Core hosting, you can always try our services start from $1.00/month.



Cheap ASP.NET 4.5 Hosting

We’re a company that works differently to most. Value is what we output and help our customers achieve, not how much money we put in the bank. It’s not because we are altruistic. It’s based on an even simpler principle. "Do good things, and good things will come to you".

Success for us is something that is continually experienced, not something that is reached. For us it is all about the experience – more than the journey. Life is a continual experience. We see the Internet as being an incredible amplifier to the experience of life for all of us. It can help humanity come together to explode in knowledge exploration and discussion. It is continual enlightenment of new ideas, experiences, and passions


Author Link

 photo ahp banner aspnet-01_zps87l92lcl.png

 

Corporate Address (Location)

ASPHostPortal
170 W 56th Street, Suite 121
New York, NY 10019
United States

Tag cloud

Sign in