65.9K
CodeProject 正在变化。 阅读更多。
Home

在 Linux 上运行 ASPNET 5 和 .NET Core

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.82/5 (18投票s)

2015 年 12 月 22 日

CPOL

6分钟阅读

viewsIcon

72903

我最近一直在听很多关于新的 ASPNET 5 (vNext) 和 .NET Core 的信息。

我最近一直在听很多关于新的 ASPNET 5 (vNext) 和 .NET Core 的信息。然而,beta 版本之间频繁的破坏性更改让我不想去尝试。随着 ASPNET 5 和 .NET Core 进入 release candidate 阶段,并且 1.0 版本近在眼前,我认为是时候去尝试一下了。

ASPNET 5 和 .NET Core 的基本概念

用一句话概括 ASPNET 5 和 .NET Core,为接下来的内容设定背景。

ASP.NET 5 是一个精简且可组合的框架,用于构建 Web 和云应用程序。ASP.NET 5 是完全开源的。

.NET Core 是 .NET 的跨平台实现,主要由 ASP.NET 5 工作负载驱动,但也源于对现代运行时(它应该是模块化的,其功能和库可以根据应用程序的需求进行精选)的需求和愿望。

选择操作系统

ASPNET 5 和 CoreCLR 可以在 Windows、Mac 或 Linux 上运行。我选择在 Linux 上尝试,因为它对我来说是最有趣的选择。我认为可以安全地假设 ASPNET 5 在 Windows 上的运行会比在其他操作系统上更顺畅。此外,如果我能在其他操作系统上让它运行起来,那么它在 Windows 环境中应该会运行得更好。Mac 很有趣,但它的主要用例是作为开发环境。而 Linux 既可以作为可行的开发环境,也可以作为吸引人的托管选项。在我的测试中,我使用了 Ubuntu Server 14.04,因为它是最受欢迎的 Linux 发行版之一,而且易于使用。

在 Mono Runtime 和 .NET Core Runtime 之间选择

开发者在新生态系统中将拥有更多选择。即使在 Linux 上,也有两个 .NET 运行时可供选择:mono runtimecoreclr。Mono 是 Xamarin 的 .NET 框架的开源实现,它有自己的 mono runtime。它已经存在了 10 多年,支持(部分)Web 和桌面 .NET 应用程序在 Linux 上运行。.NET Core 是 Microsoft 的 .NET 跨平台实现,coreclrt 是它的运行时。

在 Linux 上选择 mono runtime 或 coreclr,就像在 Windows 上选择 full CLR 和 coreclr 一样。Full CLR 支持当前所有第三方库。CoreCLR 虽然更精简且模块化,但需要库专门针对 .NET Core。因此,.NET Core 支持的库数量初期会有限,但随着采用率的提高,应该会随着时间的推移而改善。

ASPNET 5 的主要目标之一是构建轻量级且可组合的框架,我认为这是值得追求的。我将尝试 coreclr。

设置环境

使用 asp.net 网站上的入门页面,安装必要的组件非常容易。

安装 .NET 版本管理器 (DNVM)

dnvm 是 .NET 版本管理器,它是一套命令行工具,用于更新和配置要使用的 .NET 运行时。

curl -sSL https://raw.githubusercontent.com/aspnet/Home/dev/dnvminstall.sh | DNX_BRANCH=dev sh && source ~/.dnx/dnvm/dnvm.sh

 

安装 .NET 执行环境 (DNX)

.NET 执行环境包含引导和运行应用程序所需的代码。这包括编译系统、SDK 工具和原生 CLR 主机等。

#install dnx prerequisite
sudo apt-get install libunwind8 gettext libssl-dev libcurl4-openssl-dev zlib1g libicu-dev uuid-dev

#use dnvm to install .net core
dnvm upgrade -r coreclr

此时,如果您运行 dnvm list 列出已安装的运行时,您应该会看到类似以下内容:

Active Version              Runtime Architecture OperatingSystem Alias
------ -------              ------- ------------ --------------- -----
  *    1.0.0-rc1-update1    coreclr x64          linux           default

请注意,只有 coreclr 运行时已安装。asp.net 网站上的入门页面也有安装 mono runtime 的说明。由于我只对尝试 coreclr 感兴趣,不安装 mono runtime 反而更容易验证它在 coreclr 上是否正常工作。

安装 Libuv

Libuv 是一个多平台异步 IO 库,被 Kestrel 使用,Kestrel 是一个用于托管 ASP.NET 5 Web 应用程序的跨平台 HTTP 服务器。

sudo apt-get install make automake libtool curl
curl -sSL https://github.com/libuv/libuv/archive/v1.4.2.tar.gz | sudo tar zxfv - -C /usr/local/src
cd /usr/local/src/libuv-1.4.2
sudo sh autogen.sh
sudo ./configure
sudo make
sudo make install
sudo rm -rf /usr/local/src/libuv-1.4.2 && cd ~/
sudo ldconfig

