ADO.NET
一、ADO.NET概要
ADO.NET是.NET框架中的重要组件,主要用于完成C#应用程序访问数据库
二、ADO.NET的组成
①System.Data → DataTable,DataSet,DataRow,DataColumn,DataRelation,Constraint,DataColumnMapping,DataTableMapping
②System.Data.Coummon → 各种数据访问类的基类和接口
③System.Data.SqlClient → 对Sql Server进行操作的数据访问类
主要有: a) SqlConnection → 数据库连接器
b) SqlCommand → 数据库命名对象
c) SqlCommandBuilder → 生存SQL命令
d) SqlDataReader → 数据读取器
e) SqlDataAdapter → 数据适配器,填充DataSet
f) SqlParameter → 为存储过程定义参数
g) SqlTransaction → 数据库事物
三、Connection连接对象
Connection对象也称为数据库连接对象,Connection对象的功能是负责对数据源的连接。所有Connection对象的基类都是DbConnection类。
3.1、连接字符串
基本语法:数据源(Data Source)+数据库名称(Initial Catalog)+用户名(User ID)+密码(Password)
3.1.1、SQL Server连接字符串
Data Source=.;Initial Catalog=myDataBase;User Id=myUsername;Password=myPassword;
3.1.2、Access连接字符串
Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\myDatabase.mdb;User Id=admin;Password=;
3.1.3、MySQL连接字符串
Server=myServerAddress;Database=myDatabase;Uid=myUsername;Pwd=myPassword;
3.1.4、DB2连接字符串
Server=myAddress:myPortNumber;Database=myDatabase;UID=myUsername;PWD=myPassword;
3.1.5、Oracle连接字符串
Data Source=TORCL;User Id=myUsername;Password=myPassword;
在VS中获得连接字符串并连接到数据库:工具->连接到数据库
3.2、连接到数据库
Connection对象有两个重要属性:
(1)ConnectionString:表示用于打开 SQL Server 数据库的字符串;
(2)State:表示 Connection 的状态,有Closed和Open两种状态。
Connection对象有两个重要方法:
(1)Open()方法:指示打开数据库;
(2)Close()方法:指示关闭数据库。
四、Command对象
Command对象也称为数据库命令对象,Command对象主要执行包括添加、删除、修改及查询数据的操作的命令。也可以用来执行存储过程。用于执行存储过程时需要将Command对象的CommandType 属性设置为CommandType.StoredProcedure,默认情况下CommandType 属性为CommandType.Text,表示执行的是普通SQL语句。
Command主要有三个方法:
4.1、ExecuteNonQuery
ExecuteNonQuery():执行一个非查询SQL语句,返回受影响的行数,这个方法主要用于执行对数据库执行增加、更新、删除操作,注意查询的时候不是调用这个方法。用于完成insert,delete,update操作。执行时返回影响行数。
using 关键字有两个主要用途:
(一).作为指令,用于为命名空间创建别名或导入其他命名空间中定义的类型。
(二).作为语句,用于定义一个范围,在此范围的末尾将释放对象。
using只能用于实现了IDisposable接口的类型,禁止为不支持IDisposable接口的类型使用using语句,否则会出现编译错误;
4.1.1、拼接字符串
string str = string.Format("Data Source={0};Initial Catalog={1};User ID={2};Password={3}", txtSQLAdd.Text, txtSqlName.Text, txtUser.Text, txtPwd.Text);
4.1.2、参数
如果直接拼接字符串会存在安全隐患,使用参数可以解决问题。
4.1.3、增加、删除、修改
这里的示例是insert,如果想执行delete与update代码是一样的,只是变化了SQL。
增加:insert [into] <表名> (列名) values (列值) 例:insert into Strdents (姓名,性别,出生日期) values ('开心朋朋','男','1980/6/15')
删除:delete from <表名> [where <删除条件>] 例:delete from a where name='开心朋朋'(删除表a中列值为开心朋朋的行)
修改:update <表名> set <列名=更新值> [where <更新条件>] 例:update tongxunlu set 年龄=18 where 姓名='蓝色小名'
4.2、ExecuteScalar ()
ExecuteScalar ()从数据库检索单个值。这个方法主要用于统计操作。ExecuteScalar ()这个方法是针对SQL语句执行的结果是一行一列的结果集,这个方法只返回查询结果集的第一行第一列。针对SQL聚合函数(count,max,min,avg,sum)使用。
聚合函数:select count(*) from <表名>
可能返回NULL值,需要对结果进行判断。
if (object.Equals(my,null)) //可以使用Equals进行Null值的判断,易读性强
Console.WriteLine("Not Data");
else
Console.WriteLine("Yes");
五、ExecuteReader获得数据
ExecuteReader用于实现只进只读的高效数据查询。用来读取一个数据表。用于查询一个数据表。
ExecuteReader:返回一个SqlDataReader对象,可以通过这个对象检查查询结果。它提供的是只进只读的执行方式,读取一行之后,移动到下一行,上一行的数据就不能使用了。手动调用Read()方法之后,SqlDataReader对象才会移到结果集的第一行。Read()方法会返回一个bool值,True则下行可用,false表示下行不可用。
5.1、使用ExecuteReader实现数据查询
//执行查询返回结果集
SqlDataReader sdr = cmd.ExecuteReader();
//下移游标,读取一行,如果没有数据了则返回false
while (sdr.Read())
{
Console.WriteLine("编号:" + sdr["Id"] + ",车名:" + sdr["Title"] + ",速度:" + sdr["Speed"]);
}
5.2、实体类
实体类用于封装及映射数据。
5.3、DataGridView展示数据
定义一个表示对象的类
定义一个对象的列表
初始化一个对象,用sqlReader()将数据读出后存入对象。
把对象加入对象列表中。
对象列表与DataGridView 显示。
窗口间的传值
一个窗口定义一个对象【1】
在另外一个窗口定义一个对象2
显示窗口的时候把1赋值给2
在窗口2加载的时候把2的属性显示出来
上一篇,我们介绍了如何读取自定义的json文件,数据是读取出来了,只是处理的时候太麻烦,需要一遍一遍写,很枯燥.那么有没有很好的办法呢?经过钻研,办法有了.
既然一个一个读取比较麻烦,那么可以把它放入一个类里面么?当然可以.这样直接注入这个类,让控制器去读取,那么就不需要每次加载它了,一次加载就可以在任何地方使用了.
由于本文是接这上一篇文章的更新介绍,请先读取上一篇文章再浏览此篇文章
(1):在Models文件夹中添加类Class,并填写如下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
namespace CoreDemo.Models
{ public class Class
{
public int ClassNum { get ; set ; }
public string ClassName { get ; set ; }
public List<Student> Students { get ; set ; }
}
public class Student
{
public string name { get ; set ; }
public int age { get ; set ; }
}
} |
(2):更新Startup.cs脚本,更新函数ConfigureServices:
1
2
3
4
5
6
|
public void ConfigureServices(IServiceCollection services)
{
var classSetting = JsonConvert.DeserializeObject<Class>(File.ReadAllText( "JsonFile/class.json" ));
services.AddSingleton(classSetting); //全局注入
services.AddMvc();
}
|
(3):更新Home控制器,将配置类注入进来并使用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public class HomeController : Controller
{
private readonly Class _classSetting;
public HomeController(Class classSetting)
{
_classSetting = classSetting;
}
public IActionResult Index()
{
return Content( "班级编号=" + _classSetting.ClassNum + ",班级名称=" + _classSetting.ClassName + "\r"
+ "学生名称=" + _classSetting.Students[0].name+ ",学生年纪=" + _classSetting.Students[0].age
);
}
}
|
(4):运行项目,查看运行结果,成功:
总结:如果将配置文件一个一个读取的话是可行的,只是太麻烦,如果其他地方也需要用到的话又需要再次加载读取,浪费开发效率与运行效率.直接以类的方式加载并注入,可以让它在每个地方运行,消耗最少,效率最高.
查了下 转PDF的各种框架 发现大部分都是收费的。 发现一款免费的iTextSharp 就想玩一下
只是简单做个HTML 转PDF 没有过深的探究。
首先 我在项目中引入iTextSharp 还是老方法 要么 NuGet上下载 或者 直接命令
然后在项目中建了一个HTML 里面就是一些简单的标签 和一些占位符
然后建了个PDF帮助类 里面实现具体转换的方法
public class PDFHelp { public byte[] ConvertHtmlTextToPDF(string htmltext) { if (string.IsNullOrEmpty(htmltext)) { return null; } //避免htmlText没有任何html tag标签的純文字时,转PDF时会挂掉,所以一律加上<p>标签 //htmlText = "<p>" + htmltext + "</p>"; MemoryStream stream = new MemoryStream(); byte[] data = Encoding.UTF8.GetBytes(htmltext); MemoryStream msInput = new MemoryStream(data); Document doc = new Document(); PdfWriter writer = PdfWriter.GetInstance(doc, stream); //指定文件默认缩放标准100% PdfDestination pdfDest = new PdfDestination(PdfDestination.XYZ, 0, doc.PageSize.Height, 1f); doc.Open(); //使用XMLWorkerHelper把Html parse到PDF XMLWorkerHelper .GetInstance().ParseXHtml(writer, doc, msInput, null, Encoding.UTF8, new UnicodeFontFactory()); //將pdfDest 写入到PDF PdfAction action = PdfAction.GotoLocalPage(1, pdfDest, writer); writer.SetOpenAction(action); doc.Close(); msInput.Close(); stream.Close(); //回传PDF return stream.ToArray(); } }
占位符的简单替换 测试下。只写了三个 举个例子
public string Placeholderfill(string htmlContext) { var entity = _shopingRepository.FindAll().FirstOrDefault(); htmlContext = htmlContext.Replace("{{SHOPINGNAME}}", entity.ShopingName); htmlContext = htmlContext.Replace("{{SHOPINGPric}}", entity.ShopingPric.ToString()); htmlContext = htmlContext.Replace("{{SHOPINGNuber}}", entity.ShopingCount.ToString()); return htmlContext; }
接下了就是控制器 的调用了
public FileResult ExportPDF() { string htmlContext = System.IO.File.ReadAllText(Server.MapPath("~/HTMLTemplate/ShopingBuy.html")); var context= _iShopingServer.Placeholderfill(htmlContext); PDFHelp pf = new PDFHelp(); var ms= pf.ConvertHtmlTextToPDF(context); return File(ms,"application/pdf", "shoping"+ DateTime.Now+ ".pdf"); }
在页面上写个点击PDF下载的按钮 Ajax 调用这个方法 然后就是最终实现的效果。 写的很粗糙。 但实现的过程还是比较容易的。
一般业务都是转PDF然后加入邮箱附件 用来发邮件的。
这里只是简单实现。 有时间还得继续深入研究
先简单了解一这个几个 名词的意思。
控制反转(IOC) 依赖注入(DI) 并不是某种技术。 而是一种思想。一种面向对象编程法则
什么是控制反转(IOC)? 什么是依赖注入(DI)
可以点击下面链接 理解的比较详细
https://blog.csdn.net/PacosonSWJTU/article/details/52786216
https://www.cnblogs.com/Mr-Rocker/p/7721824.html
控制反转(IOC)
在之前传统应用程序 我们都是在类内部主动实例化依赖对象,从而导致类与类之间高耦合,难于测试
可以看到下图类与类之间 依赖关系很紧密
(图是我盗的。感谢做这个图的大佬)
IOC 就是一种容器 把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是 松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。
IOC很好的体现了面向对象设计法则之一—— 好莱坞法则:“别找我们,我们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。
依赖注入(DI)
依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台
依赖谁? 依赖于IOC容器
注入谁 ? 由IoC容器注入应用程序某个对象,应用程序依赖的对象;
被依赖者并不是有依赖者主动去初始化 而是有提供资源的外部创建者决定
两者关系也就是 被注入对象依赖IoC容器配置依赖对象
说到DI IOC 就有必要了解下 现在用的最多的AutoFac
首先在项目中引入 AutoFac 如果你是WebAPI 你可以输入 AutoFac.WebApi2
我定义了 两个仓储类 IRepositoryBase 和 RepositoryBase 主要用于操作数据库的各种方法
public interface IRepositoryBase<T> where T:class { IQueryable<T> FindAll(); T FindSingle(Expression<Func<T, bool>> exp = null); T Find(int id); IQueryable<T> Find(Expression<Func<T, bool>> exp = null); /// <summary> /// Linq表达式树查询分页 /// </summary> /// <returns></returns> IQueryable<T> Find(int pageindex = 1, int pagesize = 10, Expression<Func<T, bool>> exp = null); /// <summary> /// 得到受影响条数 /// </summary> /// <returns></returns> int GetCount(Expression<Func<T, bool>> exp = null); void Add(T entity); void AddBatch(T[] entitys); void AddBatch(List<T> entitys); /// <summary> /// 更新实体所有属性 /// </summary> /// <returns></returns> void Update(T entity); void Delete(T entity); /// <summary> /// 按指定的ID进行批量更新 /// </summary> void Update(Expression<Func<T, object>> identityExp,T entity); T Update(T entity,int id); /// <summary> /// 批量删除 /// </summary> void Delete(Expression<Func<T, bool>> exp); void Save(); int ExecuteSql(string sql); }
继承接口并实现
public class RepositoryBase<T> : IRepositoryBase<T> where T:class { protected OpenSPDBContext openSPDBContext = new OpenSPDBContext(); public void Add(T entity) { openSPDBContext.Set<T>().Add(entity); Save(); } public void AddBatch(T[] entitys) { openSPDBContext.Set<T>().AddRange(entitys); Save(); } public void Delete(Expression<Func<T, bool>> exp) { var entitys= openSPDBContext.Set<T>().Where(exp); openSPDBContext.Set<T>().RemoveRange(entitys); } public void Delete(T entity) { openSPDBContext.Set<T>().Remove(entity); Save(); } public int ExecuteSql(string sql) { return openSPDBContext.Database.ExecuteSqlCommand(sql); } public IQueryable<T> Find(Expression<Func<T, bool>> exp = null) { return Filter(exp); } public T Find(int id) { return openSPDBContext.Set<T>().Find(id); } public IQueryable<T> Find(int pageindex, int pagesize, Expression<Func<T, bool>> exp = null) { return Filter(exp).Skip(pagesize * (pageindex - 1)).Take(pagesize); } public IQueryable<T> FindAll() { return openSPDBContext.Set<T>(); } public T FindSingle(Expression<Func<T, bool>> exp = null) { return openSPDBContext.Set<T>().AsNoTracking().FirstOrDefault(exp); } public int GetCount(Expression<Func<T, bool>> exp = null) { return Filter(exp).Count(); } public void Update(T entity) { openSPDBContext.Entry(entity).State = EntityState.Modified; Save(); } /// <summary> /// 按指定id更新实体,会更新整个实体 /// </summary> /// <param name="identityExp">The identity exp.</param> /// <param name="entity">The entity.</param> public void Update(Expression<Func<T, object>> identityExp, T entity) { openSPDBContext.Set<T>().AddOrUpdate(identityExp, entity); } public IQueryable<T> Filter(Expression<Func<T,bool>> exp) { var dbset = openSPDBContext.Set<T>().AsQueryable(); if (exp != null) dbset = dbset.Where(exp); return dbset; } public void Save() { try { openSPDBContext.SaveChanges(); } catch (DbEntityValidationException e) { throw new Exception(e.EntityValidationErrors.First().ValidationErrors.First().ErrorMessage); throw; } } public void AddBatch(List<T> entitys) { openSPDBContext.Set<T>().AddRange(entitys); } public T Update(T entity, int id) { openSPDBContext.Entry(entity).State = EntityState.Modified; Save(); return openSPDBContext.Set<T>().Find(id); } }
然后 在全局类中注册依赖配置
我新建了个逻辑处理层 Server 用来方便控制调用 这里我依赖了IRepositoryBase<T>
public class ShopingServer : IShopingServer { private IRepositoryBase<ShopingInfo> _shopingRepository; public ShopingServer(IRepositoryBase<ShopingInfo> shopingRepository) { _shopingRepository = shopingRepository; } public IQueryable<ShopingInfo> GetAll() { var ss = _shopingRepository.FindAll(); return ss; } }
然后控制器 注入 (这里 我依赖了IRepositoryBase IShopingServer) IShopingServer 是我定义的服务类接口 这里我就不贴出来了
我们 分别调用一下 IRepositoryBase IShopingServer 里面的一个方法 测试一下
可以看到 已经注入成功了 小菜也有梦想。 每天一点点
1、InstancePerDependency
对每一个依赖或每一次调用创建一个新的唯一的实例。这也是默认的创建实例的方式。
2、InstancePerLifetimeScope
在一个生命周期域中,每一个依赖或调用创建一个单一的共享的实例,且每一个不同的生命周期域,实例是唯一的,不共享的。
3、InstancePerMatchingLifetimeScope
在一个做标识的生命周期域中,每一个依赖或调用创建一个单一的共享的实例。打了标识了的生命周期域中的子标识域中可以共享父级域中的实例。若在整个继承层次中没有找到打标识的生命周期域,则会抛出异常:DependencyResolutionException。
4、InstancePerOwned
在一个生命周期域中所拥有的实例创建的生命周期中,每一个依赖组件或调用Resolve()方法创建一个单一的共享的实例,并且子生命周期域共享父生命周期域中的实例。若在继承层级中没有发现合适的拥有子实例的生命周期域,则抛出异常:DependencyResolutionException。
5、SingleInstance
每一次依赖组件或调用Resolve()方法都会得到一个相同的共享的实例。其实就是单例模式。
6、InstancePerHttpRequest
在一次Http请求上下文中,共享一个组件实例。仅适用于asp.net mvc开发。
什么是AutoMapper?
简单来说就是将一个对象映射到另一个对象的代码。 摆脱了繁琐的赋值过程 (最常见也就是Model -——ViewModel)
AutoMapper安装
我使用的是VS2015 可以在NuGet中直接输入AutoMapper 去下载
也可以使用控制台命令
PM> Install-Package AutoMapper
这里我定义了两个类 ShopingInfo ShopingInfoViewModel
public class ShopingInfo:EntityBase { public string ShopingName { get; set; } public int ShopingCount { get; set; } public decimal ShopingPric { get; set; } public int Stock { get; set; } public int Volumeofvolume { get; set; } public int ShopingTypeId { get; set; } public virtual ShopingType ShopingType { get; set; } }
public class ShopingInfoViewModel { public int ID { get; set; } public string ShopingName { get; set; } public int ShopingCount { get; set; } public decimal ShopingPric { get; set; } public int Stock { get; set; } public int Volumeofvolume { get; set; } public string ShopingTypeName { get; set; } }
需要用到的命名空间
using AutoMapper;
然后 专门建了一个类用来存放这些映射关系SourceProfile 并且继承了 Profile
public class SourceProfile : Profile { public SourceProfile() { base.CreateMap<ShopingInfo, ShopingInfoViewModel>(); } }
如果 我们发现两类中有字段名不一致。
例如 我吧shopingInfoViewModel 中的 ShopingName 改为 Name 那你可以这样写
public class SourceProfile : Profile { public SourceProfile() { base.CreateMap<ShopingInfo, ShopingInfoViewModel>(); // base.CreateMap<ShopingInfo, ShopingInfoViewModel>().ForMember(x => x.Name, // q => { q.MapFrom(z => z.ShopingName); // }); } }
建了个中间类 用来封装上面的代码
public class AutoMapper { public static void Start() { Mapper.Initialize(x => { x.AddProfile<SourceProfile>(); }); } }
然后就在全局类Global中 使得 启动初始化就去加载 加入下面这句代码
AutoMapper.Start(); 好了。 基本的都搞好了。 现在测试一下
可以 看到 已经映射上去了。 如果对多个Profile派生类的AutoMapper有疑问,可以这么写:
1
2
3
4
5
|
//配置映射 AutoMapper.Mapper.Initialize(map => map.AddProfiles( new [] {
typeof (Dto_StudentProfile),
typeof (StudentProfile)
}));
|
其中Dto_StudentProfile,StudentProfile是两个Profile的派生类,写了不同字段名称,不同类型的互相映射,如果还有疑问,可以移步我的博客C#使用AutoMapper6.2.2.0进行对象映射