.Net Core2.0下使用Dapper遇到的问题

今天成功把.Net Framework下使用Dapper进行封装的ORM成功迁移到.Net Core 2.0上,在迁移的过程中也遇到一些很有意思的问题,值得和大家分享一下。下面我会还原迁移的每一个过程,以及在此过程中遇到的问题和处理这些问题的方法。

一、迁移前的准备

之前对Dapper的封装使用的是.Net Framework下的ORM 框架Dapper,开发工具VS2013,现在既然想在.Net Core2.0上使用Dapper,我要先到NuGet看看有没有支持 .Net Core的,在Nuget找到如下:

果然有!!!因为项目中使用的是MySQL,所以还要看看有没有MySQL的.Net驱动,发现也有,但是是预发行版本,算了等不及正式版了,先用(生产环境中我暂时没使用)它来测试,等正式版出来就正式迁移了(* ̄︶ ̄)

好了,该准备的已经准备好了,下面就是使用VS2017新建一个项目,用来测试,项目的整体结构如下:

二、正式迁移

.Net Framework下对Dapper进行的二次封装,代码部分如下,后面会介绍我为什么要这样封装: 

namespace ZSZ.Core.Respository

{


    public interface IDataAdapter

    {

        string BindVariablePrefix { get; }

        void AppendColumnName(StringBuilder sb, string columnName);

        void AppendColumnNameEqualsValue(StringBuilder sb, string columnName);

        void AppendUpdateColumnName(StringBuilder sb, string columnName);

    }

    public class OracleDataAdapter : IDataAdapter

    {

        public string BindVariablePrefix

        {

            get { return ":"; }

        }

        public void AppendColumnName(StringBuilder sb, string columnName)

        {

            sb.AppendFormat("{0}, ", columnName.ToUpper());

        }

        public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName)

        {

            sb.AppendFormat("{0}{1}, ", BindVariablePrefix, columnName.ToUpper());

        }

        public void AppendUpdateColumnName(StringBuilder sb, string columnName)