运行 HelloMVC 示例项目

环境设置好后,运行 Hello World MVC 项目也很容易。

#install git if it's not already installed
sudo apt-get install git

#clone the aspnet 5 repo
git clone https://github.com/aspnet/home

#change directory into the HelloMvc sample
#1.0.0-rc1-update matches the runtime version and could be different for you depends on what runtime you installed
cd home/samples/1.0.0-rc1-update1/HelloMvc/

#restore the packages required by HelloMvc project
dnu restore

#start the app
dnx web

如果您浏览到 http://my-ip-address:5004,您应该会看到运行中的示例应用程序。

HelloMvc

dnu restore 命令

dnu restore 命令会查看应用程序的依赖项,下载它们,并将它们添加到应用程序的包目录中。依赖项在 project.json 文件中定义。在 HelloMvc 项目中,依赖项是:

"dependencies": {
"Microsoft.AspNet.Server.Kestrel": "1.0.0-rc1-final",
"Microsoft.AspNet.IISPlatformHandler": "1.0.0-rc1-final",
"Microsoft.AspNet.Diagnostics": "1.0.0-rc1-final",
"Microsoft.AspNet.Mvc": "6.0.0-rc1-final",
"Microsoft.Extensions.Logging.Console": "1.0.0-rc1-final"
},

dnx web 命令

dnx web 命令会运行应用程序。

在早期文档中,您可能会看到对 dnx kestrel 命令的引用。如果您尝试运行 dnx kestrel 命令。您将收到以下错误消息:“Error: Unable to load application or execute command ‘kestrel’. Available commands: web.”原因是 webkestrel 是 special commands,需要定义在 project.json 中。在 HelloMvc 示例的最新版本中,只定义了 web 命令。在该示例的早期 beta 版本中,同时定义了 webkestrel

在 1.0.0-rc1-update1/HelloMvc 目录中打开 HelloMvc 项目的最新版本 project.json 文件,这里只定义了 web 命令。还要注意 server.urls 参数,其值为 http://*:5004,这解释了为什么示例会在 5004 端口上运行。

commands: {
 web: Microsoft.AspNet.Server.Kestrel --server.urls http://*:5004
},

打开 1.0.0-beta7/HelloMvc 中的 HelloMvc 项目的早期版本 project.json 文件,您将看到同时定义了 webkestrel 命令。

commands: {
 web: Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.WebListener --server.urls https://:5001,
 kestrel: Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.Kestrel --server.urls https://:5004
},

运行 MusicStore 示例项目

让 HelloMvc 示例运行起来很好,但我需要看到一个非简单的应用程序运行起来,才能投入更多时间。我的下一个测试是让 AspNet MusicStore 示例项目运行起来。这个项目包含身份验证和 EF7,使其成为一个不错的测试对象。该项目使用 InMemory provider 进行数据库访问,因为目前只有 SqlServer、SqlLite 和 InMemory provider 可用。

遵循与 HelloMvc 项目类似的步骤。

#clone the musicstore repo
git clone https://github.com/aspnet/MusicStore.git

#change the directory into the sample project
cd MusicStore/src/MusicStore

#restore package
dnu restore

#run the application
dnx web

此时,我在控制台中看到以下内容,这表明应用程序正在运行。看起来很有希望。

ubuntu@:~/MusicStore/src/MusicStore$ dnx web
Hosting environment: Production
Now listening on: https://:5000
Application started. Press Ctrl+C to shut down.

然而,当我尝试访问它时,会收到以下看起来令人恐惧的异常。

