loading...

【.NET Framework】Back to ASP.NET Framework 1

masanori_msl profile image Masui Masanori ・4 min read

Intro

In my private programming, I usually use .NET Core.
But I also use .NET Framework in my work.

I want to write at least ASP.NET MVC is the same for .NET Core and the .NET Framework.

So I try ASP.NET Framework this time.

Environments

  • Visual Studio 2019 Community
  • .NET Framework ver.4.8

Base Project

I create a project with Empty template.
Because there are no any class files, so I can't even open the root page(localhost:59522).
Alt Text

Routing

I can define how to route by "RouteConfig.cs".
First, I add "ASP.NET MVC" with NuGet.

  • ASP.NET MVC ver.5.2.7

As same as ASP.NET Core, I can use "Convention-Based Routing" and "Attribute-Based Routing".
Because I felt second one was easier to use, I choosed it.

RouteConfig.cs

using System.Web.Mvc;
using System.Web.Routing;

namespace NetFrameworkSample
{
    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            // Use Attribute-Based Routing
            routes.MapMvcAttributeRoutes();
        }
    }
}

Use RouteConfig

Because it is not activated automatically, I create Global Application Class(asax) and add RouteConfig.

Global.asax

<%@ Application Codebehind="Global.asax.cs" Inherits="NetFrameworkSample.Global" Language="C#" %>

Global.asax.cs

using System;
using System.Web.Mvc;
using System.Web.Routing;

namespace NetFrameworkSample
{
    public class Global : System.Web.HttpApplication
    {

        protected void Application_Start(object sender, EventArgs e)
        {
            AreaRegistration.RegisterAllAreas();
            RouteConfig.RegisterRoutes(RouteTable.Routes);
        }
    }
}

Although "Global.asax.cs" has many methods by default, I only use "Application_Start" in this time.

Add a Controller class

Now I can use "Attribute-Based Routing".
So I add a Controller class and show "Hello World!".

ProductsController.cs

using System.Web.Mvc;

namespace NetFrameworkSample.Controllers
{
    public class ProductsController: Controller
    {
        public ProductsController()
        {
        }
        [Route("")]
        public string GetMessage()
        {
            return "Hello world!";
        }
    }
}

Starting with "/"

In "Route" attribute, I can't start with slash like "[Route("/")]" or I get an exception.
I got a little confused, because I don't get any exceptions in ASP.NET Core.

Directory name

I add new route.

ProductsController.cs

...
        [Route("Products")]
        public string GetProductsMessage()
        {
            return "Hello Products";
        }
    }
}

Of cource I can get "Hello Products" with "localhost:59522/Products".

And I add "Products" directory and "IProductService.cs" into it.
After that, I access "localhost:59522/Products" and get an exception.

It is as same as before I add "RouteConfig".

Because if there is a directory what is named the same name as route, ASP.NET Framework searches view files in the directory.

So I have to be careful the directory names.
(I also can set routes like "localhost:59522/Api/Products")

Log (NLog)

I output log with NLog.

Install

  • NLog.Web ver.4.9.3

Configurations

To use NLog in ASP.NET Framework, I add configurations in Web.config.

Web.config

...
  <system.webServer> 
    <modules runAllManagedModulesForAllRequests="true"> 
      <add name="NLog" type="NLog.Web.NLogHttpModule, NLog.Web" />
    </modules>
  </system.webServer>
</configuration>

And I add nlog.config as same as .NET Core.

nlog.config

<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      autoReload="true">

    <targets>
        <target xsi:type="Console" name="outputconsole"
            layout="${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}" />

        <target xsi:type="File" name="outputfile" fileName="C:\tmp\logs\NetFrameworkSample\${date:format=yyyy}\${date:format=MM}\${shortdate}.log"
            layout="${longdate}|${event-properties:item=EventId_Id}|${uppercase:${level}}|${logger}|${message} ${exception:format=tostring}|url: ${aspnet-request-url}|action: ${aspnet-mvc-action}" />
    </targets>

    <rules>
        <logger name="*" minlevel="Debug" writeTo="outputconsole" />>
        <logger name="Microsoft.*" maxLevel="Info" final="true" />
        <logger name="*" minlevel="Debug" writeTo="outputfile" />
    </rules>
</nlog>

Create instances

I can create an instance by "LogManager.GetCurrentClassLogger()".

ProductsController.cs

using System.Web.Mvc;
using NLog;

namespace NetFrameworkSample.Controllers
{
    public class ProductsController: Controller
    {
        private readonly Logger _logger;
        public ProductsController()
        {
            _logger = LogManager.GetCurrentClassLogger();
        }
        [Route("")]
        public string GetMessage()
        {
            _logger.Debug("Hello");
            return "Hello world!";
        }
...

Now I can output logs like below.

2020-09-02 21:05:58.0307||DEBUG|NetFrameworkSample.Controllers.ProductsController|Hello |url: http://localhost/|action: 

DI (Dependency Injection)

ASP.NET Framework doesn't have DI by default.

I use Unity Container in this time.

I found I also could use "Microsoft.Extensions.DependencyInjection".
So in the future, I may try it.

Install

  • Unity.Container ver.5.11.8
  • Unity.Mvc ver.5.11.1

Register dependencies

IProductsService.cs

namespace NetFrameworkSample.Products
{
    public interface IProductsService
    {
    }
}

ProductsService.cs

namespace NetFrameworkSample.Products
{
    public class ProductsService: IProductsService
    {
    }
}

UnityConfig.cs

using System;
using Unity;
using NetFrameworkSample.Products;

namespace NetFrameworkSample
{
    /// <summary>
    /// Specifies the Unity configuration for the main container.
    /// </summary>
    public static class UnityConfig
    {
        #region Unity Container
        private static Lazy<IUnityContainer> container =
          new Lazy<IUnityContainer>(() =>
          {
              var container = new UnityContainer();
              RegisterTypes(container);

              container.RegisterType<IProductsService, ProductsService>(TypeLifetime.Scoped);

              return container;
          });
...

Inject

As same as "Microsoft.Extensions.DependencyInjection", Unity Container also use constructor injection.

ProductsController.cs

using System.Web.Mvc;
using NetFrameworkSample.Products;

namespace NetFrameworkSample.Controllers
{
    public class ProductsController: Controller
    {
        private readonly IProductsService _product;
        public ProductsController(IProductsService product)
        {
            _product = product;
        }
...

Register instance

When I want to inject specify instances like DbContext, I can use "RegisterInstance".

var sample = new ProductsService();
container.RegisterInstance<IProductsService>(sample, InstanceLifetime.PerContainer);              

Though I can inject NLog instance. but I shouldn't do that.
Because all of the class names of "logger" becomes "UnityConfig".

Type(Instance) lifetime

Unity Container has more types of lifetime controls than "Microsoft.Extensions.DependencyInjection".

Inject concrete classes

I can inject concrete classes without registration.
But their instances' life time is Transient.

So I must register explicit the classes what should be set other lifetime like DbContext(Scoped).

Posted on by:

masanori_msl profile

Masui Masanori

@masanori_msl

Programmer, husband, father I love C#, TypeScript, etc.

Discussion

pic
Editor guide