ASP.NET Core Web 应用程序系列(一)- 使用ASP.NET Core内置的IoC容器DI进行批量依赖注入(MVC当中应用)...

在正式进入主题之前我们来看下几个概念:

一、依赖倒置

依赖倒置是编程五大原则之一,即:

1、上层模块不应该依赖于下层模块,它们共同依赖于一个抽象。

2、抽象不能依赖于具体,具体依赖于抽象。

其中上层就是指使用者,下层就是指被使用者。

二、IoC控制反转

控制反转(IoC,全称Inversion of Control)是一种思想,所谓“控制反转”,就是反转获得依赖对象的过程。

三、依赖注入(DI)

依赖注入设计模式是一种在类及其依赖对象之间实现控制反转(IoC)思想的技术。

所谓依赖注入(DI,全称Dependency Injection),就是由IoC容器在运行期间,动态地将某种依赖关系注入到对象之中。

依赖注入主要分为3种:构造函数注入、属性注入、方法注入。

 

这里就不做过多的描述,如果有机会会通过具体例子再和大家分享,下面我们正式进入本章主题。

PS:ASP.NET Core 内置的IoC容器目前只支持构造函数注入,以下我们也主要讲解构造函数注入的方式。

话不多说,直入主题看我们的解决方案结构:

分别对上面的工程进行简单的说明:

1、TianYa.DotNetShare.Model:为demo的实体层

2、TianYa.DotNetShare.Repository:为demo的仓储层即数据访问层

3、TianYa.DotNetShare.Service:为demo的服务层即业务逻辑层

4、TianYa.DotNetShare.CommTool:为demo的公共工具类库

5、TianYa.DotNetShare.SharpCore:为demo的Sharp核心类库

6、TianYa.DotNetShare.CoreMvcDemo:为demo的web层项目,MVC框架

约定:

1、公共的类库,我们选择.NET Standard 2.0作为目标框架,可与Framework进行共享。

2、本demo的web项目为ASP.NET Core Web 应用程序(.NET Core 2.2) MVC框架。

一、实体层

1、新建一个学生实体 Student

using System;
using System.Collections.Generic;
using System.Text;namespace TianYa.DotNetShare.Model
{/// <summary>/// 学生类/// </summary>public class Student{/// <summary>/// 学号/// </summary>public string StuNo { get; set; }/// <summary>/// 姓名/// </summary>public string Name { get; set; }/// <summary>/// 年龄/// </summary>public int Age { get; set; }/// <summary>/// 性别/// </summary>public string Sex { get; set; }}
}

demo中的实体就这样了

二、仓储层

本demo的仓储层需要引用我们的实体层TianYa.DotNetShare.Model

为什么选择用仓储,原因很简单,方便我们进行个性化扩展。在数据操作的底层进行其他个性化逻辑处理。

约定:

1、接口的定义放在根目录下,接口的实现类,统一放到Impl文件夹,表示实现类目录。

2、每个实体,对应一个仓储的接口和实现类,即有多少个实体,就对应创建多少个接口和实现类。

3、仓储层接口都以“I”开头,以“Repository”结尾。仓储层实现都以“Repository”结尾。

我们新建一个Student的仓储接口IStudentRepository.cs

using System;
using System.Collections.Generic;
using System.Text;using TianYa.DotNetShare.Model;namespace TianYa.DotNetShare.Repository
{/// <summary>/// 学生类仓储层接口/// </summary>public interface IStudentRepository{/// <summary>/// 根据学号获取学生信息/// </summary>/// <param name="stuNo">学号</param>/// <returns>学生信息</returns>Student GetStuInfo(string stuNo);}
}

接着在Impl中新建一个Student的仓储实现StudentRepository.cs