fail: Microsoft.AspNet.Server.Kestrel[13]
An unhandled exception was thrown by the application.
System.InvalidOperationException: Unable to resolve service for type 'Microsoft.Extensions.CompilationAbstractions.ILibraryExporter' while attempting to activate 'Microsoft.AspNet.Mvc.Razor.Compilation.RoslynCompilationService'.
at Microsoft.Extensions.DependencyInjection.ServiceLookup.Service.PopulateCallSites(ServiceProvider provider, ISet`1 callSiteChain, ParameterInfo[] parameters, Boolean throwIfCallSiteNotFound)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.Service.CreateCallSite(ServiceProvider provider, ISet`1 callSiteChain)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetResolveCallSite(IService service, ISet`1 callSiteChain)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetServiceCallSite(Type serviceType, ISet`1 callSiteChain)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.Service.PopulateCallSites(ServiceProvider provider, ISet`1 callSiteChain, ParameterInfo[] parameters, Boolean throwIfCallSiteNotFound)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.Service.CreateCallSite(ServiceProvider provider, ISet`1 callSiteChain)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetResolveCallSite(IService service, ISet`1 callSiteChain)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetServiceCallSite(Type serviceType, ISet`1 callSiteChain)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.Service.PopulateCallSites(ServiceProvider provider, ISet`1 callSiteChain, ParameterInfo[] parameters, Boolean throwIfCallSiteNotFound)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.Service.CreateCallSite(ServiceProvider provider, ISet`1 callSiteChain)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetResolveCallSite(IService service, ISet`1 callSiteChain)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetServiceCallSite(Type serviceType, ISet`1 callSiteChain)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.Service.PopulateCallSites(ServiceProvider provider, ISet`1 callSiteChain, ParameterInfo[] parameters, Boolean throwIfCallSiteNotFound)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.Service.CreateCallSite(ServiceProvider provider, ISet`1 callSiteChain)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetResolveCallSite(IService service, ISet`1 callSiteChain)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetServiceCallSite(Type serviceType, ISet`1 callSiteChain)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.CreateServiceAccessor(Type serviceType, ServiceProvider serviceProvider)
at System.Collections.Concurrent.ConcurrentDictionaryExtensions.GetOrAdd[TKey,TValue,TArg](ConcurrentDictionary`2 dictionary, TKey key, Func`3 valueFactory, TArg arg)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderExtensions.GetRequiredService[T](IServiceProvider provider)
at Microsoft.AspNet.Mvc.Razor.Internal.MvcRazorMvcViewOptionsSetup.ConfigureMvc(IServiceProvider serviceProvider, MvcViewOptions options)
at Microsoft.Extensions.OptionsModel.OptionsCache`1.CreateOptions()
at System.Threading.LazyInitializer.EnsureInitializedCore[T](T& target, Boolean& initialized, Object& syncLock, Func`1 valueFactory)
at Microsoft.AspNet.Mvc.ViewEngines.CompositeViewEngine..ctor(IOptions`1 optionsAccessor)
at lambda_method(Closure , ServiceProvider )
at Microsoft.Extensions.DependencyInjection.ServiceProviderExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderExtensions.GetRequiredService[T](IServiceProvider provider)
at Microsoft.AspNet.Mvc.ViewResult.<ExecuteResultAsync>d__26.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNet.Mvc.Controllers.FilterActionInvoker.<InvokeResultAsync>d__45.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNet.Mvc.Controllers.FilterActionInvoker.<InvokeResultFilterAsync>d__44.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNet.Mvc.Controllers.FilterActionInvoker.<InvokeAllResultFiltersAsync>d__43.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNet.Mvc.Controllers.FilterActionInvoker.<InvokeResourceFilterAsync>d__38.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNet.Mvc.Controllers.FilterActionInvoker.<InvokeAsync>d__33.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNet.Mvc.Infrastructure.MvcRouteHandler.<InvokeActionAsync>d__8.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNet.Builder.RouterMiddleware.<Invoke>d__4.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNet.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNet.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNet.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNet.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNet.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNet.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNet.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNet.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNet.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNet.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNet.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNet.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNet.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNet.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNet.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNet.Authentication.AuthenticationMiddleware`1.<Invoke>d__18.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNet.Session.SessionMiddleware.<Invoke>d__8.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNet.Session.SessionMiddleware.<Invoke>d__8.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNet.Diagnostics.ExceptionHandlerMiddleware.<Invoke>d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNet.Diagnostics.ExceptionHandlerMiddleware.<Invoke>d__6.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNet.Diagnostics.StatusCodePagesMiddleware.<Invoke>d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNet.Hosting.Internal.RequestServicesContainerMiddleware.<Invoke>d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.AspNet.Hosting.Internal.HostingApplication.<ProcessRequestAsync>d__7.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at Microsoft.AspNet.Server.Kestrel.Http.Frame`1.<RequestProcessingAsync>d__3.MoveNext()

经过一番谷歌搜索,解决方案很简单。该示例需要最新的运行时。运行以下命令将其更新到最新版本。

#upgrade the runtime to latest version
#-r specifies the runtime we are upgrading is coreclr
#-u use the unstable feed to get the latest
dnvm upgrade -u -r coreclr

#verify the version
dnvm list

#dnvm list output indicates that we have version 1.0.0-rc2-16319 and it's the active runtime
Active Version              Runtime Architecture OperatingSystem Alias

------ -------              ------- ------------ --------------- -----

       1.0.0-rc1-update1    coreclr x64          linux           

  *    1.0.0-rc2-16319      coreclr x64          linux           default

再次运行 dnx web 命令并访问网站。太棒了!我看到了音乐商店的首页。浏览网站和添加商品到购物车也正在工作。

MusicStore

结论

在这篇博文中,我们在 Linux 上配置了 .net 运行时环境。我们还设置了 HelloMvc 和 MusicStore 示例应用程序在 coreclr 运行时上运行。虽然遇到了一些小波折,但总体体验是愉快的。

Core CLR 上可用的库仍然有限。但看起来很快就会有更多库可用。快速查看我计划使用的一些库,它们已经有了一些支持。

参考文献

 

© . All rights reserved.