35
C# ON LINUX Our experience

C#on linux

Embed Size (px)

Citation preview

Page 1: C#on linux

C# ON LINUXOur experience

Page 2: C#on linux

Different naming

■ ASP.NET 5■ ASP.NET MVC 6■ ASP.NET vNext■ ASP.NET Core 1

Page 3: C#on linux

Visual studio changesThe Node Package Manager is fully integrated with Visual Studio to provide a more natural user workflow.

Page 4: C#on linux

GRUNT / GULP / BOWER Integration

Page 5: C#on linux

Task Runner Explorer

Page 6: C#on linux

Current status

■ ASP.NET 5 has been renamed to ASP.NET Core 1.0■ May 16, 2016 - Announcing ASP.NET Core RC2

■ https://docs.asp.net/en/latest/■ https://blogs.msdn.microsoft.com/webdev/

Page 7: C#on linux

Runtime platforms

■ Classical .Net Framework. As always, it can be run on windows.

■ .Net Core. This is cross-platform environment officially developed and supported by Microsoft. Can be installed on windows, linux and Mac.

■ Mono. This is open-source cross-platform port of .NET Framework for non-windows systems. It is not officially supported by Microsoft and probably slower than .Net Core.

Page 8: C#on linux

Web application■ No more XML configuration, configuration now in JSON

■ Project.json as common project definition file

■ Startup.cs as entry point of application

■ Microsoft.AspNet.Server.Kestrel as running web server

■ All usings are Nuget packages

■ No more required csproj file, all files will be included in build

■ Web application = self-hostable console-application

Page 9: C#on linux

Project.json{ "version": "1.0.0-*", "compilationOptions": { "emitEntryPoint": true }, "dependencies": { "Microsoft.AspNet.Server.Kestrel": "1.0.0-rc1-final", "Microsoft.AspNet.Mvc": "6.0.0-rc1-final", "Microsoft.AspNet.Mvc.WebApiCompatShim": "6.0.0-rc1-final", "Microsoft.Extensions.Logging": "1.0.0-rc1-final", "Microsoft.Extensions.Caching.Abstractions": "1.0.0-rc1-final", "Microsoft.Extensions.Caching.Memory": "1.0.0-rc1-final", "System.Console": "4.0.0-beta-23516", "System.Linq": "4.0.1-beta-23516", "Trkd.Dnx.Proxy": "1.0.0-*", "ActiveMashups.Core": "1.0.0-*", "System.Runtime": "4.0.21-beta-23516", "System.Collections": "4.0.11-beta-23516", "System.Linq.Queryable": "4.0.1-beta-23516", "System.Linq.Parallel": "4.0.1-beta-23516", "System.Threading.Tasks": "4.0.11-beta-23516", "System.Runtime.Serialization.Primitives": "4.1.0-beta-23516“ }, "commands": { "web": "Microsoft.AspNet.Server.Kestrel" }, "frameworks": { "dnxcore50": { } }, "exclude": [ "wwwroot", "node_modules" ], "publishExclude": [ "**.user", "**.vspscc" ], "scripts": { "postpublish": "%project:Directory%/postpublish/postpublish.bat" }}

"frameworks": {  "dnxcore50": { // .Net Core  },  "dnx461" :{ // mono  },  "net461" :{ // .Net Framework  }}

Page 10: C#on linux

Many platforms in-time development

Page 11: C#on linux
Page 12: C#on linux

Startup.cs public class Startup { public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv) { // Set up configuration sources. var builder = new ConfigurationBuilder().AddJsonFile("Configuration/AppSettings.json").AddEnvironmentVariables(); this.Configuration = builder.Build(); }

public IConfigurationRoot Configuration { get; set; }

public static void Main(string[] args) => WebApplication.Run<Startup>(args);

// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { // Add framework services. services.AddMvc();

}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddDebug();

app.UseStaticFiles();

app.UseMvc(); } }

Configuration loading

Services, filters, options, dependency injection, etc

Logging, middleware, routing

Page 13: C#on linux

Configuration loading public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv) { // Set up configuration sources. var builder = new ConfigurationBuilder() .AddJsonFile("Configuration/AppSettings.json") .AddJsonFile("Configuration/AmqpLoggingSettings.json") .AddEnvironmentVariables(); this.Configuration = builder.Build();

this.Configuration["WebRootPath"] = env.WebRootPath; this.Configuration["AppRootPath"] = appEnv.ApplicationBasePath;

}

RC1

public Startup(IHostingEnvironment env) { var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) .AddEnvironmentVariables(); Configuration = builder.Build(); }

RC2

Page 14: C#on linux

Services, filters, options, dependency injection…

// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { // Add framework services. services.AddMvc( mvcOptions => { mvcOptions.Filters.Add(typeof(GlobalExceptionInterseptor)); mvcOptions.Filters.Add(typeof(JsonWebTokenAuthentification)); });