using System;
using System.Collections.Generic;
using System.Text;using TianYa.DotNetShare.Model;namespace TianYa.DotNetShare.Repository.Impl
{/// <summary>/// 学生类仓储层/// </summary>public class StudentRepository : IStudentRepository{/// <summary>/// 根据学号获取学生信息/// </summary>/// <param name="stuNo">学号</param>/// <returns>学生信息</returns>public Student GetStuInfo(string stuNo){//数据访问逻辑,此处为了演示就简单些var student = new Student();switch (stuNo){case "10000":student = new Student() { StuNo = "10000", Name = "张三", Sex = "", Age = 20 };break;case "10001":student = new Student() { StuNo = "10001", Name = "钱七七", Sex = "", Age = 18 };break;case "10002":student = new Student() { StuNo = "10002", Name = "李四", Sex = "", Age = 21 };break;default:student = new Student() { StuNo = "10003", Name = "王五", Sex = "", Age = 25 };break;}return student;}}
}

该类实现了IStudentRepository接口

三、服务层

本demo的服务层需要引用我们的实体层TianYa.DotNetShare.Model和我们的仓储层TianYa.DotNetShare.Repository

服务层与仓储层类似,它属于仓储层的使用者。定义的方式也与仓储层类似,有接口和Impl实现目录。

但服务层不需要一个实体对应一个,服务层更多的是按照功能模块进行划分,比如一个登录模块,创建一个LoginService。

约定:

1、服务层接口都以“I”开头,以“Service”结尾。服务层实现都以“Service”结尾。

为了演示,我们新建一个Student的服务层接口IStudentService.cs

using System;
using System.Collections.Generic;
using System.Text;using TianYa.DotNetShare.Model;namespace TianYa.DotNetShare.Service
{/// <summary>/// 学生类服务层接口/// </summary>public interface IStudentService{/// <summary>/// 根据学号获取学生信息/// </summary>/// <param name="stuNo">学号</param>/// <returns>学生信息</returns>Student GetStuInfo(string stuNo);}
}

接着我们同样在Impl中新建一个Student的服务层实现StudentService.cs

using System;
using System.Collections.Generic;
using System.Text;using TianYa.DotNetShare.Model;
using TianYa.DotNetShare.Repository;namespace TianYa.DotNetShare.Service.Impl
{/// <summary>/// 学生类服务层/// </summary>public class StudentService : IStudentService{/// <summary>/// 定义仓储层学生抽象类对象/// </summary>protected IStudentRepository StuRepository;/// <summary>/// 空构造函数/// </summary>public StudentService() { }/// <summary>/// 构造函数/// </summary>/// <param name="stuRepository">仓储层学生抽象类对象</param>public StudentService(IStudentRepository stuRepository){this.StuRepository = stuRepository;}/// <summary>/// 根据学号获取学生信息/// </summary>/// <param name="stuNo">学号</param>/// <returns>学生信息</returns>public Student GetStuInfo(string stuNo){var stu = StuRepository.GetStuInfo(stuNo);return stu;}}
}

该类实现了IStudentService接口

四、公共工具类库

公共工具类库就是将来我们要在里面写各种各样的帮助类以提高程序的可复用性,此处就不做赘述。

五、Sharp核心类库

需要从NuGet上引用以下几个程序集:

Sharp核心类库为公共的基础类,最底层。

其中Model文件夹为实体目录,主要存放数据库连接相关的实体。Extensions文件夹为扩展目录,主要存放最底层的扩展类,我们底层的批量依赖注入就放在这里面。

在Model实体目录中我们新建一个用于数据库连接的接口IDataBaseSetting.cs

