在正式进入主题之前我们来看下几个概念:
一、依赖倒置
依赖倒置是编程五大原则之一,即:
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 "":
student = new Student() { StuNo = "", Name = "张三", Sex = "男", Age = };
break;
case "":
student = new Student() { StuNo = "", Name = "钱七七", Sex = "女", Age = };
break;
case "":
student = new Student() { StuNo = "", Name = "李四", Sex = "男", Age = };
break;
default:
student = new Student() { StuNo = "", Name = "王五", Sex = "男", Age = };
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("");
var stu2 = StuService.GetStuInfo("");
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 = , 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
版权声明:如有雷同纯属巧合,如有侵权请及时联系本人修改,谢谢!!!
ASP.NET Core Web 应用程序系列(一)- 使用ASP.NET Core内置的IoC容器DI进行批量依赖注入(MVC当中应用)的更多相关文章
-
ASP.NET Core Web 应用程序系列(二)- 在ASP.NET Core中使用Autofac替换自带DI进行批量依赖注入(MVC当中应用)
在上一章中主要和大家分享在MVC当中如何使用ASP.NET Core内置的DI进行批量依赖注入,本章将继续和大家分享在ASP.NET Core中如何使用Autofac替换自带DI进行批量依赖注入. P ...
-
ASP.NET Core Web 应用程序系列(五)- 在ASP.NET Core中使用AutoMapper进行实体映射
本章主要简单介绍下在ASP.NET Core中如何使用AutoMapper进行实体映射.在正式进入主题之前我们来看下几个概念: 1.数据库持久化对象PO(Persistent Object):顾名思义 ...
-
ASP.NET Core Web 应用程序系列(三)- 在ASP.NET Core中使用Autofac替换自带DI进行构造函数和属性的批量依赖注入(MVC当中应用)
在上一章中主要和大家分享了在ASP.NET Core中如何使用Autofac替换自带DI进行构造函数的批量依赖注入,本章将和大家继续分享如何使之能够同时支持属性的批量依赖注入. 约定: 1.仓储层接口 ...
-
ASP.NET Core Web 应用程序系列(四)- ASP.NET Core 异步编程之async await
PS:异步编程的本质就是新开任务线程来处理. 约定:异步的方法名均以Async结尾. 实际上呢,异步编程就是通过Task.Run()来实现的. 了解线程的人都知道,新开一个线程来处理事务这个很常见,但 ...
-
Asp.Net Core Web应用程序—探索
前言 作为一个Windows系统下的开发者,我对于Core的使用机会几乎为0,但是考虑到微软的战略规划,我觉得,Core还是有先了解起来的必要. 因为,目前微软已经搞出了两个框架了,一个是Net标准( ...
-
使用docker部署Asp.net core web应用程序
拉取aspnetcore最新docker镜像 aspnetcore的docker镜像在docker官网是有的,是由微软提供的.它的依赖镜像是microsoft/dotnet.通过访问网址:https: ...
-
ASP.NET Core Web 应用程序开发期间部署到IIS自定义主机域名并附加到进程调试
想必大家之前在进行ASP.NET Web 应用程序开发期间都有用到过将我们的网站部署到IIS自定义主机域名并附加到进程进行调试. 那我们的ASP.NET Core Web 应用程序又是如何部署到我们的 ...
-
循序渐进学.Net Core Web Api开发系列【0】:序言与目录
一.序言 我大约在2003年时候开始接触到.NET,最初在.NET framework 1.1版本下写过代码,曾经做过WinForm和ASP.NET开发.大约在2010年的时候转型JAVA环境,这么多 ...
-
循序渐进学.Net Core Web Api开发系列【7】:项目发布到CentOS7
系列目录 循序渐进学.Net Core Web Api开发系列目录 本系列涉及到的源码下载地址:https://github.com/seabluescn/Blog_WebApi 一.概述 本篇讨论如 ...
随机推荐
-
验证管理员权限(C#)
参考页面: http://www.yuanjiaocheng.net/webapi/test-webapi.html http://www.yuanjiaocheng.net/webapi/web-a ...
-
knockoutJS+knockout.multimodels使用记录
可以多次绑定,但不能嵌套绑定 错误示例: <div class="con_c" data-model="viewModel"> <div da ...
-
四个使用this的典型应用
(1)在html元素事件属性中使用,如 <input type=”button” onclick=”showInfo(this);” value=”点击一下”/> (2)构造函数 func ...
-
linux设备驱动程序第四部分:从如何定位oops对代码的调试方法,驱动线
在一个我们谈到了如何编写一个简单的字符设备驱动程序,我们不是神,编写肯定会失败的代码,在这个过程中,我们需要继续写代码调试.在普通c应用.我们经常使用printf输出信息.或者使用gdb要调试程序,然 ...
-
css清除浮动的集中方法
一:浮动产生的副作用 1.父元素的背景不能显示 2.父元素的边框不能撑开 3.padding和margin失效 二:清除浮动的方法 1.给父元素设置高度:这样可以清除浮动,但是子元素内容高度不固定,这 ...
-
团队作业7---Alpha冲刺值事后诸葛
一.设想和目标 1.我们的软件要解决什么问题? 解决教师和助教对实验报告查重的问题,拥有两个用户:1.教师或助教:查看学生实验报告的重复率:4.学生:上传实验报告. 2.是否定义得很清楚?是否对典型用 ...
-
maven项目使用jacoco插件检测代码覆盖率详细配置
使用maven构建项目(java项目或者web项目都可以) jacoco插件的配置参考官方网址:http://www.eclemma.org/jacoco/trunk/doc/maven.html ( ...
-
day04 流程控制
在python中流程控制主要有三种:顺序流程.分支流程.循环流程 1.顺序流程:在宏观上,python程序的运行就是自上而下的顺序流程: 2.分支流程:分支流程主要是 if...else....流程 ...
-
OpenDCIM-19.01操作手册
OpenDCIM-19.01操作手册 1. 界面标签解析 1.1 用户管理 用户管理 部门管理 用户管理被存在数据表fac_User中,包含以下字段: UserID:是管理员还是用户 Name:报表 ...
-
Python操纵Excel,数据库
操作excelxlwt:写入excel表格 ,用这个之前需要先导入模块 xlwt: import xlwtxlrd:读取excel,用这个之前需要先导入模块 xlwt:import xlrd 注意:e ...