ConfigureOptions(services);

ConfigureDependencyInjection(services); }

Page 15: C#on linux

Request filter public class JsonWebTokenAuthentification : ActionFilterAttribute { public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { // if AllowAnonymous attribute is present - skip other checks if (context.Filters.OfType<AllowAnonymousFilter>().Any()) { await next(); return; }

else {

context.Result = new HttpUnauthorizedResult();return;

}}

Page 16: C#on linux

Options model – load configurationhttps://docs.asp.net/en/latest/fundamentals/configuration.html#using-options-and-configuration-objects

{ "AmqpLoggerConfiguration": { "ServerAddress": "http://localhost:15672", "UserName": "guest", "Password": "guest", "VirtualHost": "%2f", "ExchangeName": "gelf.fanout", "Facility": “MyApplication" }}

AmqpLoggingSettings.json

public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv) { // Set up configuration sources. var builder = new ConfigurationBuilder() .AddJsonFile("Configuration/AppSettings.json") .AddJsonFile("Configuration/AmqpLoggingSettings.json") .AddEnvironmentVariables(); this.Configuration = builder.Build();

Page 17: C#on linux

Options model – bind to classes throught DI public class AmqpLoggerOptions { public string ServerAddress { get; set; }

public string UserName { get; set; }

public string Password { get; set; }

public string VirtualHost { get; set; }

public string ExchangeName { get; set; }

public string Facility { get; set; } }public void ConfigureServices(IServiceCollection services)

{services.AddOptions();

services.Configure<AmqpLoggerOptions>(Configuration.GetSection("AmqpLoggerConfiguration"));}

Startup.cs

Page 18: C#on linux

public class AmqpLogger : ILogger { private readonly RabbitMqLogger logger;

private readonly AmqpLoggerOptions amqpOptions;

public AmqpLogger(IOptions<AmqpLoggerOptions> options) { amqpOptions = options.Value; var rabbitOptions = Mapper.Map<RabbitMqLoggerOptions>(amqpOptions); logger = new RabbitMqLogger(rabbitOptions); }

public async Task<Guid> LogAsync(IActiveMashupsLogEntry logEntry) { var logMessage = CreateMessageWithDefaultFields(logEntry);

var loggingSucceed = await logger.LogMessage(logMessage);

if (!loggingSucceed) { throw new Exception("Failed to send logs to amqp"); }

return logMessage.Id; }

Options model - usage

Build-in DI

Page 19: C#on linux

Build-in Dependency Injectionhttps://docs.asp.net/en/latest/fundamentals/dependency-injection.html

public void ConfigureServices(IServiceCollection services) { // Add framework services. services.AddMvc( mvcOptions => { mvcOptions.Filters.Add(typeof(GlobalExceptionInterseptor)); mvcOptions.Filters.Add(typeof(JsonWebTokenAuthentification)); });

ConfigureOptions(services);

services.AddSingleton<AmqpLogger>(); services.AddSingleton<FileLogger>(); services.AddTransient<ILogger, AmqpAndFileLogger>(); services.AddSingleton<ITokensManager, TrkdTokensManager>(); }

Startup.cs

Page 20: C#on linux

Dependency Injection - Service Lifetimes

ASP.NET services can be configured with the following lifetimes:

■ Transient - Transient lifetime services are created each time they are requested. This lifetime works best for lightweight, stateless services.

■ Scoped - Scoped lifetime services are created once per request.

■ Singleton - Singleton lifetime services are created the first time they are requested (or when ConfigureServices is run if you specify an instance there) and then every subsequent request will use the same instance. If your application requires singleton behavior, allowing the services container to manage the service’s lifetime is recommended instead of implementing the singleton design pattern and managing your object’s lifetime in the class yourself.

Page 21: C#on linux

Logging, middleware, routing

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddDebug();

app.UseMiddleware<LogBadResponsesAsWarningsMiddleware>(); app.UseMiddleware<ContentLengthResponseHeaderWriterMiddleware>();

app.UseStaticFiles();

app.UseMvc(routes => { // add the new route here. routes.MapRoute(name: "areaRoute", template: "{area:exists}/{controller}/{action}/{id?}", defaults: new { controller = "Home", action = "Index" }); }); }

Startup.cs

Page 22: C#on linux

Middleware (ex HTTP Module)https://docs.asp.net/en/latest/fundamentals/middleware.html

Page 23: C#on linux

Middleware example public class ContentLengthResponseHeaderWriterMiddleware { private readonly RequestDelegate next;

public ContentLengthResponseHeaderWriter(RequestDelegate next) { this.next = next; }

public async Task Invoke(HttpContext context) { using (var buffer = new MemoryStream()) { var response = context.Response;

var bodyStream = response.Body; response.Body = buffer;

await this.next(context);

var lenght = buffer.Length;

if (lenght > 0) { response.Headers.Add("Content-Length", new[] { lenght.ToString() }); }

buffer.Position = 0; await buffer.CopyToAsync(bodyStream); } } }

Page 24: C#on linux

United controller[Area("Portfolios")][Route("[controller]")]public class PortfoliosController : Controller { public async Task<object> Get() { return await new GetPortfoliosWorkflow(this.HttpContext).ExecuteAsync(null); }

[HttpGet("{portfolioId}")] public async Task<object> Get(long portfolioId) { return await new GetSinglePortfolioWorkflow(this.HttpContext).ExecuteAsync(portfolioId); }

[HttpPost] public async Task<object> Post([FromBody]PortfolioViewModel portfolioViewModel) { var result = await new CreatePortfolioWorkflow(this.HttpContext).ExecuteAsync(portfolioViewModel); var locationHeader = HttpContext.Request.GetDisplayUrl() + "/" + result.PortfolioId; return Created(locationHeader, result); }

[HttpPut("{portfolioId}")] public async Task<object> Put(long portfolioId, [FromBody]PortfolioViewModel portfolioViewModel) { if (portfolioViewModel.PortfolioId != 0 && portfolioViewModel.PortfolioId != portfolioId) { return new BadRequestObjectResult("Portfolio id from url is not equal to portfolio id from request"); }

portfolioViewModel.PortfolioId = portfolioId; return await new UpdatePortfolioWorkflow(this.HttpContext).ExecuteAsync(portfolioViewModel); }}

Page 25: C#on linux

Kestrel

■ Kestrel is a cross-platform web server based on libuv, a cross-platform asynchronous I/O library.

■ You add support for Kestrel by including Microsoft.AspNet.Server.Kestrel in your project’s dependencies listed in project.json.

■ https://github.com/aspnet/KestrelHttpServer

https://docs.asp.net/en/latest/fundamentals/servers.html

Page 26: C#on linux

CLI Tools

■ The DNX Utility (DNU) tool is responsible for all operations involved with packages in your application.

■ Dotnet version manager (DNVM) is responsible for downloading and installation environment runtimes.

■ DNX is .NET Execution Environment that will execute your application.

Page 27: C#on linux

Dotnet version manager (DNVM)

You can install any environment that is available on platform you are using.Some useful cli commands:■ dnvm list - will show you list of available installer runtimes, their aliases and

current default runtime (marked by *)■ dnvm install - installs requested runtime (example - dnvm install latest -r coreclr -

acrh x64)■ dnvm use - selects specific runtime as default (examples - dnvm use default; dnvm use 1.0.0-rc1-update1 -r coreclr -acrh x64)

Page 28: C#on linux
Page 29: C#on linux

The DNX Utility (DNU)

■ DNU restore - restores nuget pakages■ DNU publish -  will package your application into a self-contained directory

that can be launched. It will create the following directory structureoutput/output/packagesoutput/appNameoutput/commandName.cmd

Note, that DNU can be unavailable if there is no runtime selected in DNVM (even if runtimes are installed).

Page 30: C#on linux

.NET Execution Environment (DNX)

In common case it's only used to run you app by execution from directory with project.json file:■ dnx web - for web app■ dnx run - for console app

Page 31: C#on linux

CLI tools from RC 1 to RC 2DNVM, DNU, DNX = > Dotnet

DNX command CLI command Descriptiondnx run dotnet run Run code from source.

dnu build dotnet build Build an IL binary of your code.

dnu pack dotnet pack Package up a NuGet package of your code.

dnx [command] (for example, "dnx web") N/A* In DNX world, run a command as defined in the project.json.

dnu install N/A* In the DNX world, install a package as a dependency.

dnu restore dotnet restore Restore dependencies specified in your project.json.

dnu publish dotnet publish Publish your application for deployment in one of the three forms (portable, portable with native and standalone).

dnu wrap N/A* In DNX world, wrap a project.json in csproj.

dnu commands N/A* In DNX world, manage the globally installed commands.

(*) - these features are not supported in the CLI by design.

Page 32: C#on linux

Known issues

■ Kestrel do not set content-length header in response.If your component needs this header (for example, java-based vert.x uses content-length to read message body), add middleware as workaround.■ While working with 3rd party datasources, especially with WCF SOAP

(IIS) services, server can send response with GZipped content + content-length (header) of GZipped body.

System libs fails to deserialize response body, they UZipping body, but reads it using content-length header of GZipped body.Thus, body can be read only by half of its contents.

Page 33: C#on linux

RC2, RTM and future

Page 34: C#on linux

QUESTIONS?

Page 35: C#on linux

Thanks!

MAKSIM VOLK Software Engineer  

Office: +375 17 389 0100 x 40673   Cell: +375 29 365 6597   Email: [email protected] Minsk, Belarus (GMT+3)   epam.com