using System;
using System.Collections.Generic;
using System.Text;namespace TianYa.DotNetShare.SharpCore.Model
{public interface IDataBaseSetting{/// <summary>/// 访问数据库连接串/// </summary>string ConnectionString { get; set; }/// <summary>/// 数据库名称,当是关系型数据库时,DatabaseName属性没用到/// </summary>string DatabaseName { get; set; }}
}

接着添加一个用于数据库连接的实现类DataBaseSetting.cs

using System;
using System.Collections.Generic;
using System.Text;namespace TianYa.DotNetShare.SharpCore.Model
{public class DataBaseSetting : IDataBaseSetting{/// <summary>/// 访问数据库连接串/// </summary>public string ConnectionString { get; set; }/// <summary>/// 数据库名称,当是关系型数据库时,DatabaseName属性没用到/// </summary>public string DatabaseName { get; set; }}
}

该类实现了IDataBaseSetting.cs接口

Model实体目录主要用于以后涉及到数据库访问的时候使用,本demo主要为了简单介绍下如何使用ASP.NET Core内置的IoC容器DI进行批量依赖注入,故没有对该实体目录进行详细的讲解。

接下来就是重头戏了,我们在Extensions扩展目录中添加一个用于批量依赖注入的扩展类ServiceCollectionExtensions.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyModel;using TianYa.DotNetShare.SharpCore.Model;namespace TianYa.DotNetShare.SharpCore.Extensions
{/// <summary>/// ServiceCollection扩展/// </summary>public static class ServiceCollectionExtensions{#region 通过反射批量注入指定的程序集/// <summary>/// 通过反射批量注入指定的程序集/// </summary>/// <param name="services">服务</param>/// <param name="assemblyNames">程序集数组 如:["TianYa.DotNetShare.Repository","TianYa.DotNetShare.Service"],无需写dll</param>public static void RegisterTianYaSharpService(this IServiceCollection services, params string[] assemblyNames){foreach (string assemblyName in assemblyNames){foreach (var itemClass in GetClassInterfacePairs(assemblyName)){foreach (var itemInterface in itemClass.Value){if (itemInterface != typeof(DataBaseSetting)){services.AddTransient(itemInterface, itemClass.Key); //DI依赖注入
                        }}}}}#endregion#region DI依赖注入辅助方法/// <summary>/// 获取类以及类实现的接口键值对/// </summary>/// <param name="assemblyName">程序集名称</param>/// <returns>类以及类实现的接口键值对</returns>private static Dictionary<Type, List<Type>> GetClassInterfacePairs(string assemblyName){//存储 实现类 以及 对应接口Dictionary<Type, List<Type>> dic = new Dictionary<Type, List<Type>>();Assembly assembly = GetAssembly(assemblyName);if (assembly != null){Type[] types = assembly.GetTypes();foreach (var item in types.AsEnumerable().Where(x => !x.IsAbstract && !x.IsInterface && !x.IsGenericType)){dic.Add(item, item.GetInterfaces().Where(x => !x.IsGenericType).ToList());}}return dic;}/// <summary>/// 获取所有的程序集/// </summary>/// <returns>程序集集合</returns>private static List<Assembly> GetAllAssemblies(){var list = new List<Assembly>();var deps = DependencyContext.Default;var libs = deps.CompileLibraries.Where(lib => !lib.Serviceable && lib.Type != "package");//排除所有的系统程序集、Nuget下载包foreach (var lib in libs){try{var assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(lib.Name));list.Add(assembly);}catch (Exception){// ignored
                }}return list;}/// <summary>/// 获取指定的程序集/// </summary>/// <param name="assemblyName">程序集名称</param>/// <returns>程序集</returns>private static Assembly GetAssembly(string assemblyName){return GetAllAssemblies().FirstOrDefault(assembly => assembly.FullName.Contains(assemblyName));}#endregion}
}