        {

            sb.AppendFormat("{0}={1}{0}, ", columnName.ToUpper(), BindVariablePrefix);

        }

    }

    public class SqlServerDataAdapter : IDataAdapter

    {

        public string BindVariablePrefix

        {

            get { return "@"; }

        }

        public void AppendColumnName(StringBuilder sb, string columnName)

        {

            sb.AppendFormat("{0}, ", columnName.ToUpper());

        }


        public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName)

        {

            sb.AppendFormat("{0}{1}, ", BindVariablePrefix, columnName.ToUpper());

        }


        public void AppendUpdateColumnName(StringBuilder sb, string columnName)

        {

            sb.AppendFormat("{0}={1}{0}, ", columnName.ToUpper(), BindVariablePrefix);

        }

    }

    public class MySqlDataAdapter : IDataAdapter

    {

        public string BindVariablePrefix

        {

            get { return "@"; }

        }

        public void AppendColumnName(StringBuilder sb, string columnName)

        {

            sb.AppendFormat("{0}, ", columnName.ToUpper());

        }


        public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName)

        {

            sb.AppendFormat("{0}{1}, ", BindVariablePrefix, columnName.ToUpper());

        }


        public void AppendUpdateColumnName(StringBuilder sb, string columnName)

        {

            sb.AppendFormat("{0}={1}{0}, ", columnName.ToUpper(), BindVariablePrefix);

        }

    }

    public static class DataBase

    {

        internal class TypeInsertPair

        {

            public string Columns { get; set; }

            public string Values { get; set; }

        }


        /*

         * 

         * 

         * 线程安全:如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

         * http://www.cnblogs.com/CreateMyself/p/6086752.html

         * http://www.cnblogs.com/PurpleTide/archive/2011/11/21/2256577.html

         * http://www.cnblogs.com/lori/p/4344026.html

         */


        private static readonly ConcurrentDictionary<RuntimeTypeHandle, TypeInsertPair> TypeInsertPairDictionary = new ConcurrentDictionary<RuntimeTypeHandle, TypeInsertPair>();

        private static readonly ConcurrentDictionary<RuntimeTypeHandle, string> TypeUpdateDictionary = new ConcurrentDictionary<RuntimeTypeHandle, string>();

        private static readonly ConcurrentDictionary<RuntimeTypeHandle, string> TypeColumnsDictionary = new ConcurrentDictionary<RuntimeTypeHandle, string>();


        private static IDataAdapter defaultDataAdapter;

        private static IDataAdapter DefaultDataAdapter

        {

            get

            {

                if (defaultDataAdapter == null)

                {

                    defaultDataAdapter = GetDataAdapter(DefaultConnectionStringSettings);

                }

                return defaultDataAdapter;

            }

            set

            {

                defaultDataAdapter = value;

            }

        }

        public static IDataAdapter GetDataAdapter(this ConnectionStringSettings connectionStringSettings)

        {

            if (connectionStringSettings == null)

            {

                return defaultDataAdapter;

            }

            if (string.IsNullOrEmpty(connectionStringSettings.ProviderName))

            {

                throw new Exception("数据库连接串的配置不正确!");

            }

            if (connectionStringSettings.ProviderName.ToLower().Contains("oracle"))

            {

                return new OracleDataAdapter();

            }

            else if (connectionStringSettings.ProviderName.ToLower().Contains("mysql"))

            {

                return new MySqlDataAdapter();

            }

            else if (connectionStringSettings.ProviderName.ToLower().Contains("sql"))

            {

                return new SqlServerDataAdapter();

            }


            throw new Exception("暂不支持您使用的数据库类型!");

        }

        private static ConnectionStringSettings defaultConnectionStringSettings;

        public static ConnectionStringSettings DefaultConnectionStringSettings

        {

            get

            {

                if (defaultConnectionStringSettings == null)

                {

                    defaultConnectionStringSettings = ConfigurationManager.ConnectionStrings["db"];

                }

                return defaultConnectionStringSettings;

            }

            set

            {

                if (value == null) throw new Exception("默认的数据库连接配置信息不能为空!");

                defaultConnectionStringSettings = value;

                DefaultDataAdapter = GetDataAdapter(value);

            }

        }

        private static IDbConnection GetDbConnection(this ConnectionStringSettings connectionStringSettings)

        {

            if (connectionStringSettings != null && (string.IsNullOrEmpty(connectionStringSettings.ConnectionString) || string.IsNullOrEmpty(connectionStringSettings.ProviderName))) throw new Exception("数据库链接字符串配置不正确!");

            var settings = connectionStringSettings == null ? DefaultConnectionStringSettings : connectionStringSettings;

            var factory = System.Data.Common.DbProviderFactories.GetFactory(settings.ProviderName);

            var connection = factory.CreateConnection();

            connection.ConnectionString = settings.ConnectionString;

            return connection;


        }

        private static TypeInsertPair GetTypeInsertPair(this Type type, IDataAdapter adapter)

        {

            if (TypeInsertPairDictionary.ContainsKey(type.TypeHandle)) return TypeInsertPairDictionary[type.TypeHandle];


            var columns = new StringBuilder();

            var values = new StringBuilder();

            foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))

            {

                if (property.IsIgnore() && !"id".Equals(property.Name, StringComparison.OrdinalIgnoreCase)) continue;


                adapter.AppendColumnName(columns, property.Name);

                adapter.AppendColumnNameEqualsValue(values, property.Name);

            }


            var pair = new TypeInsertPair() { Columns = columns.ToString().Substring(0, columns.Length - 2), Values = values.ToString().Substring(0, values.Length - 2) };

            TypeInsertPairDictionary[type.TypeHandle] = pair;


            return pair;

        }

        private static string GetTypeColumns(this Type type, ConnectionStringSettings connectionStringSettings)

        {

            if (TypeColumnsDictionary.ContainsKey(type.TypeHandle)) return TypeColumnsDictionary[type.TypeHandle];


            var sb = new StringBuilder();

            var adapter = connectionStringSettings.GetDataAdapter();

            foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))

            {

                //查询时的字段

                adapter.AppendColumnName(sb, property.Name);

            }


            var columns = sb.ToString().Substring(0, sb.Length - 2);

            TypeColumnsDictionary[type.TypeHandle] = columns;

            return columns;

        }

        private static string GetTypeUpdateSetString(this Type type, ConnectionStringSettings connectionStringSettings)

        {

            if (TypeUpdateDictionary.ContainsKey(type.TypeHandle)) return TypeUpdateDictionary[type.TypeHandle];


            var sb = new StringBuilder();

            var adapter = connectionStringSettings.GetDataAdapter();

            foreach (var property in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))

            {

                //更新时如果传入实体对象的话,会有ID在里面,所以在这里要把ID(主键)去掉

                if (property.IsIgnore() || "id".Equals(property.Name, StringComparison.OrdinalIgnoreCase)) continue;


                adapter.AppendUpdateColumnName(sb, property.Name);

            }


            var update = sb.ToString().Substring(0, sb.Length - 2);

            TypeUpdateDictionary[type.TypeHandle] = update;

            return update;

        }

        //如果对应的字段上有这样的特性就不参与对应的数据库操作

        private static bool IsIgnore(this PropertyInfo property)

        {

            var attribute = property.GetCustomAttributes(typeof(IgnoreAttribute), true).FirstOrDefault() as IgnoreAttribute;

            return attribute != null && attribute.Ignore;

        }


        #region 查询

        //根据实体生成sql,映射返回实体集合

        //使用:传过来condition、param参数即可

        public static IEnumerable<T> Get<T>(string condition = null, object param = null, string tableName = null, ConnectionStringSettings connectionStringSettings = null, IDbTransaction transaction = null) where T : class

        {

            if (string.IsNullOrEmpty(tableName) && !(typeof(T).IsSubclassOf(typeof(BaseEntity<T>)))) throw new Exception("没有输入表名时只支持数据库实体查询!");

            var name = string.IsNullOrEmpty(tableName) ? BaseEntity<T>.TableName : tableName;

            var columns = string.IsNullOrEmpty(tableName) ? "*" : typeof(T).GetTypeColumns(connectionStringSettings);

            var sql = string.IsNullOrEmpty(condition) ? string.Format("select {0} from {1}", columns, name) : string.Format("select {0} from {1} where {2}", columns, name, condition);

            var conn = connectionStringSettings.GetDbConnection();

            return conn.Query<T>(sql, param, transaction);

        }


        //根据SQL映射实体或ViewModel

        //使用:传过来SQL,让Dapper进行映射

        public static IEnumerable<T> GetBySql<T>(string sql, object param = null, ConnectionStringSettings connectionStringSettings = null, IDbTransaction transaction = null) where T : class

        {

            var conn = connectionStringSettings.GetDbConnection();

            return conn.Query<T>(sql, param, transaction);

        }


        //根据ID获取单个实体对象

        //使用:传过来ID

        public static T GetById<T>(string id, string tableName = null, ConnectionStringSettings connectionStringSettings = null, IDbTransaction transaction = null) where T : class

        {

            var adapter = connectionStringSettings.GetDataAdapter();

            return Get<T>(connectionStringSettings: connectionStringSettings, tableName: tableName, condition: string.Format("id ={0}id", adapter.BindVariablePrefix), param: new { id = id }, transaction: transaction).FirstOrDefault();

        }

}

 在把.Net Framework下对Dapper进行二次封装的代码放到.Net Core 2.0之前,我们需要在上面新建的项目中引用,如下Nuget包:

(1)

在图中标识的项目中需要引用Dapper和MySQL

  1)Install-Package MySql.Data -Version 8.0.8-dmr

  2)Install-Package Dapper -Version 1.50.2

(2)迁移到.Net Core2.0上之后报错截图,前提是有些可以手动引用,下面列出来的是ctrl+.还解决不了的。

  1)在.Net Framework下    System.Configuration下有ConnectionStringSettings类,但是在.Net Core中是不是还在同样的命名空间下?于是查看接口文档https://docs.microsoft.com/zh-cn/dotnet/api/system.configuration.connectionstringsettings?view=netcore-2.0,发现,真有!!!于是就using System.Configuration;同时还需要安装Nuget包: Install-Package System.Configuration.ConfigurationManager,这样就可以把ConnectionStringSettings类的错误解决掉。

  2)ConfigurationManager类的错误,关于读取配置文件中的信息,可以添加 Microsoft.Extensions.Configuration和Microsoft.Extensions.Configuration.Json来解决配置文件的读取问题,还可以使用原始的方式来读取,但需要添加 System.Configuration.ConfigurationManager,关于怎么读取配置文件中的信息,很简单,在这里就不介绍了。下面给一篇关于如何读取配置文件信息的文章 :http://www.cnblogs.com/mantgh/p/7425113.html

  3)在.Net Framework下, system.Data.Common下有DbProviderFactories类,如下图:

但是在.Net Core 2.0的接口文档中没有找到该类,那该怎么办?首先在这里讲点ADO.Net的相关知识,我为什么要使用该类?因为使用该类中的 public static DbProviderFactory GetFactory(string providerInvariantName);可以通过providerInvariantName来创建对应的  ClientFactory,因为该方法返回DbProviderFactory ,同时SqlClientFactory、MySqlClientFactory等都继承DbProviderFactory,如下图所示:

我们通过该方法拿到对应的DbProviderFactory工厂了 ,也就意味着可以通过providerInvariantName拿到对应数据库的ClientFactory,然后调用里面的CreateConnection()方法就可以得到一个DbConnection,再调用里面的ConnectionString属性,把链接字符串赋值给该属性即可。该部分的代码在上面测试项目System.Data.CommonExts中,代码如下:

using MySql.Data.MySqlClient;

using System.Data.Common;

using System.Data.SqlClient;


namespace System.Data.CommonExts

{

    public static class DbProviderFactories

    {

        /// <summary>

        /// 通过在appsettings.json文件中配置 "providerName",来创建对应的数据库链接

        /// </summary>

        /// <param name="providerInvariantName">例如:MySql.Data.MySqlClient</param>

        /// <returns>DbProviderFactory</returns>

        public static DbProviderFactory GetFactory(string providerInvariantName)

        {

            if (string.IsNullOrEmpty(providerInvariantName)) throw new Exception("数据库链接字符串配置不正确!");

           

            if(providerInvariantName.ToLower().Contains("mysql"))

            {

                return new MySqlClientFactory();

            }

            else if(providerInvariantName.ToLower().Contains("sql"))

            {

                return  SqlClientFactory.Instance;

            }


            throw new Exception("暂不支持您使用的数据库类型!");


        }

    }

}


注意,这里需要安装的包,如下:

  4)配置文件配置如下:

{  "db": {   
 
"mysql": {    
 
"conStr": "server=.;charset=gb2312;user id=root;password=123456;persist security info=True;database=dappertest;charset=utf8;",      "providerName": "MySql.Data.MySqlClient"}} }

我知道你会想,为什么要这样做???神经病吧!!!,如果你有好的办法也可以分享出来,后面会介绍为什么我要这样封装。

好了,完成上面的三步即可完成迁移,下面是在测试时遇到的问题,在讲测试时遇到的问题前,需要给大家介绍一下,为什么我要这样封装Dapper以及正式项目中为什么要这样搭建。

三、对Dapper进行封装的原因以及正式项目中搭建这样的框架的背景

(1)如果不对Dapper进行二次封装,我们是这样使用的

using(MySqlConnection con = new MySqlConnection("server=127.0.0.1;database=test;uid=root;pwd=;charset='gbk'"))
{   var list=con.Query<User>("select * from user");............
}

每次对数据库操作,我都需要先new一个MySqlConnection,太烦。于是就有了,下面的代码:

图中的ConnectionStringSettings是没有找到对应程序集时,自己定义的。现在在.Net Core2.0中可以找到了,上面已经介绍了。

 (2)我现在使用的是MySQL数据库,如果要切换数据库,比如使用SqlServer、oracle等其他数据库,我还需要修改connection,太麻烦,于是就有了下面的代码:

通过DBProviderFactories,动态创建数据库链接。

(3)在没有对Dapper进行二次封装,如果我们切换数据库,由于不同数据库的语法不一样,修改的工作量不能忽视,如何屏蔽不同数据库之间语法的不同呢,于是就有了下面的代码:

public interface IDataAdapter

    {

        string BindVariablePrefix { get; }

        void AppendColumnName(StringBuilder sb, string columnName);

        void AppendColumnNameEqualsValue(StringBuilder sb, string columnName);

        void AppendUpdateColumnName(StringBuilder sb, string columnName);

    }


 public class MySqlDataAdapter : IDataAdapter

    {

        public string BindVariablePrefix

        {

            get { return "@"; }

        }

        public void AppendColumnName(StringBuilder sb, string columnName)

        {

            sb.AppendFormat("{0}, ", columnName.ToUpper());

        }


        public void AppendColumnNameEqualsValue(StringBuilder sb, string columnName)

        {

            sb.AppendFormat("{0}{1}, ", BindVariablePrefix, columnName.ToUpper());

        }


        public void AppendUpdateColumnName(StringBuilder sb, string columnName)

        {

            sb.AppendFormat("{0}={1}{0}, ", columnName.ToUpper(), BindVariablePrefix);

        }

    }

