1.私下认可EF生成的总是字符串比较的长和奇怪,若想使用普通的接连字符串来连接EF,则足以经过成立分门类,一碗水端平写三个构造函数,在构造函数中通过动态拼接EntityConnectionString获得EF所需的连天字符串,具代完毕代码如下:
一.暗许EF生成的连天字符串对比的长和奇特,若想选取普通的连日字符串来连接EF,则能够通过创办分项目,比量齐观写四个构造函数,在构造函数中经过动态拼接EntityConnectionString获得EF所需的接连字符串,具代完成代码如下:
多年来开发项目中用到的编码小技巧汇总表达,开发品种小技巧
1.默许EF生成的一而再字符串相比较的长和奇怪,若想使用普通的连天字符串来连接EF,则足以经过创立分连串,并重写二个构造函数,在构造函数中通过动态拼接EntityConnectionString获得EF所需的总是字符串,具代完结代码如下:
public partial class DataEntities
{
private static ConcurrentDictionary<string, string> entityConnStrings = new ConcurrentDictionary<string, string>();
public DataEntities(string connName)
: base(BuildEntityConnectionString(connName))
{
}
private static string BuildEntityConnectionString(string connName)
{
if (!entityConnStrings.ContainsKey(connName))
{
var connStrSetting = System.Configuration.ConfigurationManager.ConnectionStrings[connName];
EntityConnectionStringBuilder entityConnStrBuilder = new EntityConnectionStringBuilder();
entityConnStrBuilder.Provider = connStrSetting.ProviderName;
entityConnStrBuilder.ProviderConnectionString = EncryptUtility.DesDecrypt("XXXXX", connStrSetting.ConnectionString);
entityConnStrBuilder.Metadata = "res://*/Data.csdl|res://*/Data.ssdl|res://*/Data.msl";
string entityConnString = entityConnStrBuilder.ToString();
entityConnStrings.AddOrUpdate(connName, entityConnString, (key, value) => entityConnString);
}
return entityConnStrings[connName];
}
}
在意上边的类是贰个分部类:partial,同时BuildEntityConnectionString方法是二个静态方法,在BuildEntityConnectionString方法中ProviderConnectionString
= EncryptUtility.DesDecrypt(“XXXXX”,
connStrSetting.ConnectionString);是重点,我那边是对config中的连接字符串
也都进行了加密,故此处笔者必要解密,若无这几个需要能够一贯:ProviderConnectionString
=connStrSetting.ConnectionString即可。后续实例化EF上下文对象时,请使用:DataEntities(string
connName)那个结构涵数即可,DataEntities是切实可行的EF上下文对象,大家的EF上下文类名均或者不一致。
二.支撑二个通用对象的XML系列化(即:一个类中有可变类型属性成员,供给不一致的队列结果及变更不一样的队列成分名称),具体得以实现代码如下:
3个必要被种类化成XML的类:个中须求扭转的XML元素detail必需有子成分,且子成分名称及子成分内部属性依据项目标不等而分裂(即:detail成分下的子成分是可变的)
[XmlRootAttribute("master")]
public class DemoMaster<T> where T : class
{
[XmlElement("attr")]
public string DemoAttr { get; set; }
[XmlElement("detail")]
public DemoDetail<T> DemoDetail { get; set; } //关键点在这里,该属性元素为:detail,但其子元素根据T不同而不同
}
public class DemoDetail<T> : IXmlSerializable where T : class
{
public T body { get; set; }
public System.Xml.Schema.XmlSchema GetSchema()
{
return null;
}
public void ReadXml(System.Xml.XmlReader reader)
{
string bodyStr = reader.ReadInnerXml();
this.body = XmlHelper.XmlDeserialize<T>(bodyStr, Encoding.UTF8);
}
public void WriteXml(System.Xml.XmlWriter writer)
{
writer.WriteRaw(XmlHelper.XmlSerialize(this.body, Encoding.UTF8, true));
}
}
[XmlTypeAttribute("list-a", AnonymousType = false)]
public class DemoDetailA
{
public string Apro1 { get; set; }
public string Apro2 { get; set; }
public string Apro3 { get; set; }
}
[XmlTypeAttribute("list-b", AnonymousType = false)]
public class DemoDetailB
{
public string Bpro1 { get; set; }
public string Bpro2 { get; set; }
public string Bpro3 { get; set; }
}
[XmlTypeAttribute("list-c", AnonymousType = false)]
public class DemoDetailC
{
public string Cpro1 { get; set; }
public string Cpro2 { get; set; }
public string Cpro3 { get; set; }
}
小心上边代码中,需求关心:德姆oDetail属性及德姆oDetail<T>类,德姆oDetail属性仅是为着生成detail元高商点,而子节点则由德姆oDetail<T>类来拓展转移,德姆oDetail<T>是达成了IXmlSerializable接口,在XML种类化时,德姆oDetail<T>类仅将body属性对应的T类型实例内容开始展览系列化(WriteRaw),而反类别化时,则先反类别化body属性对应的T类型实例,然后赋值给body属性,那也是精粹纷呈之处,德姆oDetail<T>类本人并不曾真正加入到种类化中,故体系化的字符串也看不到DemoDetail<T>类相关的成分,德姆oDetail<T>类仅仅是多个XML系列化格式生成的中介。体系化的XML结果如下:
体系化代码:
var demo1 = new DemoMaster<DemoDetailA>()
{
DemoAttr = "demo1",
DemoDetail = new DemoDetail<DemoDetailA>() { body = new DemoDetailA() { Apro1 = "demoA1", Apro2 = "demoA2", Apro3 = "demoA3" } }
};
var demo2 = new DemoMaster<DemoDetailB>()
{
DemoAttr = "demo2",
DemoDetail = new DemoDetail<DemoDetailB>() { body = new DemoDetailB() { Bpro1 = "demoB1", Bpro2 = "demoB2", Bpro3 = "demoB3" } }
};
var demo3 = new DemoMaster<DemoDetailC>()
{
DemoAttr = "demo3",
DemoDetail = new DemoDetail<DemoDetailC>() { body = new DemoDetailC() { Cpro1 = "demoC1", Cpro2 = "demoC2", Cpro3 = "demoC3" } }
};
textBox1.Text = XmlHelper.XmlSerialize(demo1, Encoding.UTF8);
textBox1.Text += "\r\n" + XmlHelper.XmlSerialize(demo2, Encoding.UTF8);
textBox1.Text += "\r\n" + XmlHelper.XmlSerialize(demo3, Encoding.UTF8);
种类化的XML:
<?xml version="1.0" encoding="utf-8"?>
<master>
<attr>demo1</attr>
<detail><list-a>
<Apro1>demoA1</Apro1>
<Apro2>demoA2</Apro2>
<Apro3>demoA3</Apro3>
</list-a></detail>
</master>
<?xml version="1.0" encoding="utf-8"?>
<master>
<attr>demo2</attr>
<detail><list-b>
<Bpro1>demoB1</Bpro1>
<Bpro2>demoB2</Bpro2>
<Bpro3>demoB3</Bpro3>
</list-b></detail>
</master>
<?xml version="1.0" encoding="utf-8"?>
<master>
<attr>demo3</attr>
<detail><list-c>
<Cpro1>demoC1</Cpro1>
<Cpro2>demoC2</Cpro2>
<Cpro3>demoC3</Cpro3>
</list-c></detail>
</master>
三.winform DataGridView
实现钦命列采用密码框情势展现与编写制定,以及列绑定到复合属性(即:绑定到多层次属性),具体落到实处代码如下:
dataGridView1.CellFormatting += new DataGridViewCellFormattingEventHandler(dataGridView1_CellFormatting);
dataGridView1.EditingControlShowing += new DataGridViewEditingControlShowingEventHandler(dataGridView1_EditingControlShowing);
public string EvaluateValue(object obj, string property)
{
string retValue = string.Empty;
string[] names = property.Split('.');
for (int i = 0; i < names.Count(); i++)
{
try
{
var prop = obj.GetType().GetProperty(names[i]);
var result = prop.GetValue(obj, null);
if (result != null)
{
obj = result;
retValue = result.ToString();
}
else
{
break;
}
}
catch (Exception)
{
throw;
}
}
return retValue;
}
private void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
if (dataGridView1.Columns[e.ColumnIndex].DataPropertyName.Contains("."))
{
e.Value = EvaluateValue(dataGridView1.Rows[e.RowIndex].DataBoundItem, dataGridView1.Columns[e.ColumnIndex].DataPropertyName);
}
if (dataGridView1.Columns[e.ColumnIndex].Name == "KeyCode")
{
if (e.Value != null && e.Value.ToString().Length > 0)
{
e.Value = new string('*', e.Value.ToString().Length);
}
}
}
private void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
int i = this.dataGridView1.CurrentCell.ColumnIndex;
bool usePassword = false;
if (dataGridView1.Columns[i].Name == "KeyCode")
{
usePassword = true;
}
TextBox txt = e.Control as TextBox;
if (txt != null)
{
txt.UseSystemPasswordChar = usePassword;
}
}
//示例:绑定的源数据类定义
public class DemoBindClass
{
public string Attr { get; set; }
public string KeyCode { get; set; }
public DemoDetailA Detail { get; set; }
}
public class DemoDetailA
{
public string Apro1 { get; set; }
public string Apro2 { get; set; }
public string Apro3 { get; set; }
public DemoDetailB DetailChild { get; set; }
}
public class DemoDetailB
{
public string Bpro1 { get; set; }
public string Bpro2 { get; set; }
public string Bpro3 { get; set; }
}
绑定到数据源:
var demo = new[] {
new DemoBindClass()
{
Attr = "demo",
KeyCode="a123456789b",
Detail = new DemoDetailA()
{
Apro1 = "demoA1",
Apro2 = "demoA2",
Apro3 = "demoA3",
DetailChild = new DemoDetailB()
{
Bpro1 = "demoB1",
Bpro2 = "demoB2",
Bpro3 = "demoB3"
}
}
}
};
dataGridView1.AutoGenerateColumns = false;
dataGridView1.DataSource = demo;
兑现内定列选用密码框格局显示与编辑,以及列绑定到复合属性均必要订阅DataGridView的CellFormatting及艾德itingControlShowing事件,并在个中写转换当前Cell的Value,完毕列绑定到复合属性,关键点在:伊娃luateValue方法,该方法逻辑极粗略,就是基于绑定的习性层级(.分隔)层层遍历获取属性的值,直到遍历完或为空时结束,最后获得的结果正是绑定的属性的值。最终实现的效劳如下图示:
一.暗中同意EF生成的连日字符串相比较的长和新奇,若想行使普通的连年字符串来连…
前言
Contoso 高校以身作则 Web 应用程序演示怎么样选用实体框架(EF)Core 2.0 和
Visual Studio 2017 制造 ASP.NET Core 2.0 MVC Web 应用程序。
示范应用程序是虚构的Contoso大学的网址。
它蕴含学生入学,课程创造和名师职责等效果。
那是一名目繁多教程中的第二章,介绍怎么着初阶营造Contoso大学示例应用程序。
下载或查看已形成的应用程序 –
官方。
本类别文章翻译索引目录
EF Core 二.0 是EF的新颖版本,但还尚未包蕴拥有的 EF 六.x 效率。 有关 EF 陆.x
和 EF Core 之间怎么着抉择的音信,请参阅 EF Core vs.
EF6.x。
若是你选用 EF 6.x
,请参阅本课程种类的原先版本。
注意事项
- 对此本课程的 ASP.NET Core 一.1 版本,请参阅PDF格式的本学科的 VS
20一柒 Update
二版本。- 对于本学科的 Visual Studio 20一伍 版本,请参阅PDF格式的 VS 2015
版本的 ASP.NET Core
文档。
public partial class DataEntities
{
private static ConcurrentDictionary<string, string> entityConnStrings = new ConcurrentDictionary<string, string>();
public DataEntities(string connName)
: base(BuildEntityConnectionString(connName))
{
}
private static string BuildEntityConnectionString(string connName)
{
if (!entityConnStrings.ContainsKey(connName))
{
var connStrSetting = System.Configuration.ConfigurationManager.ConnectionStrings[connName];
EntityConnectionStringBuilder entityConnStrBuilder = new EntityConnectionStringBuilder();
entityConnStrBuilder.Provider = connStrSetting.ProviderName;
entityConnStrBuilder.ProviderConnectionString = EncryptUtility.DesDecrypt("XXXXX", connStrSetting.ConnectionString);
entityConnStrBuilder.Metadata = "res://*/Data.csdl|res://*/Data.ssdl|res://*/Data.msl";
string entityConnString = entityConnStrBuilder.ToString();
entityConnStrings.AddOrUpdate(connName, entityConnString, (key, value) => entityConnString);
}
return entityConnStrings[connName];
}
}
public partial class DataEntities
{
private static ConcurrentDictionary<string, string> entityConnStrings = new ConcurrentDictionary<string, string>();
public DataEntities(string connName)
: base(BuildEntityConnectionString(connName))
{
}
private static string BuildEntityConnectionString(string connName)
{
if (!entityConnStrings.ContainsKey(connName))
{
var connStrSetting = System.Configuration.ConfigurationManager.ConnectionStrings[connName];
EntityConnectionStringBuilder entityConnStrBuilder = new EntityConnectionStringBuilder();
entityConnStrBuilder.Provider = connStrSetting.ProviderName;
entityConnStrBuilder.ProviderConnectionString = EncryptUtility.DesDecrypt("XXXXX", connStrSetting.ConnectionString);
entityConnStrBuilder.Metadata = "res://*/Data.csdl|res://*/Data.ssdl|res://*/Data.msl";
string entityConnString = entityConnStrBuilder.ToString();
entityConnStrings.AddOrUpdate(connName, entityConnString, (key, value) => entityConnString);
}
return entityConnStrings[connName];
}
}
支出条件
安装如下工具:
- .NET Core 2.0.0
SDK
或更新版本。 - Visual Studio
2017
V一5.三 或更高版本,安装 ASP.NET 和 Web 开发工具。
留意上边的类是1个分部类:partial,同时BuildEntityConnectionString方法是3个静态方法,在BuildEntityConnectionString方法中ProviderConnectionString
= EncryptUtility.DesDecrypt(“XXXXX”,
connStrSetting.ConnectionString);是重大,我这里是对config中的连接字符串
也都进展了加密,故此处作者急需解密,若无这么些须要能够直接:ProviderConnectionString
=connStrSetting.ConnectionString即可。后续实例化EF上下文对象时,请使用:DataEntities(string
connName)那个组织涵数即可,DataEntities是实际的EF上下文对象,大家的EF上下文类名均或者不1样。
在意上面的类是三个分部类:partial,同时BuildEntityConnectionString方法是一个静态方法,在BuildEntityConnectionString方法中ProviderConnectionString
= EncryptUtility.DesDecrypt(“XXXXX”,
connStrSetting.ConnectionString);是重点,作者那边是对config中的连接字符串
也都进行了加密,故此处作者要求解密,若无这么些须要能够平昔:ProviderConnectionString
=connStrSetting.ConnectionString即可。后续实例化EF上下文对象时,请使用:DataEntities(string
connName)那么些结构涵数即可,DataEntities是具体的EF上下文对象,大家的EF上下文类名均也许不一样。
故障排除
借使赶上难点,您不能够消除,平日能够通过将代码与成就的品类进展相比来找到化解方案。
有关常见错误和解决措施的列表,请参阅本连串最终叁个课程的故障排除有的。
若是你未有找到所需的内容,您也得以在 StackOverflow.com 上发问。
小贴士
本类别包蕴11个学科,各类教程都创设在最初等教育程中的基础之上。
在中标完结每种教程之后,请思虑保存项目标副本。
然后,借使遇上标题,您可以从上多个课程重新初阶,无需从头初阶。
2.帮忙多个通用对象的XML体系化(即:一个类中有可变类型属性成员,需求不一样的体系结果及变化差别的行列成分名称),具体达成代码如下:
2.帮衬1个通用对象的XML类别化(即:2个类中有可变类型属性成员,要求分歧的类别结果及变化差异的行列成分名称),具体完结代码如下:
Contoso 大学网址行使
课程中营造的是一个简短的大学网址。
用户可以查看和更新学生,课程和教育工小编音讯。 上边是您将要创立的一对页面。
index
edit
网址的 UI 风格与内置模板生成的 UI 风格保持1致,本课程首要关心怎么样运用
Entity Framework。
贰个亟待被种类化成XML的类:在这之中要求扭转的XML成分detail必需有子元素,且子成分名称及子成分内部属性依据项目标不等而分歧(即:detail成分下的子成分是可变的)
二个内需被种类化成XML的类:在那之中要求转变的XML成分detail必需有子成分,且子成分名称及子成分内部属性依照项目标不一样而各异(即:detail成分下的子成分是可变的)
近些年开发品种中用到的编码小技巧汇总表达,开发项目小技巧。始建 ASP.NET Core MVC 网址应用
开拓 Visual Studio 并创设名叫 “ContosoUniversity” 的新 ASP.NET Core C#
web 项目。
- 从文件菜单中,选拔新建>项目。
- 从左窗格中接纳 已设置 -> Visual C# -> Web 。
- 个中窗格采用 ASP.NET Core Web 应用程序。
- 输入 ContosoUniversity 作为项目名称,然后单击显明。