并且添加一个Dynamic的扩展类DynamicExtensions.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Dynamic;
using System.Linq;
using System.Xml;namespace TianYa.DotNetShare.SharpCore.Extensions
{/// <summary>/// Dynamic的扩展方法/// </summary>public static class DynamicExtensions{#region 匿名对象处理#region 将对象[主要是匿名对象]转换为dynamic/// <summary>/// 将对象[主要是匿名对象]转换为dynamic/// </summary>public static dynamic ToDynamic(this object value){IDictionary<string, object> expando = new ExpandoObject();var type = value.GetType();var properties = TypeDescriptor.GetProperties(type);foreach (PropertyDescriptor property in properties){var val = property.GetValue(value);if (property.PropertyType.FullName.StartsWith("<>f__AnonymousType")){dynamic dval = val.ToDynamic();expando.Add(property.Name, dval);}else{expando.Add(property.Name, val);}}return expando as ExpandoObject;}#endregion#region 将对象[主要是匿名对象]转换为List<dynamic>/// <summary>/// 将对象[主要是匿名对象]转换为List<dynamic>/// </summary>public static List<dynamic> ToDynamicList(this IEnumerable<dynamic> values){var list = new List<dynamic>();if (values != null){if (values.Any()){list.AddRange(values.Select(v => ((object)v).ToDynamic()));}}return list;}#endregion#region 将匿名对象集合转换为XML/// <summary>/// 将匿名对象集合转换为XML/// </summary>public static XmlDocument ListObjertToXML(this IEnumerable<dynamic> values){var xmlDoc = new XmlDocument();var xmlElem = xmlDoc.CreateElement("DocumentElement");xmlDoc.AppendChild(xmlElem);if (values != null){if (values.Any()){var node = xmlDoc.SelectSingleNode("DocumentElement");foreach (var item in values){var xmlRow = xmlDoc.CreateElement("Row");ObjectToXML(item, xmlDoc, xmlRow);node.AppendChild(xmlRow);}}}return xmlDoc;}#endregion#region 将匿名对象填充XML节点/// <summary>/// 将匿名对象填充XML节点/// </summary>private static void ObjectToXML(object value, XmlDocument xmlDoc, XmlElement xmlRow){IDictionary<string, object> expando = new ExpandoObject();var type = value.GetType();var properties = TypeDescriptor.GetProperties(type);foreach (PropertyDescriptor property in properties){var val = property.GetValue(value);xmlRow.CloneNode(false);var xmlTemp = xmlDoc.CreateElement(property.Name);XmlText xmlText;if (property.PropertyType.FullName.StartsWith("<>f__AnonymousType")){dynamic dval = val.ToDynamic();xmlText = xmlDoc.CreateTextNode(dval.ObjectToString());}else{xmlText = xmlDoc.CreateTextNode(val.ToString());}xmlTemp.AppendChild(xmlText);xmlRow.AppendChild(xmlTemp);}}#endregion#endregion}
}

该扩展类主要在我们的Action向视图传递匿名类型值的时候使用

六、Web层

本demo的web项目需要引用以下几个程序集:

1、TianYa.DotNetShare.Model 我们的实体层

2、TianYa.DotNetShare.Service 我们的服务层

3、TianYa.DotNetShare.Repository 我们的仓储层,正常我们的web项目是不应该使用仓储层的,此处我们引用是为了演示IoC依赖注入

4、TianYa.DotNetShare.CommTool 我们的公共工具类库

5、TianYa.DotNetShare.SharpCore 我们的Sharp核心类库

到了这里我们所有的工作都已经准备好了,接下来就是开始做注入工作了。