下面是真实项目的整体框架,如下图:

不是说这样的搭建是好的,可以适合任何的项目,只能说,它适合我,适合现在的需求。现在的web已经是一个泛化的web,网站不是web的全部分,只是web的一小部分。现在的产品是一个web产品矩阵,不仅包括网站而且还包括iOS、Android、微信、微信小程序等。所以把接口单独分离出来,到时候可以单独部署在一台服务器上,作为公共服务,不仅我们的网站可以使用,而且我们的小程序也可以使用。好了,有点扯了,说的不对的还请各位指出来。

四、测试时遇到的问题

 (1)链接字符串server=.需要修改为:server=127.0.0.1; 否则会报链接不上数据库的错误,这里就不截图了。

(2)链接字符串需要加上SslMode=None

最后完整的配置文件如下:

1 {
2 "db": {
3 "mysql": {
4 "conStr": "server=127.0.0.1;charset=gb2312;user id=root;password=123456;persist security info=True;database=dappertest;charset=utf8;SslMode=None",
5 "providerName": "MySql.Data.MySqlClient"
6    }    
7  }  
8 }

讲到这里基本上就讲完了,大家如果遇到问题了,可以留言,我看到后会及时回复大家。

五、总结

通过上面的讲解,我们不要为了使用ORM而使用ORM,而忘记了他们底层使用的是ADO.Net,把他们搞明白,比任何ORM都重要!!!,谢谢大家,希望对你有帮助!

原文地址:http://www.cnblogs.com/runningsmallguo/p/7430693.html


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/323940.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

汇编语言(二)之数值求和

输入一串数字&#xff0c;求和 运行结果&#xff1a; 程序代码&#xff1a; datas segmentx db ? y db ? z db ?xInputPrompt db Enter a number x$ yInputPrompt db 0dh,0ah,Enter a number y$ zOutputPrompt db 0dh,0…

avue中怎样隐藏新增和编辑的按钮

“大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;????雄雄的小课堂????”????‍♂️今天给大家分享的技术是&#xff1a;avue中如何设置新增和编辑的按钮隐藏掉。????‍????涉及技术????前端&#xff1a;avue????后端&…

.NET跨平台实践:Linux .Net Core自宿主应用程序瘦身记

一&#xff0c;.NET Core 自宿主应用程序个头有点大 发布.NET Core应用程序有两个方式&#xff0c;一种是“便携式”&#xff0c;一种是“自宿主式”。便携式发布时&#xff0c;目标程序不带.net core运行环境&#xff0c;所以“个头”很小&#xff0c;可能只有几十K几百K字节…

汇编语言(三)之判断数值是否大于42H并统计个数

在内存中一串数值&#xff0c;判断数值是否大于42H并统计个数 程序运行&#xff1a; 代码&#xff1a; datas segmentidata equ 100hnums db idata dup(41h,42h,43h,42h,41h,43h,30h)count dw $-numsup dw 0down dw 0upNumber …

idea打war的问题

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;&#x1f449;雄雄的小课堂&#x1f448;。 &#x1f468;‍&#x1f3eb;前言 今天&#xff0c;记录个到现在为止还没搞清的问题&#xff0c;这个问题浪费了我几个小时的时间&#xff0c;基本上昨天晚上…

汇编语言(四)之比较字符串

输入两个字符串&#xff0c;比较字符串是否相同 程序运行&#xff1a; 代码&#xff1a; datas segmentstring1MaxLength db 0ffh,0 string1 db 100h dup(?)string1Number dw 0 string2MaxLength db 0ffh,0 string2 db 100h dup(?) string2Number…

.NET平台微服务项目汇集

最近博客园出现了一篇文章《微服务时代之2017年五军之战&#xff1a;Net PHP谁先死》&#xff0c;掀起了一波撕逼&#xff0c;作者只是从一个使用者的角度来指点江山&#xff0c;这个姿势是不对的。.NET Core就是专门针对模块化的微服务架构而设计&#xff0c;在微服务架构这方…

idea打war包时,JDK版本的问题解决方式

“大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;????雄雄的小课堂????。”????‍????前言今天&#xff0c;记录个到现在为止还没搞清的问题&#xff0c;这个问题浪费了我几个小时的时间&#xff0c;基本上昨天晚上啥也没干&#xff0…