newProject
- 等待 “新建 ASP.NET Core Web 应用程序” 对话框出现
- 挑选 ASP.NET Core 贰.0 和 Web应用程序(模型视图控制器)模板。
- 留意:本课程要求 ASP.NET Core 二.0 和 EF Core 二.0 或更高版本 –
确认保障未接纳ASP.NET Core 一.1。 - 管教认证设置为“不进行身份验证”。
- 单击 “确定” 按钮。
newCore
[XmlRootAttribute("master")]
public class DemoMaster<T> where T : class
{
[XmlElement("attr")]
public string DemoAttr { get; set; }
[XmlElement("detail")]
public DemoDetail<T> DemoDetail { get; set; } //关键点在这里,该属性元素为:detail,但其子元素根据T不同而不同
}
public class DemoDetail<T> : IXmlSerializable where T : class
{
public T body { get; set; }
public System.Xml.Schema.XmlSchema GetSchema()
{
return null;
}
public void ReadXml(System.Xml.XmlReader reader)
{
string bodyStr = reader.ReadInnerXml();
this.body = XmlHelper.XmlDeserialize<T>(bodyStr, Encoding.UTF8);
}
public void WriteXml(System.Xml.XmlWriter writer)
{
writer.WriteRaw(XmlHelper.XmlSerialize(this.body, Encoding.UTF8, true));
}
}
[XmlTypeAttribute("list-a", AnonymousType = false)]
public class DemoDetailA
{
public string Apro1 { get; set; }
public string Apro2 { get; set; }
public string Apro3 { get; set; }
}
[XmlTypeAttribute("list-b", AnonymousType = false)]
public class DemoDetailB
{
public string Bpro1 { get; set; }
public string Bpro2 { get; set; }
public string Bpro3 { get; set; }
}
[XmlTypeAttribute("list-c", AnonymousType = false)]
public class DemoDetailC
{
public string Cpro1 { get; set; }
public string Cpro2 { get; set; }
public string Cpro3 { get; set; }
}
[XmlRootAttribute("master")]
public class DemoMaster<T> where T : class
{
[XmlElement("attr")]
public string DemoAttr { get; set; }
[XmlElement("detail")]
public DemoDetail<T> DemoDetail { get; set; } //关键点在这里,该属性元素为:detail,但其子元素根据T不同而不同
}
public class DemoDetail<T> : IXmlSerializable where T : class
{
public T body { get; set; }
public System.Xml.Schema.XmlSchema GetSchema()
{
return null;
}
public void ReadXml(System.Xml.XmlReader reader)
{
string bodyStr = reader.ReadInnerXml();
this.body = XmlHelper.XmlDeserialize<T>(bodyStr, Encoding.UTF8);
}
public void WriteXml(System.Xml.XmlWriter writer)
{
writer.WriteRaw(XmlHelper.XmlSerialize(this.body, Encoding.UTF8, true));
}
}
[XmlTypeAttribute("list-a", AnonymousType = false)]
public class DemoDetailA
{
public string Apro1 { get; set; }
public string Apro2 { get; set; }
public string Apro3 { get; set; }
}
[XmlTypeAttribute("list-b", AnonymousType = false)]
public class DemoDetailB
{
public string Bpro1 { get; set; }
public string Bpro2 { get; set; }
public string Bpro3 { get; set; }
}
[XmlTypeAttribute("list-c", AnonymousType = false)]
public class DemoDetailC
{
public string Cpro1 { get; set; }
public string Cpro2 { get; set; }
public string Cpro3 { get; set; }
}
安装网址样式
不难修改多少个岗位,设置站点菜单,布局和主页。
打开 Views/Shared/_Layout.cshtml 文件,进行以下改变:
- 将三处 “ContosoUniversity” 文字修改为“Contoso University”。
- 添加 学生、课程、教授和部门菜单,删除联系人菜单。
根本的修改如下
<html>
......
<title>@ViewData["Title"] - Contoso University</title>
......
class="navbar-brand">Contoso University</a>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li><a asp-area="" asp-controller="Home" asp-action="Index">Home</a></li>
<li><a asp-area="" asp-controller="Home" asp-action="About">About</a></li>
<li><a asp-area="" asp-controller="Students" asp-action="Index">Students</a></li>
<li><a asp-area="" asp-controller="Courses" asp-action="Index">Courses</a></li>
<li><a asp-area="" asp-controller="Instructors" asp-action="Index">Instructors</a></li>
<li><a asp-area="" asp-controller="Departments" asp-action="Index">Departments</a></li>
</ul>
</div>
</div>
</nav>
<div class="container body-content">
@RenderBody()
<hr />
<footer>
<p>© 2017 - Contoso University</p>
</footer>
</div>
</body>
</html>
在 Views/Home/Index.cshtml 文件,使用以下代码替换文件的内容:
@{
ViewData["Title"] = "Home Page";
}
<div class="jumbotron">
<h1>Contoso University</h1>
</div>
<div class="row">
<div class="col-md-4">
<h2>Welcome to Contoso University</h2>
<p>
Contoso University is a sample application that
demonstrates how to use Entity Framework Core in an
ASP.NET Core MVC web application.
</p>
</div>
<div class="col-md-4">
<h2>Build it from scratch</h2>
<p>You can build the application by following the steps in a series of tutorials.</p>
<p><a class="btn btn-default" href="https://docs.asp.net/en/latest/data/ef-mvc/intro.html">See the tutorial »</a></p>
</div>
<div class="col-md-4">
<h2>Download it</h2>
<p>You can download the completed project from GitHub.</p>
<p><a class="btn btn-default" href="https://github.com/aspnet/Docs/tree/master/aspnetcore/data/ef-mvc/intro/samples/cu-final">See project source code »</a></p>
</div>
</div>
按 CT奇骏L+F5 运维项目或从菜单中选择 调节和测试-> 起先推行(不调节和测试),
您将在浏览器中来看本学科中落到实处的首页。
image.png
专注上面代码中,要求关爱:德姆oDetail属性及德姆oDetail<T>类,DemoDetail属性仅是为着生成detail成分节点,而子节点则由德姆oDetail<T>类来进展变更,德姆oDetail<T>是贯彻了IXmlSerializable接口,在XML类别化时,德姆oDetail<T>类仅将body属性对应的T类型实例内容进行种类化(WriteRaw),而反连串化时,则先反系列化body属性对应的T类型实例,然后赋值给body属性,这也是巧妙之处,德姆oDetail<T>类本人并未当真加入到种类化中,故种类化的字符串也看不到德姆oDetail<T>类相关的成分,德姆oDetail<T>类仅仅是叁个XML种类化格式生成的中介。种类化的XML结果如下:
小心上边代码中,需求关爱:德姆oDetail属性及德姆oDetail<T>类,德姆oDetail属性仅是为了生成detail成分节点,而子节点则由德姆oDetail<T>类来开始展览变更,德姆oDetail<T>是贯彻了IXml塞里alizable接口,在XML类别化时,德姆oDetail<T>类仅将body属性对应的T类型实例内容实行类别化(WriteRaw),而反系列化时,则先反连串化body属性对应的T类型实例,然后赋值给body属性,那也是卓绝纷呈之处,DemoDetail<T>类本人并未当真参预到体系化中,故类别化的字符串也看不到德姆oDetail<T>类相关的要素,DemoDetail<T>类仅仅是1个XML种类化格式生成的中介。种类化的XML结果如下:
Entity Framework Core NuGet packages
翻译注: 此标题不翻译好过翻译
要在档次中添加 EF Core 帮忙,必要设置相应的数据库完成。本学科使用 SQL
Server 数据库,所急需的主次包 Microsoft.EntityFrameworkCore.SqlServer
已经嵌入于 Microsoft.AspNetCore.All 包中,因而大家以后如何都不要做。
以此顺序包 (Microsoft.EntityFrameworkCore) 及其信赖项
(Microsoft.EntityFrameworkCore.Relational) 提供了EF运营时协助。在稍后的
”数据库迁移“教程中,你将会学习添加叁个工具包。
至于可用以 Entity Framework Core 的任何数据库帮衬程序的新闻,请参阅
Data
Providers。
类别化代码:
连串化代码:
开创数据模型
接下去,您将为Contoso大学应用程序创造实体课程。 您将从以下八个实体开端。
class diagram
在 Student 与 Enrollement 实体间是一个壹对多的涉嫌, 在 Course 与
Enrollment 间也存在一些多关乎。
换句话说,学生能够参预任意数量的科目,课程能够有自由数量的学童登记。
在偏下一些中,您将为各样实体创制1个类。
var demo1 = new DemoMaster<DemoDetailA>()
{
DemoAttr = "demo1",
DemoDetail = new DemoDetail<DemoDetailA>() { body = new DemoDetailA() { Apro1 = "demoA1", Apro2 = "demoA2", Apro3 = "demoA3" } }
};
var demo2 = new DemoMaster<DemoDetailB>()
{
DemoAttr = "demo2",
DemoDetail = new DemoDetail<DemoDetailB>() { body = new DemoDetailB() { Bpro1 = "demoB1", Bpro2 = "demoB2", Bpro3 = "demoB3" } }
};
var demo3 = new DemoMaster<DemoDetailC>()
{
DemoAttr = "demo3",
DemoDetail = new DemoDetail<DemoDetailC>() { body = new DemoDetailC() { Cpro1 = "demoC1", Cpro2 = "demoC2", Cpro3 = "demoC3" } }
};
textBox1.Text = XmlHelper.XmlSerialize(demo1, Encoding.UTF8);
textBox1.Text += "\r\n" + XmlHelper.XmlSerialize(demo2, Encoding.UTF8);
textBox1.Text += "\r\n" + XmlHelper.XmlSerialize(demo3, Encoding.UTF8);
var demo1 = new DemoMaster<DemoDetailA>()
{
DemoAttr = "demo1",
DemoDetail = new DemoDetail<DemoDetailA>() { body = new DemoDetailA() { Apro1 = "demoA1", Apro2 = "demoA2", Apro3 = "demoA3" } }
};
var demo2 = new DemoMaster<DemoDetailB>()
{
DemoAttr = "demo2",
DemoDetail = new DemoDetail<DemoDetailB>() { body = new DemoDetailB() { Bpro1 = "demoB1", Bpro2 = "demoB2", Bpro3 = "demoB3" } }
};
var demo3 = new DemoMaster<DemoDetailC>()
{
DemoAttr = "demo3",
DemoDetail = new DemoDetail<DemoDetailC>() { body = new DemoDetailC() { Cpro1 = "demoC1", Cpro2 = "demoC2", Cpro3 = "demoC3" } }
};
textBox1.Text = XmlHelper.XmlSerialize(demo1, Encoding.UTF8);
textBox1.Text += "\r\n" + XmlHelper.XmlSerialize(demo2, Encoding.UTF8);
textBox1.Text += "\r\n" + XmlHelper.XmlSerialize(demo3, Encoding.UTF8);
Student 实体
在 Models 文件夹中,创造二个名字为 Student.cs
的类公事,并动用以下代码替换模板代码。
using System;
using System.Collections.Generic;
namespace ContosoUniversity.Models
{
public class Student
{
public int ID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
public ICollection<Enrollment> Enrollments { get; set; }
}
}
ID
属性将变成对应数据表的主键。默许景况下,Entity Framework 将名称为 ID
或 {类名}ID 的本性解释为主键。
Enrollments
属性是导航属性。导航属性用于关联别的实体。对于八个上学的小孩子实体(数据)来说,在那之中的
Enrollments 属性包涵全体与该学生相关联的 Enrollment
实体(数据)。也便是说,要是数据库中的一个上学的小孩子行数据涉嫌五个注册行数据(一对多,在
Enrollment 表中外键关联StudentID 值为该学员的主键值),则 Student
实体中的 Enrollments 导航属性将富含那多个 Enrollment 实体。
借使导航属性能够包容八个实体(在多对多或1对多涉及中),则其品种必须是足以加上,删除和换代条指标列表,例如ICollection
<T>。您能够钦赐ICollection <T>或项目,如List
<T>或HashSet <T>。如若内定ICollection
<T>,EF私下认可创设三个HashSet <T>集合。
系列化的XML:
种类化的XML:
Enrollment 实体
Enrollment
在 Models 文件夹中,创设一个名称叫 Student.cs
的类公事,并接纳以下代码替换模板代码。
namespace ContosoUniversity.Models
{
public enum Grade
{
A, B, C, D, F
}
public class Enrollment
{
public int EnrollmentID { get; set; }
public int CourseID { get; set; }
public int StudentID { get; set; }
public Grade? Grade { get; set; }
public Course Course { get; set; }
public Student Student { get; set; }
}
}
EnrollmentID
属性将变成主键。本实体使用 {类名}ID 情势代表在 Studnet
实体中采取的 ID 情势。 平日你会只采用1种情势,并在整个数据模型中运用。
在那里,不一致的形式是为了演示,表明你能够动用任壹格局。
在前面包车型地铁教程中,您将看到什么样运用未有类名的 ID
能够更易于地在数据模型中落到实处持续。
Grade (等级)
属性是3个枚举类型。 Grade
类型注脚后的 ?
表示
可为空类型。 三个空的阶段和3个值为0的阶段是例外的 —
空表示等级未知恐怕未有被赋值。
StudentID
属性是外键,相应的导航属性是 Student
。 一个 Enrollment
实体与一个 Student
实体相关联,由此该属性只可以拥有保留单个 Studnet
实体(与你以前看到的能够分包三个注册实体的 Student.Enrollments
导航属性分裂)。
CourseID
属性是外键, 对应的导航属性是 Course
。 一个 Enrollment
实体与3个 Course
实体相关联。
当3性格能名称相符格局 <导航属性名><主键名> , EF
将质量解析为外键属性(例如,StudentID
对应 Student
导航属性,因为
Student
实体的主键是 ID
)。 外键属性也足以回顾地选用<主键属性名称>(例如,CourseID
,因为课程实体的主键是
CourseID
)。
<?xml version="1.0" encoding="utf-8"?>
<master>
<attr>demo1</attr>
<detail><list-a>
<Apro1>demoA1</Apro1>
<Apro2>demoA2</Apro2>
<Apro3>demoA3</Apro3>
</list-a></detail>
</master>
<?xml version="1.0" encoding="utf-8"?>
<master>
<attr>demo2</attr>
<detail><list-b>
<Bpro1>demoB1</Bpro1>
<Bpro2>demoB2</Bpro2>
<Bpro3>demoB3</Bpro3>
</list-b></detail>
</master>
<?xml version="1.0" encoding="utf-8"?>
<master>
<attr>demo3</attr>
<detail><list-c>
<Cpro1>demoC1</Cpro1>
<Cpro2>demoC2</Cpro2>
<Cpro3>demoC3</Cpro3>
</list-c></detail>
</master>
<?xml version="1.0" encoding="utf-8"?>
<master>
<attr>demo1</attr>
<detail><list-a>
<Apro1>demoA1</Apro1>
<Apro2>demoA2</Apro2>
<Apro3>demoA3</Apro3>
</list-a></detail>
</master>
<?xml version="1.0" encoding="utf-8"?>
<master>
<attr>demo2</attr>
<detail><list-b>
<Bpro1>demoB1</Bpro1>
<Bpro2>demoB2</Bpro2>
<Bpro3>demoB3</Bpro3>
</list-b></detail>
</master>
<?xml version="1.0" encoding="utf-8"?>
<master>
<attr>demo3</attr>
<detail><list-c>
<Cpro1>demoC1</Cpro1>
<Cpro2>demoC2</Cpro2>
<Cpro3>demoC3</Cpro3>
</list-c></detail>
</master>
Course 实体
Course
在 Models 文件夹中,创立2个名叫 Course.cs
的类公事,并行使以下代码替换模板代码。
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
namespace ContosoUniversity.Models
{
public class Course
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
public ICollection<Enrollment> Enrollments { get; set; }
}
}
Enrollments
属性是导航属性。多个 Course
实体能够提到到任意几个
Enrollment
实体。
大家将在本连串的接二连三教程中详尽介绍 DatabaseGenerated
本性。
此本性允许你内定 Course
的主键名,而不是让数据库生成它。
3.winform DataGridView
达成钦定列选择密码框情势展现与编写制定,以及列绑定到复合属性(即:绑定到多层次属性),具体贯彻代码如下:
三.winform DataGridView
完毕钦定列选取密码框方式突显与编辑,以及列绑定到复合属性(即:绑定到多层次属性),具体完成代码如下:
创设数据库上下文 Database Context
将数据模型与 Entity Framework 功用协同工作的重要性类是数据库上下文类。
通过从 Microsoft.EntityFrameworkCore.DbContext 类派生来创制此类。
在代码中,能够钦点数据模型中涵盖哪些实体。 还足以自定义某个 Entity
Framework 行为。 在那一个类型中,该类被命名叫 SchoolContext
。
在类型文件夹中,创立一个名称叫Data的文书夹。
在 Data 文件夹中开创三个名称叫 SchoolContext.cs
的新类,并用于下代码替换模板代码:
using ContosoUniversity.Models;
using Microsoft.EntityFrameworkCore;
namespace ContosoUniversity.Data
{
public class SchoolContext : DbContext
{
public SchoolContext(DbContextOptions<SchoolContext> options) : base(options)
{
}
public DbSet<Course> Courses { get; set; }
public DbSet<Enrollment> Enrollments { get; set; }
public DbSet<Student> Students { get; set; }
}
}
dataGridView1.CellFormatting += new DataGridViewCellFormattingEventHandler(dataGridView1_CellFormatting);
dataGridView1.EditingControlShowing += new DataGridViewEditingControlShowingEventHandler(dataGridView1_EditingControlShowing);
public string EvaluateValue(object obj, string property)
{
string retValue = string.Empty;
string[] names = property.Split('.');
for (int i = 0; i < names.Count(); i++)
{
try
{
var prop = obj.GetType().GetProperty(names[i]);
var result = prop.GetValue(obj, null);
if (result != null)
{
obj = result;
retValue = result.ToString();
}
else
{
break;
}
}
catch (Exception)
{
throw;
}
}
return retValue;
}
private void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
if (dataGridView1.Columns[e.ColumnIndex].DataPropertyName.Contains("."))
{
e.Value = EvaluateValue(dataGridView1.Rows[e.RowIndex].DataBoundItem, dataGridView1.Columns[e.ColumnIndex].DataPropertyName);
}
if (dataGridView1.Columns[e.ColumnIndex].Name == "KeyCode")
{
if (e.Value != null && e.Value.ToString().Length > 0)
{
e.Value = new string('*', e.Value.ToString().Length);
}
}
}
private void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
int i = this.dataGridView1.CurrentCell.ColumnIndex;
bool usePassword = false;
if (dataGridView1.Columns[i].Name == "KeyCode")
{
usePassword = true;
}
TextBox txt = e.Control as TextBox;
if (txt != null)
{
txt.UseSystemPasswordChar = usePassword;
}
}
//示例:绑定的源数据类定义
public class DemoBindClass
{
public string Attr { get; set; }
public string KeyCode { get; set; }
public DemoDetailA Detail { get; set; }
}
public class DemoDetailA
{
public string Apro1 { get; set; }
public string Apro2 { get; set; }
public string Apro3 { get; set; }
public DemoDetailB DetailChild { get; set; }
}
public class DemoDetailB
{
public string Bpro1 { get; set; }
public string Bpro2 { get; set; }
public string Bpro3 { get; set; }
}
dataGridView1.CellFormatting += new DataGridViewCellFormattingEventHandler(dataGridView1_CellFormatting);
dataGridView1.EditingControlShowing += new DataGridViewEditingControlShowingEventHandler(dataGridView1_EditingControlShowing);
public string EvaluateValue(object obj, string property)
{
string retValue = string.Empty;
string[] names = property.Split('.');
for (int i = 0; i < names.Count(); i++)
{
try
{
var prop = obj.GetType().GetProperty(names[i]);
var result = prop.GetValue(obj, null);
if (result != null)
{
obj = result;
retValue = result.ToString();
}
else
{
break;
}
}
catch (Exception)
{
throw;
}
}
return retValue;
}
private void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
if (dataGridView1.Columns[e.ColumnIndex].DataPropertyName.Contains("."))
{
e.Value = EvaluateValue(dataGridView1.Rows[e.RowIndex].DataBoundItem, dataGridView1.Columns[e.ColumnIndex].DataPropertyName);
}
if (dataGridView1.Columns[e.ColumnIndex].Name == "KeyCode")
{
if (e.Value != null && e.Value.ToString().Length > 0)
{
e.Value = new string('*', e.Value.ToString().Length);
}
}
}
private void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
int i = this.dataGridView1.CurrentCell.ColumnIndex;
bool usePassword = false;
if (dataGridView1.Columns[i].Name == "KeyCode")
{
usePassword = true;
}
TextBox txt = e.Control as TextBox;
if (txt != null)
{
txt.UseSystemPasswordChar = usePassword;
}
}
//示例:绑定的源数据类定义
public class DemoBindClass
{
public string Attr { get; set; }
public string KeyCode { get; set; }
public DemoDetailA Detail { get; set; }
}
public class DemoDetailA
{
public string Apro1 { get; set; }
public string Apro2 { get; set; }
public string Apro3 { get; set; }
public DemoDetailB DetailChild { get; set; }
}
public class DemoDetailB
{
public string Bpro1 { get; set; }
public string Bpro2 { get; set; }
public string Bpro3 { get; set; }
}
采取数据上下文 – 使用注重注入
ASP.NET Core 私下认可使用重视注入技术。
服务(如EF数据库上下文)在应用程序运转时期通过正视注入注册实例。
那个急需运用服务的组件通过构造函数参数获得劳动的实例。
稍后我们得以见到控制器构造函数获取上下文实例的代码。
要将 SchoolContext
注册为服务,请打开 Startup.cs
,并依照如下代码修改
ConfigureServices
方法。
//Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<SchoolContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddMvc();
}
通过调用 DbContextOptionsBuilder
对象上的章程将接连字符串的称号传递给上下文。 对于本地开发,ASP.NET Core
配置种类从 appsettings.json 文件读取连接字符串。
开拓appsettings.json文件并累加三个连接字符串,如下例所示。
{
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=ContosoUniversity1;Trusted_Connection=True;MultipleActiveResultSets=true"
},
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Warning"
}
}
}
绑定到数据源:
绑定到数据源:
SQL Server Express LocalDB
老是字符串内定 SQL Server LocalDB 数据库。 LocalDB 是 SQL Server Express
数据库引擎的轻量级版本,旨在用于应用程序开发,而不是生产用途。 LocalDB
按需运营并以用户方式运营,因而未有复杂的计划。 私下认可情状下,LocalDB在
C:/Users/<user> 目录中开创 .mdf 数据库文件。
var demo = new[] {
new DemoBindClass()
{
Attr = "demo",
KeyCode="a123456789b",
Detail = new DemoDetailA()
{
Apro1 = "demoA1",
Apro2 = "demoA2",
Apro3 = "demoA3",
DetailChild = new DemoDetailB()
{
Bpro1 = "demoB1",
Bpro2 = "demoB2",
Bpro3 = "demoB3"
}
}
}
};
dataGridView1.AutoGenerateColumns = false;
dataGridView1.DataSource = demo;
var demo = new[] {
new DemoBindClass()
{
Attr = "demo",
KeyCode="a123456789b",
Detail = new DemoDetailA()
{
Apro1 = "demoA1",
Apro2 = "demoA2",
Apro3 = "demoA3",
DetailChild = new DemoDetailB()
{
Bpro1 = "demoB1",
Bpro2 = "demoB2",
Bpro3 = "demoB3"
}
}
}
};
dataGridView1.AutoGenerateColumns = false;
dataGridView1.DataSource = demo;
足够代码,使用测试数据初步化数据库
EF 将为你创制叁个空数据库。
在本节中,您将编写制定一个成立数据库后调用的方式,以便利用测试数据举办填写。
在那里,您将动用 EnsureCreated 方法自动创造数据库。
在前面包车型地铁教程中,您将看到什么样运用 Code First Migration
(代码优先迁移)
来更改数据库架构而不是剔除和另行创设数据库来拍卖架构更改。
在 Data
文件夹中,成立1个名称叫 DbInitializer.cs
的新类文件,并运用以下代码替换模板代码,这么些代码将在急需时成立数据库,并将测试数据加载到新数据库中。
//DbInitializer.cs
using ContosoUniversity.Models;
using System;
using System.Linq;
namespace ContosoUniversity.Data
{
public static class DbInitializer
{
public static void Initialize(SchoolContext context)
{
context.Database.EnsureCreated();
// Look for any students.
if (context.Students.Any())
{
return; // DB has been seeded
}
var students = new Student[]
{
new Student{FirstMidName="Carson",LastName="Alexander",EnrollmentDate=DateTime.Parse("2005-09-01")},
new Student{FirstMidName="Meredith",LastName="Alonso",EnrollmentDate=DateTime.Parse("2002-09-01")},
new Student{FirstMidName="Arturo",LastName="Anand",EnrollmentDate=DateTime.Parse("2003-09-01")},
new Student{FirstMidName="Gytis",LastName="Barzdukas",EnrollmentDate=DateTime.Parse("2002-09-01")},
new Student{FirstMidName="Yan",LastName="Li",EnrollmentDate=DateTime.Parse("2002-09-01")},
new Student{FirstMidName="Peggy",LastName="Justice",EnrollmentDate=DateTime.Parse("2001-09-01")},
new Student{FirstMidName="Laura",LastName="Norman",EnrollmentDate=DateTime.Parse("2003-09-01")},
new Student{FirstMidName="Nino",LastName="Olivetto",EnrollmentDate=DateTime.Parse("2005-09-01")}
};
foreach (Student s in students)
{
context.Students.Add(s);
}
context.SaveChanges();
var courses = new Course[]
{
new Course{CourseID=1050,Title="Chemistry",Credits=3},
new Course{CourseID=4022,Title="Microeconomics",Credits=3},
new Course{CourseID=4041,Title="Macroeconomics",Credits=3},
new Course{CourseID=1045,Title="Calculus",Credits=4},
new Course{CourseID=3141,Title="Trigonometry",Credits=4},
new Course{CourseID=2021,Title="Composition",Credits=3},
new Course{CourseID=2042,Title="Literature",Credits=4}
};
foreach (Course c in courses)
{
context.Courses.Add(c);
}
context.SaveChanges();
var enrollments = new Enrollment[]
{
new Enrollment{StudentID=1,CourseID=1050,Grade=Grade.A},
new Enrollment{StudentID=1,CourseID=4022,Grade=Grade.C},
new Enrollment{StudentID=1,CourseID=4041,Grade=Grade.B},
new Enrollment{StudentID=2,CourseID=1045,Grade=Grade.B},
new Enrollment{StudentID=2,CourseID=3141,Grade=Grade.F},
new Enrollment{StudentID=2,CourseID=2021,Grade=Grade.F},
new Enrollment{StudentID=3,CourseID=1050},
new Enrollment{StudentID=4,CourseID=1050},
new Enrollment{StudentID=4,CourseID=4022,Grade=Grade.F},
new Enrollment{StudentID=5,CourseID=4041,Grade=Grade.C},
new Enrollment{StudentID=6,CourseID=1045},
new Enrollment{StudentID=7,CourseID=3141,Grade=Grade.A},
};
foreach (Enrollment e in enrollments)
{
context.Enrollments.Add(e);
}
context.SaveChanges();
}
}
}
代码检查数据库中是或不是有学员,如若没有,则只要数据库是新的,并且须要选择测试数据举行种子。它将测试数据加载到数组而不是
List <T> 集合来优化品质。
在Program.cs中,修改Main方法在应用程序运营时执行以下操作:
- 从重视注入容器获取数据库上下文实例。
- 调用种子方法,传递给它的上下文。
- 种子方法成功时销毁上下文。
public static void Main(string[] args)
{
var host = BuildWebHost(args);
using (var scope = host.Services.CreateScope())
{
var services = scope.ServiceProvider;
try
{
var context = services.GetRequiredService<SchoolContext>();
DbInitializer.Initialize(context);
}
catch (Exception ex)
{
var logger = services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "An error occurred while seeding the database.");
}
}
host.Run();
}
亚洲必赢官网 ,在较旧的学科中,您恐怕会在Startup.cs中的Configure方法中见到类似的代码。
我们建议你仅使用Configure方法来设置请求管道。
应用程序运维代码属于Main方法。
第3遍运维应用程序时,将开创数据库并植入测试数据。
无论曾几何时转移数据模型,都足以去除数据库,更新种子方法,并以新的数据库重新开始再一次开动。
在背后的科目中,您将看到在数据模型更改时怎样修改数据库,而不删除和另行创设它。
兑现钦赐列接纳密码框格局显示与编辑,以及列绑定到复合属性均需求订阅DataGridView的CellFormatting及艾德itingControlShowing事件,并在内部写转换当前Cell的Value,实现列绑定到复合属性,关键点在:伊娃luateValue方法,该办法逻辑很简短,正是基于绑定的习性层级(.分隔)层层遍历获取属性的值,直到遍历完或为空时甘休,最终取得的结果便是绑定的属性的值。最后兑现的效果如下图示:
兑现钦点列采用密码框情势展现与编写制定,以及列绑定到复合属性均供给订阅DataGridView的CellFormatting及EditingControlShowing事件,并在中间写转换当前Cell的Value,达成列绑定到复合属性,关键点在:伊娃luateValue方法,该办法逻辑一点也不细略,正是基于绑定的性情层级(.分隔)层层遍历获取属性的值,直到遍历完或为空时结束,最终收获的结果就是绑定的品质的值。最后达成的服从如下图示:
创办控制器和视图
接下去,您将采用 Visual Studio 脚手架加上 MVC 控制器和视图,并选用 EF
来查询和封存数据。
机关创制CRUD操作方法和视图称为脚手架。
脚手架与代码生成器不相同之处在于,脚手架代码只是基础代码,您能够依据自身的必要进行改动,而壹般状态下,您不会修改生成器生成的代码。
当您需求自定义生成器生成的代码,能够选取一些类,或然在气象产生改变时时重新生成代码。
- 右键单击化解方案能源管理器中的 Controllers 文件夹,然后选择 添加
-> 控制器。 - 在“添加基架”对话框中,选拔“视图使用 Entity Framework 的 MVC
控制器”,点击“添加” - 在“添加控制器”对话框中:
- 模型类选用
Student
。 - 数量上下文类选拔
SchoolContext
- 点击 “添加”。
- 模型类选用
new Controller
当你单击添加时,Visual Studio 脚手架引擎创制1个 StudentsController.cs
文件和一组与控制器一起使用的视图(.cshtml文件)。
(脚手架引擎还足以为你创立数据库上下文,如若你不像在此以前在本教程中那么手动创立它。
您能够通过单击数据上下文类左边的加号在“添加控制器”框中内定新的光景文类。
然后Visual Studio将创立您的DbContext类以及控制器和视图。)
你会专注到控制器将3个 SchoolContext 作为3个构造函数参数。
namespace ContosoUniversity.Controllers
{
public class StudentsController : Controller
{
private readonly SchoolContext _context;
public StudentsController(SchoolContext context)
{
_context = context;
}
ASP.NET 信赖注入负责将 SchoolContext 的三个实例传递到控制器中。
前文中,已经
在 Startup.cs 文件中布局 SchoolContext 的依靠注入。
控制器包罗一个 Index
方法,用于展现数据库中的全体学生。
该措施通过读取数据库上下文实例的 Students
属性获取学生实体集中的学员列表:
public async Task<IActionResult> Index()
{
return View(await _context.Students.ToListAsync());
}
稍后将介绍此代码中的异步编程知识。
视图 Views/Students/Index.cshtml 使用 HTML 表格突显学生列表。
(此处未对脚手架生成的代码进行其余改动,不再贴代码占用著作篇幅。 )
按 CTHummerH二L + F5 运行项目或从菜单中采取 调节和测试 -> 初阶执行(不调试)。
单击 Student
链接,可以观察 DbInitializer.Initialize
方法中插入的测试数据。 依据浏览器窗口的窄小程度,您会看到页面顶部的
Student
链接,也有希望你必须单击右上角的导航空图标才能来看隐藏菜单中的链接。
narrow Page
sdudent index
翻开数据库
当您运行应用程序时,DbInitializer.Initialize 方法调用 EnsureCreated 。
EF 看到未有数据库,所以它创造了多少个,然后 Initialize
方法代码的别的部分用数码填充数据库。 在 Visual Studio 中,您可以行使 SQL
Server 对象财富管理器(SSOX)查看数据库。
假使 SSOX 窗口未有打开,在 Visual Studio 中,点击菜单 “视图” -> “SQL
Server 对象能源管理器”。
在 SSOX 中,单击(localdb)\ MSSQLLocalDB > 数据库,然后单击
ContosoUniversity一,相当于大家前面在 appsettings.json
文件中设置的连年字符串中数据库名称。
展开“表”节点以查看数据库中的表。
ssox
右键单击 Student
表,然后单击 “查看数据”
以查看已开立的列和插入到表中的数据行。
student table
.mdf 和.ldf 数据库文件位于C:\Users<你的用户名> 文件夹中。
因为你在应用程序运行时运营的起头化程序方法中调用 EnsureCreated
,所以未来能够更改 Student
类,删除数据库,再度运维应用程序,并自行重新成立数据库以同盟您的改动。
例如,借使您将 EmailAddress 属性添加到 Student
类,则会在再一次创造的表中看到二个新的EmailAddress 列。
肆.施用BCP(sqlbulkcopy)来贯彻三个例外数据库之间实行数量差异传输(即:数据同步)
四.使用BCP(sqlbulkcopy)来兑现多个不等数据库之间开始展览多少差别传输(即:数据同步)
约定
基于约定优于配备的尺度,Entity Framework
营造三个数据库时,你所需书写的代码很少。
-
DbSet 属性的称号作为表名。
对于未由DbSet属性引用的实体,实体类名用作表名。 -
实体性质名称用于列名。
-
名字为 ID 或 classnameID 的实业性质被辨认为主键属性。
-
应用 导航属性名+实体主键名 命名的习性,会被自动识别为外键,例如:
StudentID 由 Student (导航属性) + ID (Student实体主键名
)组成。外键也得以大致只利用实体主键名,例如 EnrollmentID (外键) 与
EnrollmentID (Enrollment 实体的主键)。
约定能够被覆盖。例如,你能够显式钦命表名,如本课程前面所看到的。
您能够安装列名称并将其余性质设置为主键或外键,那将在后头的课程中聊到。
TransferBulkCopy作用:实现五个不等数据库之间开始展览多少差距传输,BuildInsertOrUpdateToDestTableSql效率:依照目的表及一时表生成更新与插入记录的SQL语句,以此达成:若同步的数据已存在,则更新,不设有,则插入。
TransferBulkCopy功效:完毕三个例外数据库之间展开数据差别传输,BuildInsertOrUpdateToDestTableSql效率:依据目标表及一时半刻表生成更新与插入记录的SQL语句,以此达成:若同步的数码已存在,则更新,不设有,则插入。
异步代码
ASP.NET Core和EF Core的暗中认可使用异步编制程序。
Web
服务器的可用线程数量少于,在高负荷景况下,全部可用线程都只怕都在动用。
当发生那种景色时,服务器不能处理新的央浼,直到线程被释放。
使用同步代码时,许二十八线程大概会被绑定,而实在它们并不曾做任何工作,因为它们正在等候
I/O 达成。 使用异步代码,当进程正在等候I/O
达成时,其线程将被放飞,供服务器用于拍卖任何请求。
由此,异步代码能够更实惠地采纳服务器财富,并且使服务器能够无延迟地拍卖越多流量。
异步代码在运行时引入了少量的付出,不过对于低流量情形,质量下跌可以忽略不计,而对此高流量景况,潜在的属性进步是惊天动地的。
在以下代码中,async 关键字, Task<T> 再次回到值,await 关键字和
ToListAsync 方法共同组成异步执行代码。
public async Task<IActionResult> Index()
{
return View(await _context.Students.ToListAsync());
}
-
async
关键字告诉编写翻译器为格局体生成回调函数,并活动制造再次回到的
Task <IActionResult>
对象。 -
回到类型
Task<IActionResult>
表示正在开始展览的工作,其结果类型为
IActionResult
。 -
await
关键字告诉编写翻译器将该措施分为两某个。
第一有的以异步运行的操作甘休。
第3局地被放入回调方法,该操作在操作达成时被调用。 -
ToListAsync
是ToList
增添方法的异步版本。
当您编写使用实体框架的异步代码时,须求专注的局地作业:
-
唯有会掀起查询或将下令发送到数据库的语句才须要异步执行。 那包涵诸如
ToListAsync
,SingleOrDefaultAsync
和SaveChangesAsync
。
它不应有包蕴,例如,只是改变IQueryable的口舌,类似
var students = context.Students.Where(s => s.LastName == "Davolio")
那样的言辞。 -
EF上下文不是线程安全的:不要尝试并行执行多少个操作。 当您调用任何异步
EF 方法时,请始终使用 await 关键字。 -
1旦你想行使异步代码的性质优势,请确认保证您正在使用的别样库包(例如用于分页)也运用异步,假诺她们调用任何导致查询发送到数据库的办法。
有关.NET中异步编制程序的更加多音信,请参阅 Async
Overview。
/// <summary>
/// 通用数据传输方法(采用SqlBulkCopy快速批量插入,然后再进行处理)
/// </summary>
/// <param name="sourceSelectSql"></param>
/// <param name="sourceConn"></param>
/// <param name="destTableName"></param>
/// <param name="destConn"></param>
/// <param name="colMapFunc"></param>
/// <param name="lastSaveAction"></param>
public void TransferBulkCopy(string sourceSelectSql, SqlConnection sourceConn, string destTableName, SqlConnection destConn, Func<DataTable, Dictionary<string, string>> colMapFunc,
Func<string, DataTable, SqlConnection, SqlConnection, bool> lastSaveAction, bool closeConnection = true)
{
DataTable srcTable = new DataTable();
SqlDataAdapter srcAdapter = new SqlDataAdapter(sourceSelectSql, sourceConn);
srcAdapter.AcceptChangesDuringUpdate = false;
SqlCommandBuilder srcCmdBuilder = new SqlCommandBuilder(srcAdapter);
srcAdapter.Fill(srcTable);
if (srcTable != null && srcTable.Rows.Count > 0)
{
string tempDestTableName = "#temp_" + destTableName;
ClsDatabase.gExecCommand(destConn, string.Format("select top 0 * into {0} from {1}", tempDestTableName, destTableName), false);
List<string> mapDestColNameList = new List<string>();
using (SqlBulkCopy sqlBulkCopy = new SqlBulkCopy(destConn))
{
sqlBulkCopy.DestinationTableName = tempDestTableName;
foreach (var map in colMapFunc(srcTable))
{
sqlBulkCopy.ColumnMappings.Add(map.Key, map.Value);
mapDestColNameList.Add(map.Value);
}
sqlBulkCopy.WriteToServer(srcTable);
}
srcTable.ExtendedProperties.Add(MapDestColNames_String, mapDestColNameList);
bool needUpdate = lastSaveAction(tempDestTableName, srcTable, destConn, sourceConn);
if (needUpdate)
{
if (srcTable.Columns.Contains("TranFlag"))
{
foreach (DataRow row in srcTable.Rows)
{
row["TranFlag"] = true;
}
}
srcAdapter.Update(srcTable);
}
}
if (closeConnection)
{
DisposeConnections(sourceConn, destConn);
}
}
/// <summary>
/// 通用数据传输方法(采用SqlBulkCopy快速批量插入,然后再进行处理)
/// </summary>
/// <param name="sourceSelectSql"></param>
/// <param name="sourceConn"></param>
/// <param name="destTableName"></param>
/// <param name="destConn"></param>
/// <param name="colMapFunc"></param>
/// <param name="lastSaveAction"></param>
public void TransferBulkCopy(string sourceSelectSql, SqlConnection sourceConn, string destTableName, SqlConnection destConn, Func<DataTable, Dictionary<string, string>> colMapFunc,
Func<string, DataTable, SqlConnection, SqlConnection, bool> lastSaveAction, bool closeConnection = true)
{
DataTable srcTable = new DataTable();
SqlDataAdapter srcAdapter = new SqlDataAdapter(sourceSelectSql, sourceConn);
srcAdapter.AcceptChangesDuringUpdate = false;
SqlCommandBuilder srcCmdBuilder = new SqlCommandBuilder(srcAdapter);
srcAdapter.Fill(srcTable);
if (srcTable != null && srcTable.Rows.Count > 0)
{
string tempDestTableName = "#temp_" + destTableName;
ClsDatabase.gExecCommand(destConn, string.Format("select top 0 * into {0} from {1}", tempDestTableName, destTableName), false);
List<string> mapDestColNameList = new List<string>();
using (SqlBulkCopy sqlBulkCopy = new SqlBulkCopy(destConn))
{
sqlBulkCopy.DestinationTableName = tempDestTableName;
foreach (var map in colMapFunc(srcTable))
{
sqlBulkCopy.ColumnMappings.Add(map.Key, map.Value);
mapDestColNameList.Add(map.Value);
}
sqlBulkCopy.WriteToServer(srcTable);
}
srcTable.ExtendedProperties.Add(MapDestColNames_String, mapDestColNameList);
bool needUpdate = lastSaveAction(tempDestTableName, srcTable, destConn, sourceConn);
if (needUpdate)
{
if (srcTable.Columns.Contains("TranFlag"))
{
foreach (DataRow row in srcTable.Rows)
{
row["TranFlag"] = true;
}
}
srcAdapter.Update(srcTable);
}
}
if (closeConnection)
{
DisposeConnections(sourceConn, destConn);
}
}
/// <summary>
/// 生成同步插入及更新目的表SQL语句
/// </summary>
/// <param name="destTableName"></param>
/// <param name="tempdestTableName"></param>
/// <param name="pkWhere"></param>
/// <param name="mapDestColNames"></param>
/// <returns></returns>
public string BuildInsertOrUpdateToDestTableSql(string destTableName, string tempdestTableName, string[] pkWhereColNames, object mapDestColNames)
{
var mapDestColNameList = mapDestColNames as List<string>;
string updateColNames = null;
foreach (string col in mapDestColNameList)
{
if (!pkWhereColNames.Contains(col, StringComparer.OrdinalIgnoreCase))
{
updateColNames += string.Format(",{0}=tnew.{0}", col);
}
}
updateColNames = updateColNames.Substring(1);
string insertColNames = string.Join(",", mapDestColNameList);
string pkWhereSql = null;
foreach (string col in pkWhereColNames)
{
pkWhereSql += string.Format("and told.{0}=tnew.{0}", col);
}
pkWhereSql = pkWhereSql.Substring(3);
StringBuilder sqlBuilder = new StringBuilder();
sqlBuilder.AppendFormat("UPDATE {0} SET {1} FROM {0} told INNER JOIN {2} tnew ON {3} " + Environment.NewLine,
destTableName, updateColNames, tempdestTableName, pkWhereSql);
sqlBuilder.AppendFormat("INSERT INTO {0}({1}) SELECT {1} FROM {2} tnew WHERE NOT EXISTS(SELECT 1 FROM {0} told WHERE {3}) " + Environment.NewLine,
destTableName, insertColNames, tempdestTableName, pkWhereSql);
return sqlBuilder.ToString();
}
小结
你现在创设了二个简便的应用程序,使用 Entity Framework Core 和 SQL Server
Express LocalDB 存款和储蓄和体现数据。 在底下的课程中,您将学习如何执行基本的
CRUD(创设,读取,更新和删除)操作。
动用示例如下:
/// <summary>
/// 生成同步插入及更新目的表SQL语句
/// </summary>
/// <param name="destTableName"></param>
/// <param name="tempdestTableName"></param>
/// <param name="pkWhereColNames"></param>
/// <param name="mapDestColNames"></param>
/// <param name="sqlType">0=生成INSERT与UPDATE;1=生成UPDATE语句;2=生成INSERT语句</param>
/// <returns></returns>
public string BuildInsertOrUpdateToDestTableSql(string destTableName, string tempdestTableName, string[] pkWhereColNames, object mapDestColNames, int sqlType = 0)
{
var mapDestColNameList = mapDestColNames as List<string>;
string updateColNames = null;
foreach (string col in mapDestColNameList)
{
if (!pkWhereColNames.Contains(col, StringComparer.OrdinalIgnoreCase))
{
updateColNames += string.Format(",{0}=tnew.{0}", col);
}
}
updateColNames = updateColNames.Substring(1);
string insertColNames = string.Join(",", mapDestColNameList);
string pkWhereSql = null;
foreach (string col in pkWhereColNames)
{
pkWhereSql += string.Format(" and told.{0}=tnew.{0} ", col);
}
pkWhereSql = pkWhereSql.Trim().Substring(3);
StringBuilder sqlBuilder = new StringBuilder();
if (sqlType == 0 || sqlType == 1)
{
sqlBuilder.AppendFormat("UPDATE {0} SET {1} FROM {0} told INNER JOIN {2} tnew ON {3} " + Environment.NewLine,
destTableName, updateColNames, tempdestTableName, pkWhereSql);
}
if (sqlType == 0 || sqlType == 2)
{
sqlBuilder.AppendFormat("INSERT INTO {0}({1}) SELECT {1} FROM {2} tnew WHERE NOT EXISTS(SELECT 1 FROM {0} told WHERE {3}) " + Environment.NewLine,
destTableName, insertColNames, tempdestTableName, pkWhereSql);
}
return sqlBuilder.ToString();
}
public void SendData_CustomerAuthorization()
{
try
{
SqlConnection obConnLMS1 = new SqlConnection(master.connLMSStr);
SqlConnection obConnWEB1 = new SqlConnection(master.connWEBStr);
string selectSql = @"SELECT TOP {0} Id,Phone,Mac,IsSet,LastLoginTime,PCName,TranFlag FROM TWEB_CustomerAuthorization WHERE TranFlag=0 ORDER BY Id ";
selectSql = string.Format(selectSql, master.batchSize);
master.TransferBulkCopy(selectSql, obConnWEB1,
"TB_CustomerAuthorization", obConnLMS1,
(stable) =>
{
var colMaps = new Dictionary<string, string>();
foreach (DataColumn col in stable.Columns)
{
if (!col.ColumnName.Equals("TranFlag", StringComparison.OrdinalIgnoreCase))
{
colMaps.Add(col.ColumnName, col.ColumnName);
}
}
return colMaps;
},
(tempTableName, stable, destConn, srcConn) =>
{
StringBuilder saveSqlBuilder = new StringBuilder("begin tran" + Environment.NewLine);
string IUSql = master.BuildInsertOrUpdateToDestTableSql("TB_CustomerAuthorization", tempTableName, new[] { "Id" }, stable.ExtendedProperties[master.MapDestColNames_String]);
saveSqlBuilder.Append(IUSql);
saveSqlBuilder.AppendLine("commit");
ClsDatabase.gExecCommand(destConn, saveSqlBuilder.ToString());
master.WriteMsg(master.lstSended, string.Format("上传时间:{0:yyyy-MM-dd HH:mm}\t SendData_CustomerAuthorization \t Succeed:{1}", DateTime.Now, stable.Rows.Count));
return true;
});
}
catch (Exception ex)
{
master.WriteMsg(master.lstErrorInfo, DateTime.Now.ToString("yyyy-MM-dd HH:mm") + "\t" + "SendData_CustomerAuthorization" + "\t" + ex.Message.ToString());
}
}
一同原理如下:
1.概念好查询源服务器的要求共同的表(壹般表中我们定义一个用来是还是不是同步的标识字段,如:TranFlag
Bit类型,0代表新数据,未共同,1意味着已同步);
利用示例如下:
二.查询源服务器的要求1起的表的记录(一般是TranFlag=0的记录),利用SqlDataAdapter+SqlCommandBuilder
装载Dataset,指标是后续能够采用SqlData艾达pter直接扭转更新命令并实行;
public void SendData_CustomerAuthorization()
{
try
{
SqlConnection obConnLMS1 = new SqlConnection(master.connLMSStr);
SqlConnection obConnWEB1 = new SqlConnection(master.connWEBStr);
string selectSql = @"SELECT TOP {0} Id,Phone,Mac,IsSet,LastLoginTime,PCName,TranFlag FROM TWEB_CustomerAuthorization WHERE TranFlag=0 ORDER BY Id ";
selectSql = string.Format(selectSql, master.batchSize);
master.TransferBulkCopy(selectSql, obConnWEB1,
"TB_CustomerAuthorization", obConnLMS1,
(stable) =>
{
var colMaps = new Dictionary<string, string>();
foreach (DataColumn col in stable.Columns)
{
if (!col.ColumnName.Equals("TranFlag", StringComparison.OrdinalIgnoreCase))
{
colMaps.Add(col.ColumnName, col.ColumnName);
}
}
return colMaps;
},
(tempTableName, stable, destConn, srcConn) =>
{
StringBuilder saveSqlBuilder = new StringBuilder("begin tran" + Environment.NewLine);
string IUSql = master.BuildInsertOrUpdateToDestTableSql("TB_CustomerAuthorization", tempTableName, new[] { "Id" }, stable.ExtendedProperties[master.MapDestColNames_String]);
saveSqlBuilder.Append(IUSql);
saveSqlBuilder.AppendLine("commit");
ClsDatabase.gExecCommand(destConn, saveSqlBuilder.ToString());
master.WriteMsg(master.lstSended, string.Format("上传时间:{0:yyyy-MM-dd HH:mm}\t SendData_CustomerAuthorization \t Succeed:{1}", DateTime.Now, stable.Rows.Count));
return true;
});
}
catch (Exception ex)
{
master.WriteMsg(master.lstErrorInfo, DateTime.Now.ToString("yyyy-MM-dd HH:mm") + "\t" + "SendData_CustomerAuthorization" + "\t" + ex.Message.ToString());
}
}
3.选用insert
into从指标服务器的将被同台的表复制结构产生三个近期表,表名壹般是:#temp_目标服务器的将被联合表名
,那样权且表与实体表的结构完全1致;
联机原理如下:
4.实例化三个SqlBulkCopy,并树立源服务器的内需联合的表字段与指标一时半刻表字段的映照,然后实施跨服务器传输;
肆.一.概念好查询源服务器的内需共同的表(1般表中大家定义二个用于是不是同步的标识字段,如:TranFlag
Bit类型,0意味新数据,未共同,壹表示已联合署名);
五.使用 BuildInsertOrUpdateToDestTableSql 方法 ,生成
目标服务器的将被1并的表 与
一时表的插入与创新SQL语句(以往在同一个库了,想怎么用SQL语句均可)
四.2.查询源服务器的内需1块的表的笔录(一般是TranFlag=0的记录),利用SqlData艾达pter+SqlCommandBuilder
装载Dataset,目的是继续能够行使SqlDataAdapter直接生成更新命令并推行;
陆.为确认保证一致性,故外层还需包裹事务SQL语句,若还需投入别的处理SQL,能够加在begin
tran …
commit代码块中即可,最后执行SQL语句:gExecCommand(ClsDatabase.gExecCommand是一个SQLDB
HELPE中华V 类的实施SQL命令的办法)
4.三.施用insert
into从指标服务器的将被联合的表复制结构发生一个一时半刻表,表名1般是:#temp_指标服务器的将被1道表名
,那样权且表与实体表的布局完全一致;
四.四.实例化1个SqlBulkCopy,并确立源服务器的内需一块的表字段与目标一时表字段的照射,然后实施跨服务器传输;
4.五.应用 BuildInsertOrUpdateToDestTableSql 方法 ,生成
指标服务器的将被同步的表 与
暂且表的插入与更新SQL语句(以往在同一个库了,想怎么用SQL语句均可)
肆.六.为保证1致性,故外层还需包裹事务SQL语句,若还需投入其余处理SQL,能够加在begin
tran …
commit代码块中即可,最后执行SQL语句:gExecCommand(ClsDatabase.gExecCommand是四个SQLDB
HELPEHaval 类的推行SQL命令的格局)
五.实现同2个WINDOWS SE奥迪Q5VICE程序
COPY多份,然后经过变更自定义的劳动ID(ServiceID)配置项来达成:同叁个服务程序安装成多少个例外的WINDOWS服务进程:
伍.一.成立八个WINDOWS服务项目,在ProjectInstaller设计器界面通过右键弹出菜谱选拔安装程序(serviceProcessInstaller壹、serviceInstaller一)、并设置好ServiceName、DisplayName、Description、Account等,如下图示:
5.贰.在ProjectInstaller构造函数中加进从CONFIG文件中读取自定义的劳务ID(ServiceID)配置项的值,然后将ServiceID拼加到预设的ServiceName后边,以便实际依照ServiceID能够设置成分化ServiceID后缀的劳动进度,关键点在于改变ServiceName,另二个关键点是从CONFIG文件中获得ServiceID,由于设置时,守旧的章程不可能寻常读取到CONFIG,只好通过Assembly.GetExecutingAssembly().Location
来赢妥贴前执行的先后集的门路再拼成CONFIG文件路径,最后读出ServiceID的值,示例代码如下:
public partial class ProjectInstaller : System.Configuration.Install.Installer
{
public ProjectInstaller()
{
InitializeComponent();
string assyLocation = System.Reflection.Assembly.GetExecutingAssembly().Location;
string assyCfgPath = assyLocation + ".config";
string installServiceLogPath = Path.Combine(Path.GetDirectoryName(assyLocation), "InstallServiceLog.log");
string serviceID = ConfigUtil.GetAppSettingValueForConfigPath("ServiceID", assyCfgPath);
System.IO.File.AppendAllText(installServiceLogPath, string.Format("[{0:yyyy-MM-dd HH:mm:ss}] ServiceAssembly ConfigPath:{1};\r\n", DateTime.Now, assyCfgPath));
if (!string.IsNullOrWhiteSpace(serviceID))
{
this.serviceInstaller1.DisplayName = "TestService_" + serviceID;
this.serviceInstaller1.ServiceName = "TestService_" + serviceID;
}
System.IO.File.AppendAllText(installServiceLogPath, string.Format("[{0:yyyy-MM-dd HH:mm:ss}] ProjectInstaller.ProjectInstaller() ->ServiceID:{1},ServiceName:{2}; \r\n", DateTime.Now, serviceID, this.serviceInstaller1.ServiceName));
}
}
伍.三.在服务类的构造函数中1样扩充从CONFIG中读取自定义的服务ID(ServiceID)配置项的值,然后将ServiceID拼加到预设的ServiceName后边(注意应与上述ProjectInstaller中钦定的ServiceName相同),示例代码如下:
public partial class TestService: ServiceBase
{
public TestService()
{
serviceID = ConfigUtil.GetAppSettingValue("ServiceID");
if (!string.IsNullOrWhiteSpace(serviceID))
{
this.ServiceName = "TestService_" + serviceID;
}
}
}
上述三步就到位了同3个服务程序安装成多个分歧的WINDOWS服务进程,这一个依然相比较实用的哦!上述ConfigUtil是包裹的一个安插文件读写支持类,在此之前小说有介绍,后边也会公布2个更完整的ConfigUtil类。