Julian
Julian

Reputation: 36700

Create one Nuget Package for both ASP.NET 4 & ASP.NET Core 1.0

We have a library which works in ASP.NET 4 and uses HttpContext.Current. We are aware that we can't use HttpContext.Current in ASP.NET Core but should use IHttpContextAccessor.

How can we change this library so we both support ASP.NET 4 (by using HttpContext.Current) and ASP.NET Core (IHttpContextAccessor) and packaged as one NuGet package?

Upvotes: 4

Views: 1088

Answers (3)

Julian
Julian

Reputation: 36700

The answer is that it isn't possible. The same that you can't create a one NuGet package to target both MVC1 and MVC2 (both not as Nuget package available)

Of course you could use assembly bindings, but only when there aren't breaking changes.

Microsoft is naming there ASP.NET Core packages with "AspNetCore", e.g. Microsoft.AspNetCore.Mvc vs Microsoft.AspNet.Mvc

Upvotes: 0

qbik
qbik

Reputation: 5908

If you define two target frameworks in project.json (like aspnetcore and net451), then two separate dlls will be created and put into lib/aspnetcore and lib/net451 inside the nuget package. Nuget will know which one to use, based on the target framework of the project that consumes the package.

Now, you can use #ifdef statements in your library to distinguish, which parts of code should be compiled only for specified platforms.

A simple example:

using System;

namespace MyLib
{
#if NETFX
    using HttpContext = System.Web.HttpContext;
#elif NETCORE
    using HttpContext = Microsoft.AspNetCore.Http.HttpContext;
#endif
    public class MyClass
    {
        public string GetUserAgent(HttpContext ctx)
        {
            return ctx.Request.Headers["User-Agent"];
        }
        public void WriteToResponse(HttpContext ctx, string text)
        {
#if NETFX
            ctx.Response.Output.Write(text);
#elif NETCORE
            var bytes = System.Text.Encoding.UTF8.GetBytes(text);
            ctx.Response.Body.Write(bytes, 0, bytes.Length);
#endif
        }
    }
}

To make these switches work, you have to define framework-specific constants in project.json:

  "frameworks": {
    "netcoreapp1.0": {
      "buildOptions": {
        "define": [
          "NETCORE"
        ]
      },
      "dependencies": {
        "Microsoft.NETCore.App": {
          "type": "platform",
          "version": "1.0.0"
        },
        "Microsoft.AspNetCore.Http": "1.0.0"
      }
    },
    "net451": {
      "buildOptions": {
        "define": [
          "NETFX"
        ]
      },
      "dependencies": {
        "Microsoft.AspNet.WebApi": "5.2.3"
      },
      "frameworkAssemblies": {
        "System.Web": "4.0.0.0"
      }
    }
  },

Note that both frameworks have different set of dependencies. HttpContext here will be a different type when compiled for netcoreapp and for net451. Fortunatelly, these types have very similar methods, so often times you can use them in the same way, without #ifdefs (like in GetUserAgent). Other times, you won't be able to avoid it (like in WriteToResponse).

Now, in a AspNet Core project you could use this library like this:

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        app.Use(async (ctx, next) => {
            var lib = new MyLib.MyClass();
            var msg = lib.GetUserAgent(ctx);
            lib.WriteToResponse(ctx, "user agent is: " + msg);
        });
    }

And in old AspNet MVC:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        var lib = new MyLib.MyClass();
        var msg = lib.GetUserAgent(System.Web.HttpContext.Current);
        lib.WriteToResponse(System.Web.HttpContext.Current, "user agent is: " + msg);
        return new HttpStatusCodeResult(200);
    }
}

Full source code on github.

Upvotes: 0

The Shooter
The Shooter

Reputation: 733

One possibility:

Create three libraries.

  1. Uses HttpContext
  2. Uses IHttpContextAccessor
  3. Factory which loads from one of these library.

1 and 2 have the same assembly name and Factory just references either of the two.

At a time there will be only two assemblies present as reference in the folder. Factory and one among the other two in references.

Create a nuget package with all the three assemblies. 1 and 2 go in separate folders lets call it aspnet4 and aspnet5.

Package also contains target file which is used by aspnet projects. The target file copies factory and one of the libraries into the references folder depending upon a property present in aspnet project. This property could be something like aspnet=aspnet5.

This might be a too big thing and simpler solution might exist. I will think of a simpler solution.

Sample Code

//4. AspNetUsageBaseClass Assembly
namespace AspNetUsage
{
    //rest of code

    public abstract AspNetUsageBaseClass
    {
        //rest of code
        public abstract void HttpContextIHttpContextAccessor();
    }
}

//1. AspNetUsage Assembly, Reference AspNetUsageBaseClass Assembly
using AspNetUsage;
public class AspNetUsage:AspNetUsageBaseClass
{
    public override void HttpContextIHttpContextAccessor()
    {
        //HttpContext based code
    }
}

//2. AspNetUsage Assembly, Reference AspNetUsageBaseClass Assembly
using AspNetUsage;
public class AspNetUsage:AspNetUsageBaseClass
{
    public override void HttpContextIHttpContextAccessor()
    {
        //IHttpContextAccessor based code
    }
}

Now there will be in total 3 assemblies. One base class assembly. Two others implementing specific functionality. Inside nuget package structure should be:

AspNetUsageBaseClass.dll - base class assembly

AspNet4\AspNetUsage.dll - HttpContext assembly

AspNet5OrCore\AspNetUsage.dll - IHttpContextAccessor assembly

AspNet.target - This will be imported in asp net project and depending on the value of the property extract one of the two dlls.

Inside AspNet project you would have:

AspNetUsageBaseClass context = new AspNetUsage();
context.HttpContextIHttpContextAccessor();

This is not Factory pattern based but this can be extended to Factory pattern based with some modification.

There might be some mistakes as I have used notepad to type the logic in absence of VS.

Upvotes: 1

Related Questions