打开我们的Startup.cs文件进行注入工作:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;using TianYa.DotNetShare.SharpCore.Extensions;namespace TianYa.DotNetShare.CoreMvcDemo
{public class Startup{public Startup(IConfiguration configuration){Configuration = configuration;}public IConfiguration Configuration { get; }// This method gets called by the runtime. Use this method to add services to the container.public void ConfigureServices(IServiceCollection services){services.Configure<CookiePolicyOptions>(options =>{// This lambda determines whether user consent for non-essential cookies is needed for a given request.options.CheckConsentNeeded = context => true;options.MinimumSameSitePolicy = SameSiteMode.None;});services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);//DI依赖注入,批量注入指定的程序集services.RegisterTianYaSharpService(new string[] { "TianYa.DotNetShare.Repository", "TianYa.DotNetShare.Service" });}// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.public void Configure(IApplicationBuilder app, IHostingEnvironment env){if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}else{app.UseExceptionHandler("/Home/Error");}app.UseStaticFiles();app.UseCookiePolicy();app.UseMvc(routes =>{routes.MapRoute(name: "default",template: "{controller=Home}/{action=Index}/{id?}");});}}
}

其中用来实现批量依赖注入的只要一句话就搞定了,如下所示:

//DI依赖注入,批量注入指定的程序集
services.RegisterTianYaSharpService(new string[] { "TianYa.DotNetShare.Repository", "TianYa.DotNetShare.Service" });

Sharp核心类库在底层实现了批量注入的逻辑,程序集的注入必须按照先后顺序进行,先进行仓储层注入然后再进行服务层注入。

接下来我们来看看控制器里面怎么弄:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;using TianYa.DotNetShare.CoreMvcDemo.Models;
using TianYa.DotNetShare.Service;
using TianYa.DotNetShare.Repository;namespace TianYa.DotNetShare.CoreMvcDemo.Controllers
{public class HomeController : Controller{/// <summary>/// 定义仓储层学生抽象类对象/// </summary>protected IStudentRepository StuRepository;/// <summary>/// 定义服务层学生抽象类对象/// </summary>protected IStudentService StuService;/// <summary>/// 通过构造函数进行注入/// 注意:参数是抽象类,而非实现类,因为已经在Startup.cs中将实现类映射给了抽象类/// </summary>/// <param name="stuRepository">仓储层学生抽象类对象</param>/// <param name="stuService">服务层学生抽象类对象</param>public HomeController(IStudentRepository stuRepository, IStudentService stuService){this.StuRepository = stuRepository;this.StuService = stuService;}public IActionResult Index(){var stu1 = StuRepository.GetStuInfo("10000");var stu2 = StuService.GetStuInfo("10001");string msg = $"学号:10000,姓名:{stu1.Name},性别:{stu1.Sex},年龄:{stu1.Age}<br />";msg += $"学号:10001,姓名:{stu2.Name},性别:{stu2.Sex},年龄:{stu2.Age}";return Content(msg, "text/html", System.Text.Encoding.UTF8);}public IActionResult Privacy(){return View();}[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]public IActionResult Error(){return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });}}
}

至此,完成处理,接下来就是见证奇迹的时刻了,我们来访问一下/home/index,看看是否能返回学生信息。

我们可以发现,返回了学生的信息,说明我们注入成功了。

另外通过这个例子我们可以发现在注入仓储层对象StudentRepository时,不仅控制器中注入成功了,而且在服务层中也注入成功了,说明我们ASP.NET Core内置的IoC容器DI依赖注入是全局的。

总结:

1、采用的是构造函数注入的方式,在构造函数中初始化赋值。

2、ASP.NET Core内置的IoC容器DI依赖注入是全局的。

3、DI批量依赖注入的核心思想就是根据程序集的名称通过反射获取类以及类实现的接口键值对字典,然后通过循环进行批量注入。

 

扩展:DI生命周期

生命周期是依赖注入设计原则里一个非常重要的概念,ASP.NET Core 一共有3种生命周期。

1、暂时(Transient):顾名思义,这种生命周期的对象是暂时的,每次请求都会创建一个新的实例。

services.AddTransient<IStudentRepository, StudentRepository>();
services.AddTransient<IStudentService, StudentService>();

2、作用域(Scoped):每次请求使用的是同一个实例。

services.AddScoped<IStudentRepository, StudentRepository>();
services.AddScoped<IStudentService, StudentService>();

3、单例(Singleton):第一次请求时就创建,以后每次请求都是使用相同的实例。

services.AddSingleton<IStudentRepository, StudentRepository>();
services.AddSingleton<IStudentService, StudentService>();

官方文档建议:依赖注入是静态/全局对象访问模式的替代方法,如果将其与静态对象访问混合使用,则可能无法实现依赖关系注入的优点。

至此,本章就介绍完了,如果你觉得这篇文章对你有所帮助请记得点赞哦,谢谢!!!

demo源码:

链接:https://pan.baidu.com/s/17GIgvp0JWy8BaNOE8l6p9A 
提取码:i9hh

 

参考博文:https://www.cnblogs.com/fei686868/p/11077997.html

版权声明:如有雷同纯属巧合,如有侵权请及时联系本人修改,谢谢!!!

转载于:https://www.cnblogs.com/xyh9039/p/11373269.html

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

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

相关文章

苹果电脑基本设置+Linux 命令+Android 实战集锦

本文微信公众号「AndroidTraveler」首发。 背景 大多数应届毕业生在大学期间使用的比较多的是 windows 电脑&#xff0c;因此初入职场如果拿到一台苹果电脑&#xff0c;可能一时间不能够很快的上手。基于此&#xff0c;这边出了系列视频&#xff0c;通过实际的演示让没使用过苹…

Mac中AndroidStudio没有找到Plugins的问题

我们在windows中都可以正常找到plugins 但是在Mac上AndroidStudio里 setting打开却没有plugins 正准备在Mac上搞一下flutter呢 我感觉智商受到了侮辱&#xff01; 这里其实是mac版本给我开了个玩笑 你可以按快捷键&#xff0c;你就可以找到 快捷键 command ‘,’ 没错就是comm…

进程和操作系统概述

进程和操作系统概述 进程的基础 程序和进程&#xff1a; 程序是一对静态的代码文件 进程是一个正在运行着的程序&#xff0c;抽象概念 进程由操作系统操控调用交于CPU运行 操作系统 1.管理控制协调计算机硬件和软件的关系 2.操作系统的作用&#xff1f; ​ 第一个作用&#xff…

对前端Jenkins自动化部署的研究

1. 安装 安装 Nginx 1.1去官网下直接下载&#xff0c;解压缩 start nginx就可以使了&#xff0c;常用命令&#xff1a; start nginx # 启动 nginx -s reload # 修改配置后重新加载生效 nginx -s reopen # 重新打开日志文件 nginx -t # 配置文件检测是否正确 1.2 安装Jenkins…

ASP.NET Core Web 应用程序系列(二)- 在ASP.NET Core中使用Autofac替换自带DI进行批量依赖注入(MVC当中应用)...

在上一章中主要和大家分享在MVC当中如何使用ASP.NET Core内置的DI进行批量依赖注入&#xff0c;本章将继续和大家分享在ASP.NET Core中如何使用Autofac替换自带DI进行批量依赖注入。 PS&#xff1a;本章将主要采用构造函数注入的方式&#xff0c;下一章将继续分享如何使之能够同…

Java过滤器与SpringMVC拦截器之间的关系与区别

今天学习和认识了一下&#xff0c;过滤器和SpringMVC的拦截器的区别&#xff0c;学到了不少的东西&#xff0c;以前一直以为拦截器就是过滤器实现的&#xff0c;现在想想还真是一种错误啊&#xff0c;而且看的比较粗浅&#xff0c;没有一个全局而又细致的认识&#xff0c;由于已…

二次幂权限设计

设置含有的权限如增删改查减为1,2,4,8,16 如果A包含增删改这5个权限&#xff0c;那A的值为1247 如果B包含增改查这5个权限&#xff0c;那A的值为14813 如果C包含增删改查减这5个权限&#xff0c;那A的值为12481631 7二进制为111,13的二进制为1101,31二进制为11111 1二进制为1&a…

使用putty在linux主机和windows主机之间拷贝文件(已测试可执行)

转载于&#xff0c;请点击 首先下载putty&#xff0c;putty下载地址zip&#xff0c; 解压zip发现里面有plink.exe pscp.exe psftp.exe putty.exe puttygen.exe puttytel.exe等可执行文件&#xff0c;如果只是想要链接主机做一些操作那么使用putty.exe&#xff0c;要想要上传 …

ASP.NET Core Web 应用程序系列(三)- 在ASP.NET Core中使用Autofac替换自带DI进行构造函数和属性的批量依赖注入(MVC当中应用)...

在上一章中主要和大家分享了在ASP.NET Core中如何使用Autofac替换自带DI进行构造函数的批量依赖注入&#xff0c;本章将和大家继续分享如何使之能够同时支持属性的批量依赖注入。 约定&#xff1a; 1、仓储层接口都以“I”开头&#xff0c;以“Repository”结尾。仓储层实现都以…

可视化caffe模型结构及在线可视化

在线可视化caffe模型结构 http://ethereon.github.io/netscope/#/editor 假设Caffe的目录是$(CAFFE_ROOT) 1.编译caffe的python接口 $ make pycaffe 2.装各种依赖 $ pip install pydot $ sudo apt-get install graphviz 3.可视化模型 draw_net.py执行的时候带三个参数 …

iOS核心动画之CALayer(1)

本文目录 一、什么是CALayer二、CALayer的简单使用 回到顶部一、什么是CALayer * 在iOS系统中&#xff0c;你能看得见摸得着的东西基本上都是UIView&#xff0c;比如一个按钮、一个文本标签、一个文本输入框、一个图标等等&#xff0c;这些都是UIView。 * 其实UIView之所以能显…

ASP.NET Core Web 应用程序系列(四)- ASP.NET Core 异步编程之async await

PS&#xff1a;异步编程的本质就是新开任务线程来处理。 约定&#xff1a;异步的方法名均以Async结尾。 实际上呢&#xff0c;异步编程就是通过Task.Run()来实现的。 了解线程的人都知道&#xff0c;新开一个线程来处理事务这个很常见&#xff0c;但是在以往是没办法接收线程里…

iOS核心动画之CALayer-layer的创建

本文目录 一、添加一个简单的图层二、添加一个显示图片的图层三、为什么CALayer中使用CGColorRef和CGImageRef这2种数据类型&#xff0c;而不用UIColor和UIImage&#xff1f;四、UIView和CALayer的选择五、UIView和CALayer的其他关系 * 上一讲已经说过&#xff0c;UIView内部默…

iOS核心动画之CALayer-隐式动画

本文目录 一、隐式动画属性二、position和anchorPoint 回到顶部一、隐式动画属性 * 在前面几讲中已经提到&#xff0c;每一个UIView内部都默认关联着一个CALayer&#xff0c;我们可用称这个Layer为Root Layer&#xff08;根层&#xff09;。所有的非Root Layer&#xff0c;也就…

iOS核心动画之CALayer-自定义层

本文目录 一、自定义层的方法1二、自定义层的方法2 三、其他 自定义层&#xff0c;其实就是在层上绘图&#xff0c;一共有2种方法&#xff0c;下面详细介绍一下。 回到顶部一、自定义层的方法1 方法描述&#xff1a;创建一个CALayer的子类&#xff0c;然后覆盖drawInContext:方…

iOS核心动画之CoreAnimation

本文目录一、Core Animation简介二、Core Animation的使用步骤三、CAAnimation四、CAPropertyAnimation 回到顶部一、Core Animation简介 * Core Animation&#xff0c;中文翻译为核心动画&#xff0c;它是一组非常强大的动画处理API&#xff0c;使用它能做出非常炫丽的动画效果…

iOS核心动画 Core Animation2-CABasicAnimation

Core Animation2-CABasicAnimation 本文目录 一、平移动画二、缩放动画三、旋转动画四、其他 CABasicAnimation是CAPropertyAnimation的子类&#xff0c;使用它可以实现一些基本的动画效果&#xff0c;它可以让CALayer的某个属性从某个值渐变到另一个值。下面就用CABasicAnimat…

OpenStack Telemetry系统架构及实践

1. 概述 早期OpenStack的计量功能由Ceilometer项目负责&#xff0c;后来Ceilometer一分为四&#xff0c;每个项目负责一个方面的工作。不得不说这是OpenStack开发中的一个特色&#xff0c;比如Cinder和Neutron也是从早期的Nova中拆分出来的。 OpenStack Telemetry体系的架构如下…

产品经理十二时辰:内容过于真实,扎心了!

各大平台也纷纷借势&#xff1a; 《阿里十二时辰》 《优酷十二时辰》 《垃圾十二时辰》 《深圳十二时辰》 《北京十二时辰》 《考研人十二时辰》 …… 各种十二时辰刷爆朋友圈。 后台很多留言&#xff1a; 起点学院怎么没有出《产品经理十二时辰》呢&#xff1f;你这产品经理教…

iOS持久化存储-CoreData简介

本文目录 一、简介二、模型文件三、了解NSManagedObject对象四、CoreData中的核心对象五、代码实现六、打开CoreData的SQL语句输出开关七、创建NSManagedObject的子类 回到顶部一、简介 Core Data是iOS5之后才出现的一个框架&#xff0c;它提供了对象-关系映射(ORM)的功能&…