汇编语言(五)之数组中正数和负数分离

将数组中的正数和负数分离到两个数组 程序运行&#xff1a; 代码&#xff1a; datas segmenta dw -1,2,3,4,-2,-3,5,6,7,8,9,-10,13,15,-5,-24,-36,34,53,-90count dw ($-a)/2p dw 20 dup(?)n dw 20 dup(?)pNumber dw 0…

dotnet使用Selenium执行自动化任务

如果要做百度文库&#xff0c;百度贴吧&#xff0c;百度知道签到&#xff0c;你&#xff0c;会怎么做&#xff1f;前不久我还会觉得这好像太麻烦了&#xff0c;now,soeasy。 自动化测试工具&#xff1a;Selenium Selenium是一个用于Web应用程序测试的工具。Selenium测试直接运行…

若依前后端部署之后验证码不显示

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;&#x1f449;雄雄的小课堂&#x1f448;。 最近的几天都在搞publiccms的内容&#xff0c;从0到1实现&#xff0c;在花费了大量精力下&#xff0c;终于将一个门户站完完全全的实现了&#xff0c;且还可以…

汇编语言(六)之输出字符的前导后字符

输入一个字符&#xff0c;输出该字符的前导后字符 程序运行&#xff1a; 代码&#xff1a; datas segmenta db ?inputPrompt db input a lowercase character:$outputPrompt db 0dh,0ah,output …

若依部署上线之后验证码不显示的解决方法之一

“大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;????雄雄的小课堂????。”最近的几天都在搞publiccms的内容&#xff0c;从0到1实现&#xff0c;在花费了大量精力下&#xff0c;终于将一个门户站完完全全的实现了&#xff0c;且还可以自定义扩…

在 ASP.NET Core 中执行租户服务

本博文翻译自&#xff1a;http://gunnarpeipman.com/2017/08/tenant-providers/ 在我之前关于 Entity Framework core 2.0 全局查询过滤器的文章中&#xff0c;我提出了一个想法&#xff0c;当构建模型时&#xff0c;如何自动地将查询过滤器应用到所有的领域实体中&#xff0c…

【最全最详细】使用publiccms实现动态可维护的首页轮播

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;&#x1f449;雄雄的小课堂&#x1f448;。 &#x1f481;‍♂️前言 前几天&#xff0c;分享了一篇关于publiccms的教程&#xff0c;在这里&#xff1a;【最全最详细】publiccms使用教程&#xff0c;不…

汇编语言(七)之字符串转大写

输入一串字符&#xff0c;将字符串的小写字母转成大写字母 程序运行&#xff1a; 代码&#xff1a; datas segmentoriginalCaseMaxLength db 0ffh,0originalCase db 100h dup(?)uppercase db 100h dup(?)inputPrompt …

基于.NET CORE微服务框架 -谈谈surging API网关

1、前言 对于最近surging更新的API 网关大家也有所关注&#xff0c;也收到了不少反馈提出是否能介绍下Api网关&#xff0c;那么我们将在此篇文章中谈谈surging Api 网关 开源地址&#xff1a;https://github.com/dotnetcore/surging 2. API网关 简介 API 网关是服务提供者…

【最全最详细】使用publiccms实现动态可维护的导航菜单栏

“大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;????**雄雄的小课堂????。”????‍????前言昨天&#xff0c;给大家整理的是通过publiccms实现动态可维护的轮播图&#xff0c;有需要的小伙伴可以点击这里&#xff1a;publiccms实现动…

【上海】关于云计算,你想学习哪些知识,快让我来满足你

超高人气、干货十足的 免费云计算课堂 Microsoft Cloud Day云思塾 2017下半年再出发&#xff01; 即将开启上海之旅&#xff0c;微软诚邀您参加&#xff01; Microsoft Cloud Day是个啥&#xff1f; 这是为时一天的结合用户培训与经验分享的云计算免费研讨会&#xff0c;通…

【最全最详细】publiccms实现将公共部分提取成单独模块引入

大家好&#xff0c;我是雄雄&#xff0c;欢迎关注微信公众号&#xff1a;&#x1f449;雄雄的小课堂&#x1f448;。 &#x1f9d8;‍♂️往期系列 这两天一直在整理Publiccms系列的教程&#xff0c;有需要的小伙伴们可以点击以下链接查看&#xff1a; ☝publiccms使用教程&a…