使用 Docker 时,开发人员会创建一个应用或服务,并将它及其依赖项打包到一个容器映像中。 映像是应用或服务及其配置和依赖项的静态表示形式。 若要运行应用或服务,应用的映像会实例化,以创建一个在 Docker 主机上运行的容器。 最初,会在开发环境或 PC 中测试容器。 开发人员应将映像存储在注册表中,该注册表可充当映射库并在部署到生产业务流程协调程序时使用。 Docker 通过 Docker 中心维护公共注册表;其他供应商为不同映像集合提供注册表,包括 Azure 容器注册表。 或者,企业可以拥有一个本地专用注册表,用于其 Docker 映像。 图 2-4 显示了 Docker 中的映像和注册表与其他组件相关联的方式。 还显示了供应商的多个注册表产品/服务。 图 2-4。 Docker 术语和概念的分类 将映射存储到注册表中可存储静态和不可变的应用程序,包括其在框架级别的所有依赖项。 然后,这些映像可部署到多种环境中,并进行版本控制,从而提供一致的部署单元。 无论是托管在本地还是托管在云中,在下列情况下都建议使用专用映像注册表: 由于保密性,不能公开分享映像。 在映像和所选部署环境之间,希望网络延迟保持最低。 例如,如果生产环境是 Azure 云,为实现最低的网络延迟,可将映像存储在 Azure 容器注册表中。 同样,如果生产环境是在本地,便需要使本地 Docker 信任的注册表在相同的本地网络中可用。 from:https://docs.microsoft.com/zh-cn/dotnet/standard/microservices-architecture/container-docker-introduction/docker-containers-images-registries
View Details本节列出了在深入了解 Docker 之前应熟悉的术语和定义。 有关进一步的定义,请参阅 Docker 提供的详细术语表。 容器映像:包含创建容器所需的所有依赖项和信息的包。 映像包括所有依赖项(例如框架),以及容器运行时使用的部署和执行配置。 通常情况下,映像派生自多个基础映像,这些基础映像是堆叠在一起形成容器文件系统的层。 创建后,映像不可变。 Dockerfile:包含有关如何生成 Docker 映像的说明的文本文件。 与批处理脚本相似,首先第一行将介绍基础映像,然后是关于安装所需程序、复制文件等操作的说明,直至获取所需的工作环境。 生成:基于其 Dockerfile 提供的信息和上下文生成容器映像的操作,以及生成映像的文件夹中的其他文件。 可以使用 Docker 的“docker 生成”命令生成映像。 容器:Docker 映像的实例。 容器表示单个应用程序、进程或服务的执行。 它由 Docker 映像的内容、执行环境和一组标准指令组成。在缩放服务时,可以从相同的映像创建多个容器实例。 或者,批处理作业可以从同一个映像创建多个容器,向每个实例传递不同的参数。 卷:提供一个容器可以使用的可写文件系统。 由于映像只可读取,而多数程序需要写入到文件系统,因此卷在容器映像顶部添加了一个可写层,这样程序就可以访问可写文件系统。 程序并不知道它正在访问的是分层文件系统,此文件系统就是往常的文件系统。 卷位于主机系统中,由 Docker 管理。 标记:可以应用于映像的标记或标签,以便可以识别同一映像的不同映像或版本(具体取决于版本号或目标环境)。 多阶段生成:Docker 17.05 或更高版本的一个功能,可帮助减小最终映像的大小。 概括来说,借助多阶段生成,可以使用一个包含 SDK 的大型基础映像(以此为例)编译和发布应用程序,然后使用发布文件夹和一个小型仅运行时基础映像生成一个更小的最终映像 存储库 (repo):相关的 Docker 映像集合,带有指示映像版本的标记。 某些存储库包含特定映像的多个变量,例如包含 SDK(较重)的映像,包含唯一运行时(较轻)的映像,等等。这些变量可以使用标记进行标记。 单个存储库中可包含平台变量,如 Linux 映像和 Windows 映像。 注册表:提供存储库访问权限的服务。 大多数公共映像的默认注册表是 Docker 中心(归作为组织的 Docker 所有)。 注册表通常包含来自多个团队的存储库。 公司通常使用私有注册表来存储和管理其创建的映像。 另一个示例是 Azure 容器注册表。 多体系结构映像:就多体系结构而言,它是一种根据运行 Docker 的平台简化相应映像选择的功能,例如当 Dockerfile 从注册表请求基础映像 FROM microsoft/dotnet:2.1-sdk 时,实际上它会获得 2.1-sdk-nanoserver-1709、2.1-sdk-nanoserver-1803 或 2.1-sdk-alpine,具体取决于操作系统和运行 Docker 的版本。 Docker Hub:上传并使用映像的公共注册表。 Docker 中心提供 Docker 映像托管、公共或私有注册表,生成触发器和 Web 挂钩,以及与 GitHub 和 Bitbucket 集成。 Azure 容器注册表:用于在 Azure 中使用 Docker 映像及其组件的公共资源。 这提供了接近 Azure 中部署的注册表,授予控制访问权限,使其可以使用 Azure Active Directory 组和权限。 Docker 受信任注册表 (DTR):Docker 注册表服务(来自 Docker),可以安装在本地,因此它存在于组织的数据中心和网络中。 这对于应该在企业内部管理的私有映像来说很方便。 Docker 受信任注册表是 Docker 数据中心产品的一部分。 有关详细信息,请参阅 Docker 受信任注册表 […]
View DetailsDocker 是一种开源项目,用于将应用程序自动部署为可在云或本地运行的便携式独立容器。 Docker 也是一家公司,它与云、Linux 和 Windows 供应商(包括 Microsoft)协作,致力于推广和发展这项技术。 图 2-2。 Docker 在混合云的所有层部署容器 Docker 映像容器可以在 Linux 和 Windows 上本机运行。 但是,Windows 映像仅能在 Windows 主机上运行,Linux 映像可以在 Linux 主机和 Windows 主机上运行(到目前为止,使用 Hyper-V Linux VM),其中主机是指服务器或 VM。 开发人员可以在 Windows、Linux 或 macOS 上使用开发环境。 在开发计算机上,开发人员运行部署了 Docker 映像(包括应用及其依赖项)的 Docker 主机。 在 Linux 或 Mac 上进行开发的开发人员使用基于 Linux 的 Docker 主机,并且他们可以仅为 Linux 容器创建映像。 (在 Mac 上进行开发的开发人员可以从 macOS 中编辑代码或运行 Docker CLI,但截至撰写本文时,容器不在 macOS 上直接运行。)在 Windows 上进行开发的开发人员可以为 Linux 或 Windows 容器创建映像。 为了在开发环境中承载容器,并提供其他开发人员工具,Docker 为 Windows 或 macOS 提供了 Docker 社区版 (CE)。 这些产品安装了承载容器所需的 VM(Docker 主机)。 Docker 还提供 Docker 企业版 (EE),该版本专为企业开发而设计,供生成、交付和在生产中运行大型业务关键型应用程序的 IT 团队使用。 若要运行 Windows 容器,有两种类型的运行时可供使用: Windows Server 容器通过进程和命名空间隔离技术提供应用程序隔离。 Windows Server 容器与容器主机和主机上运行的所有容器共享内核。 Hyper-V 容器通过在高度优化的虚拟机中运行各容器来扩展 Windows Server 容器提供的隔离。 在此配置中,容器主机的内核不与 Hyper-V […]
View Details容器化是软件开发的一种方法,通过该方法可将应用程序或服务、其依赖项及其配置(抽象化为部署清单文件)一起打包为容器映像。 容器化应用程序可以作为一个单元进行测试,并可以作为容器映像实例部署到主机操作系统 (OS)。 就像船只、火车或卡车运输集装箱而不论其内部的货物一样,软件容器充当软件部署的标准单元,其中可以包含不同的代码和依赖项。 按照这种方式容器化软件,开发人员和 IT 专业人员只需进行极少修改或不修改,即可将其部署到不同的环境。 容器还会在共享 OS 上将应用程序彼此隔离开。 容器化应用程序在容器主机上运行,而容器主机在 OS(Linux 或 Windows)上运行。因此,容器的占用比虚拟机 (VM) 映像小得多。 每个容器可以运行整个 Web 应用或服务,如图 2-1 所示。 在此示例中,Docker 主机是容器主机,而 App1、App2、Svc 1 和 Svc 2 是容器化应用程序或服务。 图 2-1. 在一个容器主机上运行多个容器 容器化的另一个优势在于可伸缩性。 通过为短期任务创建新容器,可以快速扩大。 从应用程序的角度来看,实例化映像(创建容器)类似于实例化 服务或 Web 应用等进程。 但出于可靠性考虑,在多个主机服务器上运行同一映像的多个实例时,通常要使每个容器(映像实例)在不同容错域中的不同主机服务器或 VM 中运行。 总而言之,容器在整个应用程序生命周期工作流中提供以下优点:隔离性、可移植性、灵活性、可伸缩性和可控性。 最重要的优点是可在开发和运营之间提供隔离。 from:https://docs.microsoft.com/zh-cn/dotnet/standard/microservices-architecture/container-docker-introduction/index
View Details有了上一篇《.NET Core玩转机器学习》打基础,这一次我们以纽约出租车费的预测做为新的场景案例,来体验一下回归模型。 场景概述 我们的目标是预测纽约的出租车费,乍一看似乎仅仅取决于行程的距离和时长,然而纽约的出租车供应商对其他因素,如额外的乘客数、信用卡而不是现金支付等,会综合考虑而收取不同数额的费用。纽约市官方给出了一份样本数据。 确定策略 为了能够预测出租车费,我们选择通过机器学习建立一个回归模型。使用官方提供的真实数据进行拟合,在训练模型的过程中确定真正能影响出租车费的决定性特征。在获得模型后,对模型进行评估验证,如果偏差在接受的范围内,就以这个模型来对新的数据进行预测。 解决方案 创建项目 看过上一篇文章的读者,就比较轻车熟路了,推荐使用Visual Studio 2017创建一个.NET Core的控制台应用程序项目,命名为TaxiFarePrediction。使用NuGet包管理工具添加对Microsoft.ML的引用。 准备数据集 下载训练数据集taxi-fare-train.csv和验证数据集taxi-fare-test.csv,数据集的内容类似为:
1 2 3 4 5 6 7 |
vendor_id,rate_code,passenger_count,trip_time_in_secs,trip_distance,payment_type,fare_amount VTS,1,1,1140,3.75,CRD,15.5 VTS,1,1,480,2.72,CRD,10.0 VTS,1,1,1680,7.8,CSH,26.5 VTS,1,1,600,4.73,CSH,14.5 VTS,1,1,600,2.18,CRD,9.5 ... |
对字段简单说明一下: 字段名 含义 说明 vendor_id 供应商编号 特征值 rate_code 比率码 特征值 passenger_count 乘客人数 特征值 trip_time_in_secs 行程时长 特征值 trip_distance 行程距离 特征值 payment_type 支付类型 特征值 fare_amount 费用 目标值 在项目中添加一个Data目录,将两份数据集复制到该目录下,对文件属性设置“复制到输出目录”。 定义数据类型和路径 首先声明相关的包引用。
1 2 3 4 5 6 7 8 9 |
using System; using Microsoft.ML.Models; using Microsoft.ML.Runtime; using Microsoft.ML.Runtime.Api; using Microsoft.ML.Trainers; using Microsoft.ML.Transforms; using System.Collections.Generic; using System.Linq; using Microsoft.ML; |
在Main函数的上方定义一些使用到的常量。
1 2 3 4 |
const string DataPath = @".\Data\taxi-fare-train.csv"; const string TestDataPath = @".\Data\taxi-fare-test.csv"; const string ModelPath = @".\Models\Model.zip"; const string ModelDirectory = @".\Models"; |
接下来定义一些使用到的数据类型,以及和数据集中每一行的位置对应关系。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
public class TaxiTrip { [Column(ordinal: "0")] public string vendor_id; [Column(ordinal: "1")] public string rate_code; [Column(ordinal: "2")] public float passenger_count; [Column(ordinal: "3")] public float trip_time_in_secs; [Column(ordinal: "4")] public float trip_distance; [Column(ordinal: "5")] public string payment_type; [Column(ordinal: "6")] public float fare_amount; } public class TaxiTripFarePrediction { [ColumnName("Score")] public float fare_amount; } static class TestTrips { internal static readonly TaxiTrip Trip1 = new TaxiTrip { vendor_id = "VTS", rate_code = "1", passenger_count = 1, trip_distance = 10.33f, payment_type = "CSH", fare_amount = 0 // predict it. actual = 29.5 }; } |
创建处理过程 创建一个Train方法,定义对数据集的处理过程,随后声明一个模型接收训练后的结果,在返回前把模型保存到指定的位置,以便以后直接取出来使用不需要再重新训练。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
public static async Task<PredictionModel<TaxiTrip, TaxiTripFarePrediction>> Train() { var pipeline = new LearningPipeline(); pipeline.Add(new TextLoader<TaxiTrip>(DataPath, useHeader: true, separator: ",")); pipeline.Add(new ColumnCopier(("fare_amount", "Label"))); pipeline.Add(new CategoricalOneHotVectorizer("vendor_id", "rate_code", "payment_type")); pipeline.Add(new ColumnConcatenator("Features", "vendor_id", "rate_code", "passenger_count", "trip_distance", "payment_type")); pipeline.Add(new FastTreeRegressor()); PredictionModel<TaxiTrip, TaxiTripFarePrediction> model = pipeline.Train<TaxiTrip, TaxiTripFarePrediction>(); if (!Directory.Exists(ModelDirectory)) { Directory.CreateDirectory(ModelDirectory); } await model.WriteAsync(ModelPath); return model; } |
评估验证模型 创建一个Evaluate方法,对训练后的模型进行验证评估。
1 2 3 4 5 6 7 8 9 |
public static void Evaluate(PredictionModel<TaxiTrip, TaxiTripFarePrediction> model) { var testData = new TextLoader<TaxiTrip>(TestDataPath, useHeader: true, separator: ","); var evaluator = new RegressionEvaluator(); RegressionMetrics metrics = evaluator.Evaluate(model, testData); // Rms should be around 2.795276 Console.WriteLine("Rms=" + metrics.Rms); Console.WriteLine("RSquared = " + metrics.RSquared); } |
预测新数据 定义一个被用于预测的新数据,对于各个特征进行恰当地赋值。
1 2 3 4 5 6 7 8 9 10 11 12 |
static class TestTrips { internal static readonly TaxiTrip Trip1 = new TaxiTrip { vendor_id = "VTS", rate_code = "1", passenger_count = 1, trip_distance = 10.33f, payment_type = "CSH", fare_amount = 0 // predict it. actual = 29.5 }; } |
预测的方法很简单,prediction即预测的结果,从中打印出预测的费用和真实费用。
1 2 3 |
var prediction = model.Predict(TestTrips.Trip1); Console.WriteLine("Predicted fare: {0}, actual fare: 29.5", prediction.fare_amount); |
运行结果 到此我们完成了所有的步骤,关于这些代码的详细说明,可以参看《Tutorial: Use ML.NET to Predict New York Taxi Fares (Regression)》,只是要注意该文中的部分代码有误,由于使用到了C# 7.1的语法特性,本文的代码是经过了修正的。完整的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
using System; using Microsoft.ML.Models; using Microsoft.ML.Runtime; using Microsoft.ML.Runtime.Api; using Microsoft.ML.Trainers; using Microsoft.ML.Transforms; using System.Collections.Generic; using System.Linq; using Microsoft.ML; using System.Threading.Tasks; using System.IO; namespace TaxiFarePrediction { class Program { const string DataPath = @".\Data\taxi-fare-train.csv"; const string TestDataPath = @".\Data\taxi-fare-test.csv"; const string ModelPath = @".\Models\Model.zip"; const string ModelDirectory = @".\Models"; public class TaxiTrip { [Column(ordinal: "0")] public string vendor_id; [Column(ordinal: "1")] public string rate_code; [Column(ordinal: "2")] public float passenger_count; [Column(ordinal: "3")] public float trip_time_in_secs; [Column(ordinal: "4")] public float trip_distance; [Column(ordinal: "5")] public string payment_type; [Column(ordinal: "6")] public float fare_amount; } public class TaxiTripFarePrediction { [ColumnName("Score")] public float fare_amount; } static class TestTrips { internal static readonly TaxiTrip Trip1 = new TaxiTrip { vendor_id = "VTS", rate_code = "1", passenger_count = 1, trip_distance = 10.33f, payment_type = "CSH", fare_amount = 0 // predict it. actual = 29.5 }; } public static async Task<PredictionModel<TaxiTrip, TaxiTripFarePrediction>> Train() { var pipeline = new LearningPipeline(); pipeline.Add(new TextLoader<TaxiTrip>(DataPath, useHeader: true, separator: ",")); pipeline.Add(new ColumnCopier(("fare_amount", "Label"))); pipeline.Add(new CategoricalOneHotVectorizer("vendor_id", "rate_code", "payment_type")); pipeline.Add(new ColumnConcatenator("Features", "vendor_id", "rate_code", "passenger_count", "trip_distance", "payment_type")); pipeline.Add(new FastTreeRegressor()); PredictionModel<TaxiTrip, TaxiTripFarePrediction> model = pipeline.Train<TaxiTrip, TaxiTripFarePrediction>(); if (!Directory.Exists(ModelDirectory)) { Directory.CreateDirectory(ModelDirectory); } await model.WriteAsync(ModelPath); return model; } public static void Evaluate(PredictionModel<TaxiTrip, TaxiTripFarePrediction> model) { var testData = new TextLoader<TaxiTrip>(TestDataPath, useHeader: true, separator: ","); var evaluator = new RegressionEvaluator(); RegressionMetrics metrics = evaluator.Evaluate(model, testData); // Rms should be around 2.795276 Console.WriteLine("Rms=" + metrics.Rms); Console.WriteLine("RSquared = " + metrics.RSquared); } static async Task Main(string[] args) { PredictionModel<TaxiTrip, TaxiTripFarePrediction> model = await Train(); Evaluate(model); var prediction = model.Predict(TestTrips.Trip1); Console.WriteLine("Predicted fare: {0}, actual fare: 29.5", prediction.fare_amount); } } } |
不知不觉我们的ML.NET之旅又向前进了一步,是不是对于使用.NET Core进行机器学习解决现实生活中的问题更有兴趣了?请保持关注吧。 from:http://www.cnblogs.com/BeanHsiang/p/9017618.html
View DetailsML.NET 专门为.NET开发者提供了一套跨平台的开源的机器学习框架。 ML.NET支持.NET开发者不需要过度专业的机器学习开发经验,就能轻松地训练自己的模型,并且嵌入到自己的应用中。一切尽在.NET之中。ML.NET早期是由Microsoft Research开发,近十年来逐步集成到一个大体系中被众多Microsoft产品使用,如大家熟知的Windows、Bing、PowerPoint、Excel之类。 ML.NET的第一个预览版提供了分类器(如文本分类、情感分析)和回归(如价格预测)等实用的机器学习模型。第一版发布后在既有功能之上又新增了关于训练模型的.NET API,使用这些模型进行预测,就像框架中算法、转换、数据结构一类核心组件一样的开发体验。 接下来用个示例,一起进入快速上手的实践中来。 安装.NET SDK 为了创建一个.NET应用,首先下载 .NET SDK。 创建应用 使用如下命令初始化项目,创建一个控制台应用程序,目标为myApp:
1 2 |
dotnet new console -o myApp cd myApp |
安装ML.NET包 使用如下命令安装Microsoft.ML包:
1 |
dotnet add package Microsoft.ML |
下载数据集 假设我们使用机器学习来预测鸢尾花的类型,比如有setosa、versicolor、virginica三种,基于特征有四种:花瓣长度、花瓣宽度, 萼片长度、萼片宽度。 去UCI Machine Learning Repository: Iris Data Set下载一个现成的数据集,复制粘贴其中的数据到任何一个文本编辑器中,然后保存命名为iris-data.txt到myApp目录中。 粘贴完文本内容应该是如下格式,每一行表示不同鸢尾花的样本,数值的部分从左到右依次是萼片长度、萼片宽度、花瓣长度、花瓣宽度,最后是鸢尾花的类型。
1 2 3 4 |
5.1,3.5,1.4,0.2,Iris-setosa 4.9,3.0,1.4,0.2,Iris-setosa 4.7,3.2,1.3,0.2,Iris-setosa ... |
如果是使用了Visual Studio,将iris-data.txt添加至项目中,需要进行如下配置确保运行时数据集文件在输出的目录中。 编写代码 打开Program.cs文件,输入以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
using System; using Microsoft.ML; using Microsoft.ML.Runtime.Api; using Microsoft.ML.Trainers; using Microsoft.ML.Transforms; using Microsoft.ML.Data; namespace myApp { class Program { // STEP 1: Define your data structures // IrisData is used to provide training data, and as // input for prediction operations // - First 4 properties are inputs/features used to predict the label // - Label is what you are predicting, and is only set when training public class IrisData { [Column("0")] public float SepalLength; [Column("1")] public float SepalWidth; [Column("2")] public float PetalLength; [Column("3")] public float PetalWidth; [Column("4")] [ColumnName("Label")] public string Label; } // IrisPrediction is the result returned from prediction operations public class IrisPrediction { [ColumnName("PredictedLabel")] public string PredictedLabels; } static void Main(string[] args) { // STEP 2: Create a pipeline and load your data var pipeline = new LearningPipeline(); // If working in Visual Studio, make sure the 'Copy to Output Directory' // property of iris-data.txt is set to 'Copy always' string dataPath = "iris.data.txt"; pipeline.Add(new TextLoader(dataPath).CreateFrom<IrisData>(separator:',')); // STEP 3: Transform your data // Assign numeric values to text in the "Label" column, because only // numbers can be processed during model training pipeline.Add(new Dictionarizer("Label")); // Puts all features into a vector pipeline.Add(new ColumnConcatenator("Features", "SepalLength", "SepalWidth", "PetalLength", "PetalWidth")); // STEP 4: Add learner // Add a learning algorithm to the pipeline. // This is a classification scenario (What type of iris is this?) pipeline.Add(new StochasticDualCoordinateAscentClassifier()); // Convert the Label back into original text (after converting to number in step 3) pipeline.Add(new PredictedLabelColumnOriginalValueConverter() { PredictedLabelColumn = "PredictedLabel" }); // STEP 5: Train your model based on the data set var model = pipeline.Train<IrisData, IrisPrediction>(); // STEP 6: Use your model to make a prediction // You can change these numbers to test different predictions var prediction = model.Predict(new IrisData() { SepalLength = 3.3f, SepalWidth = 1.6f, PetalLength = 0.2f, PetalWidth = 5.1f, }); Console.WriteLine($"Predicted flower type is: {prediction.PredictedLabels}"); Console.ReadLine(); } } } |
运行应用 使用如下命令行运行程序:
1 |
dotnet run |
在最后一行将输出对花的预测结果,你可以修改传给Predict函数各种鸢尾花的特征值看看有什么不同的结果。 恭喜,你已经跨入使用ML.NET进行机器学习的门槛了! from:http://www.cnblogs.com/BeanHsiang/p/9010267.html
View Details最近公司有些小项目要用asp.net core尝试一下,局域网内建了内部的nuget服务。今天搞好.net core类库后发布过程一切顺利。 但在具体的项目中还原nuget包后直接提示: 警告 NU1701 已使用“.NETFramework,Version=v4.6.1”而不是项目目标框架“.NETCoreApp,Version=v2.1”还原包“DBCore 1.0.8”。此包可能与项目不完全兼容。 首先,怀疑是不是引用了.net core不支持的类库,然后把依赖全删除,不行。 然后,还是怀疑自己的类库有问题,就新建了一个空白的.net core类库,还原后还是提示。 然后,拿redis的nuget包发布上去,还原了一下,还是提示。 最后发现redis的类库是以目标框架作为文件夹的!!!然后在lib下增加了文件夹netcoreapp2.1,问题解决。
View Details一、前言 我们都知道,ASP.NET Core作为最新的框架,在MVC5和ASP.NET WebForm的基础上做了大量的重构。如果我们想使用以前版本中的HttpContext.Current的话,目前是不可用的,因为ASP.NET Core中是并没有这个API的。 当然我们也可以通过在Controller中访问HttpContext,但是某些情况下,这样使用起来还是不如HttpContext.Current方便。 二、IHttpContextAccessor 利用ASP.NET Core的依赖注入容器系统,通过请求获取IHttpContextAccessor接口,我们拥有模拟使用HttpContext.Current这样API的可能性。但是因为IHttpContextAccessor接口默认不是由依赖注入进行实例管理的。我们先要将它注册到ServiceCollection中:
1 2 3 4 5 6 |
public void ConfigureServices(IServiceCollection services) { services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>(); // Other code... } |
来模拟一个HttpContext.Current吧:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public static class HttpContext { public static IServiceProvider ServiceProvider; public static Microsoft.AspNetCore.Http.HttpContext Current { get { object factory = ServiceProvider.GetService(typeof(Microsoft.AspNetCore.Http.IHttpContextAccessor)); Microsoft.AspNetCore.Http.HttpContext context = ((Microsoft.AspNetCore.Http.HttpContextAccessor)factory).HttpContext; return context; } } } |
其实说到HttpContext.Current就不得不提到多线程问题,在以前的ASP.NET版本中,如果遇到多线程环境很有可能HttpContext.Current为空的情况。说到这个问题以前就是有解决方案的,那就是CallContext; CallContext 是类似于方法调用的线程本地存储区的专用集合对象,并提供对每个逻辑执行线程都唯一的数据槽。数据槽不在其他逻辑线程上的调用上下文之间共享。当 CallContext 沿执行代码路径往返传播并且由该路径中的各个对象检查时,可将对象添加到其中。 当使用ASP.NET的时候,虽然线城池里的线程是复用的,但是CallContext并不在一个线程的多次使用中共享。因为CallContext是针对逻辑线程的TLS,线程池中被复用的线程是操作系统中的内核对象而不是托管对象。就像数据库连接池中保存的是非托管资源而不是托管资源。因此,先后执行的两个托管线程可能在底层复用了一个物理线程(内核对象),但并不能共享同一组CallContext数据槽。就像先后new的两个SqlConnection对象可能在底层使用了同一个物理连接,但是托管对象的属性已经被重置。 与此对照的是ThreadStaticAttribute,标记上这个特性的静态字段是往物理线程的TLS中保存数据(根据MSDN的描述猜的。具体没试过),因此如果两个托管线程对象内部使用的是同一个物理线程,则这个字段会复用(在两个线程通过这一字段访问同一个数据槽)。 在.NET Core中,也有新的API选择,AsyncLocal<T>。 三、HttpContextAccessor 我们来看看ASP.NET Core中的IHttpContextAccessor接口实现吧:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
public class HttpContextAccessor : IHttpContextAccessor { #if NET451 private static readonly string LogicalDataKey = "__HttpContext_Current__" + AppDomain.CurrentDomain.Id; public HttpContext HttpContext { get { var handle = CallContext.LogicalGetData(LogicalDataKey) as ObjectHandle; return handle?.Unwrap() as HttpContext; } set { CallContext.LogicalSetData(LogicalDataKey, new ObjectHandle(value)); } } #elif NETSTANDARD1_3 private AsyncLocal<HttpContext> _httpContextCurrent = new AsyncLocal<HttpContext>(); public HttpContext HttpContext { get { return _httpContextCurrent.Value; } set { _httpContextCurrent.Value = value; } } #endif } |
最后我只能说在ASP.NET Core中是万物皆DI啊,其实Core中的实现早就为我们想好了这些功能,只是改变了使用方式。 GitHub:https://github.com/maxzhang1985/YOYOFx 如果觉还可以请Star下, 欢迎一起交流。 .NET Core 开源学习群: 214741894 from:https://www.cnblogs.com/maxzhang1985/p/6186455.html
View Details在我们用来获取客户端IP地址的传统ASP.NET中Request.UserHostAddress。但是这不适用于ASP.NET Core 2.0。我们需要一种不同的方式来检索HTTP请求信息。 1.在你的MVC控制器中定义一个变量
1 |
private IHttpContextAccessor _accessor; |
2. DI进入控制器的构造函数 public SomeController(IHttpContextAccessor accessor) { _accessor = accessor; } 3.回传IP地址
1 |
_accessor.HttpContext.Connection.RemoteIpAddress.ToString() |
这RemoteIpAddress是在类型IPAddress,而不是string。它包含了IPv4,IPv6等信息,它不像经典的ASP.NET,对我们来说更有用。 from:https://blog.csdn.net/yzj_xiaoyue/article/details/79200714
View DetailsAppContext.BaseDirectory 获取项目的根目录 from:https://www.cnblogs.com/zxs-onestar/p/7147265.html
View Details