澳门新萄京官方网站-www.8455.com-澳门新萄京赌场网址

澳门新萄京官方网站:近期开发项目中用到的编

2019-05-18 作者:www.8455.com   |   浏览(194)

1.默认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上下文类名均可能不相同。

2.支持一个通用对象的XML序列化(即:一个类中有可变类型属性成员,需要不同的序列结果及生成不同的序列元素名称),具体实现代码如下:

一个需要被序列化成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; }
    }

注意上面代码中,需要关注:DemoDetail属性及DemoDetail<T>类,DemoDetail属性仅是为了生成detail元素节点,而子节点则由DemoDetail<T>类来进行生成,DemoDetail<T>是实现了IXmlSerializable接口,在XML序列化时,DemoDetail<T>类仅将body属性对应的T类型实例内容进行序列化(WriteRaw),而反序列化时,则先反序列化body属性对应的T类型实例,然后赋值给body属性,这也是巧妙之处,DemoDetail<T>类本身并没有真正参与到序列化中,故序列化的字符串也看不到DemoDetail<T>类相关的元素,DemoDetail<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  = "rn"   XmlHelper.XmlSerialize(demo2, Encoding.UTF8);

            textBox1.Text  = "rn"   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>

3.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及EditingControlShowing事件,并在其中写转换当前Cell的Value,实现列绑定到复合属性,关键点在:EvaluateValue方法,该方法逻辑很简单,就是根据绑定的属性层级(.分隔)层层遍历获取属性的值,直到遍历完或为空时停止,最后得到的结果即是绑定的属性的值。最终实现的效果如下图示:

澳门新萄京官方网站 1

  

 

1.默认EF生成的连接字符串比较的长和怪异,若想使用普通的连接字符串来连...

    在上一节中,实现了CodeFirst快速入门。但是很多与数据库的细节还无法自定义。以及使用EF过程中,需要注意的事项。

前言

Contoso 大学示例 Web 应用程序演示如何使用实体框架(EF)Core 2.0 和 Visual Studio 2017 创建 ASP.NET Core 2.0 MVC Web 应用程序。
示例应用程序是虚构的Contoso大学的网站。 它包括学生入学,课程创建和教师任务等功能。 这是一系列教程中的第一章,介绍如何从头构建Contoso大学示例应用程序。
下载或查看已完成的应用程序 - 官方。

本系列文章翻译索引目录

EF Core 2.0 是EF的最新版本,但还没有包括所有的 EF 6.x 功能。 有关 EF 6.x 和 EF Core 之间如何选择的信息,请参阅 EF Core vs. EF6.x。 如果您选择 EF 6.x ,请参阅本教程系列的以前版本。

注意事项

  • 对于本教程的 ASP.NET Core 1.1 版本,请参阅PDF格式的本教程的 VS 2017 Update 2版本。
  • 对于本教程的 Visual Studio 2015 版本,请参阅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];
        }
    }

    在本节中,会涉及到以下

开发环境

安装如下工具:

  • .NET Core 2.0.0 SDK 或更新版本。
  • Visual Studio 2017 V15.3 或更高版本,安装 ASP.NET 和 Web 开发工具。

注意上面的类是一个分部类:partial,同时BuildEntityConnectionString方法是一个静态方法,在BuildEntityConnectionString方法中ProviderConnectionString = EncryptUtility.DesDecrypt("XXXXX", connStrSetting.ConnectionString);是关键,我这里是对config中的连接字符串 也都进行了加密,故此处我需要解密,若无这个需求可以直接:ProviderConnectionString =connStrSetting.ConnectionString即可。后续实例化EF上下文对象时,请使用:DataEntities(string connName)这个构造涵数即可,DataEntities是具体的EF上下文对象,大家的EF上下文类名均可能不相同。

  • EF中的连接字符串
  • EF的对象状态
  • 延迟加载,为什么需要Virtual,贪婪加载
  • 绕过EF,直接SQL查询
  • 查看EF生成SQL语句
  • 实体与数据库的映射通过DataAnnotation与FluentAPI两种方式
  • 结合MVC异步控制器做个CRUD例子
  • 建议

故障排除

如果遇到问题,您无法解决,通常可以通过将代码与完成的项目进行比较来找到解决方案。 有关常见错误和解决方法的列表,请参阅本系列最后一个教程的故障排除部分。 如果您没有找到所需的内容,您也可以在 StackOverflow.com 上提问。

小贴士

本系列包含10个教程,每个教程都建立在早期教程中的基础之上。 在成功完成每个教程之后,请考虑保存项目的副本。 然后,如果遇到问题,您可以从上一个教程重新开始,无需从头开始。

2.支持一个通用对象的XML序列化(即:一个类中有可变类型属性成员,需要不同的序列结果及生成不同的序列元素名称),具体实现代码如下:

 

Contoso 大学网站应用

教程中构建的是一个简单的大学网站。
用户可以查看和更新学生,课程和教师信息。 下面是您将要创建的一些页面。

澳门新萄京官方网站 2

index

澳门新萄京官方网站 3

edit

网站的 UI 风格与内置模板生成的 UI 风格保持一致,本教程主要关注如何使用 Entity Framework。

一个需要被序列化成XML的类:其中要求生成的XML元素detail必需有子元素,且子元素名称及子元素内部属性根据类型的不同而不同(即:detail元素下的子元素是可变的)

连接字符串

创建 ASP.NET Core MVC 网站应用

打开 Visual Studio 并创建名为 “ContosoUniversity” 的新 ASP.NET Core C# web 项目。

  • 从文件菜单中,选择新建>项目。
  • 从左窗格中选择 已安装 -> Visual C# -> Web 。
  • 中间窗格选择 ASP.NET Core Web 应用程序。
  • 输入 ContosoUniversity 作为项目名称,然后单击确定。
![](https://upload-images.jianshu.io/upload_images/4235187-1c7a131da4c70365.png)

newProject
  • 等待 "新建 ASP.NET Core Web 应用程序" 对话框出现
  • 选择 ASP.NET Core 2.0 和 Web应用程序(模型视图控制器)模板。
  • 注意:本教程需要 ASP.NET Core 2.0 和 EF Core 2.0 或更高版本 - 确保未选择ASP.NET Core 1.1。
  • 确保认证设置为“不进行身份验证”。
  • 单击 “确定” 按钮。

澳门新萄京官方网站 4

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; }
    }

  1个完整的连接字符串  IP:端口实例名 数据库名组成

设置网站样式

简单修改几个位置,设置站点菜单,布局和主页。
打开 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>

按 CTRL F5 运行项目或从菜单中选择 调试-> 开始执行(不调试), 您将在浏览器中看到本教程中实现的首页。

澳门新萄京官方网站 5

image.png

注意上面代码中,需要关注:DemoDetail属性及DemoDetail<T>类,DemoDetail属性仅是为了生成detail元素节点,而子节点则由DemoDetail<T>类来进行生成,DemoDetail<T>是实现了IXmlSerializable接口,在XML序列化时,DemoDetail<T>类仅将body属性对应的T类型实例内容进行序列化(WriteRaw),而反序列化时,则先反序列化body属性对应的T类型实例,然后赋值给body属性,这也是巧妙之处,DemoDetail<T>类本身并没有真正参与到序列化中,故序列化的字符串也看不到DemoDetail<T>类相关的元素,DemoDetail<T>类仅仅是一个XML序列化格式生成的中介。序列化的XML结果如下:

  Nuget安装完EF会在*.config的entityFramework节点下添加,表示使用LocalDb数据库,实例名为v11.0。

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。

序列化代码:

<defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
      <parameters>
        <parameter value="v11.0" />
      </parameters>
</defaultConnectionFactory>

创建数据模型

接下来,您将为Contoso大学应用程序创建实体课程。 您将从以下三个实体开始。

澳门新萄京官方网站 6

class diagram

在 Student 与 Enrollement 实体间是一个一对多的关系, 在 Course 与 Enrollment 间也存在一对多关系。 换句话说,学生可以参加任意数量的课程,课程可以有任意数量的学生注册。

在以下部分中,您将为每个实体创建一个类。

            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  = "rn"   XmlHelper.XmlSerialize(demo2, Encoding.UTF8);

            textBox1.Text  = "rn"   XmlHelper.XmlSerialize(demo3, Encoding.UTF8);

  而数据库名,默认为类的全限定名。如下调用父类构造函数,表示数据库名为SaleDb。而不是命名空间 类名。

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 实体。

如果导航属性可以容纳多个实体(在多对多或一对多关系中),则其类型必须是可以添加,删除和更新条目的列表,例如ICollection <T>。您可以指定ICollection <T>或类型,如List <T>或HashSet <T>。如果指定ICollection <T>,EF默认创建一个HashSet <T>集合。

序列化的XML:

    public class SaleDb : DbContext
    {
        public SaleDb():base("SaleDb")
        {      
        }
    }

Enrollment 实体

澳门新萄京官方网站 7

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 模式。 通常你会只选择一种模式,并在整个数据模型中使用。 在这里,不同的模式是为了演示,说明您可以使用任一模式。 在后面的教程中,您将看到如何使用没有类名的 ID 可以更容易地在数据模型中实现继承。

Grade (等级) 属性是一个枚举类型。 Grade 类型声明后的 ? 表示 可为空类型。 一个空的等级和一个值为0的等级是不同的 -- 空表示等级未知或者尚未被赋值。

StudentID 属性是外键,相应的导航属性是 Student。 一个 Enrollment 实体与一个 Student 实体相关联,因此该属性只能持有保存单个 Studnet 实体(与您之前看到的可以包含多个注册实体的 Student.Enrollments 导航属性不同)。

CourseID 属性是外键, 对应的导航属性是 Course。 一个 Enrollment 实体与一个 Course 实体相关联。

当一个属性名称符合模式 <导航属性名><主键名> , 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>

  当然,在这个构造函数里,可以完整的写上完整的连接字符串。

Course 实体

澳门新萄京官方网站 8

Course

在 Models 文件夹中,创建一个名为 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 实现指定列采取密码框模式显示与编辑,以及列绑定到复合属性(即:绑定到多层次属性),具体实现代码如下:

 

创建数据库上下文 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; }
    }

EF的对象状态

应用数据上下文 - 使用依赖注入

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"
    }
  }
}

绑定到数据源:

  db.Entry(TEntity).State  (附加Entity,并设置状态)

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;
    public enum EntityState
    {
        // 摘要: 
        //     The entity is not being tracked by the context.  An entity is in this state
        //     immediately after it has been created with the new operator or with one of
        //     the System.Data.Entity.DbSet Create methods.
        Detached = 1,
        //
        // 摘要: 
        //     The entity is being tracked by the context and exists in the database, and
        //     its property values have not changed from the values in the database.
        Unchanged = 2,
        //
        // 摘要: 
        //     The entity is being tracked by the context but does not yet exist in the
        //     database.
        Added = 4,
        //
        // 摘要: 
        //     The entity is being tracked by the context and exists in the database, but
        //     has been marked for deletion from the database the next time SaveChanges
        //     is called.
        Deleted = 8,
        //
        // 摘要: 
        //     The entity is being tracked by the context and exists in the database, and
        //     some or all of its property values have been modified.
        Modified = 16,
    }
添加代码,使用测试数据初始化数据库

EF 将为您创建一个空数据库。 在本节中,您将编写一个创建数据库后调用的方法,以便使用测试数据进行填充。

在这里,您将使用 EnsureCreated 方法自动创建数据库。 在后面的教程中,您将看到如何使用 Code First Migration (代码优先迁移) 来更改数据库架构而不是删除和重新创建数据库来处理架构更改。

Data 文件夹中,创建一个名为 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方法。

首次运行应用程序时,将创建数据库并植入测试数据。 无论何时更改数据模型,都可以删除数据库,更新种子方法,并以新的数据库重新开始重新启动。 在后面的教程中,您将看到在数据模型更改时如何修改数据库,而不删除和重新创建它。

实现指定列采取密码框模式显示与编辑,以及列绑定到复合属性均需要订阅DataGridView的CellFormatting及EditingControlShowing事件,并在其中写转换当前Cell的Value,实现列绑定到复合属性,关键点在:EvaluateValue方法,该方法逻辑很简单,就是根据绑定的属性层级(.分隔)层层遍历获取属性的值,直到遍历完或为空时停止,最后得到的结果即是绑定的属性的值。最终实现的效果如下图示:

  

创建控制器和视图

接下来,您将使用 Visual Studio 脚手架添加 MVC 控制器和视图,并使用 EF 来查询和保存数据。

自动创建CRUD操作方法和视图称为脚手架。 脚手架与代码生成器不同之处在于,脚手架代码只是基础代码,您可以根据自己的需要进行修改,而通常情况下,您不会修改生成器生成的代码。 当您需要自定义生成器生成的代码,可以使用部分类,或者在情况发生改变时时重新生成代码。

  • 右键单击解决方案资源管理器中的 Controllers 文件夹,然后选择 添加 -> 控制器。
  • 在“添加基架”对话框中,选择“视图使用 Entity Framework 的 MVC 控制器”,点击“添加”
  • 在“添加控制器”对话框中:
    • 模型类选择 Student
    • 数据上下文类选择 SchoolContext
    • 点击 “添加”。

澳门新萄京官方网站 9

new Controller

当您单击添加时,Visual Studio 脚手架引擎创建一个 StudentsController.cs 文件和一组与控制器一起使用的视图(.cshtml文件)。

(脚手架引擎还可以为您创建数据库上下文,如果您不像以前在本教程中那样手动创建它。 您可以通过单击数据上下文类右侧的加号在“添加控制器”框中指定新的上下文类。 然后Visual Studio将创建您的DbContext类以及控制器和视图。)

你会注意到控制器将一个 SchoolContext 作为一个构造函数参数。

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 表格显示学生列表。 (此处未对脚手架生成的代码进行任何修改,不再贴代码占用文章篇幅。 )

按 CTRL F5 运行项目或从菜单中选择 调试 -> 开始执行(不调试)。

单击 Student澳门新萄京官方网站:近期开发项目中用到的编码小技巧汇总说明,入门教程。 链接,可以看到 DbInitializer.Initialize 方法中插入的测试数据。 根据浏览器窗口的狭窄程度,您会看到页面顶部的 Student 链接,也有可能您必须单击右上角的导航图标才能看到隐藏菜单中的链接。

澳门新萄京官方网站 10

narrow Page

澳门新萄京官方网站 11

sdudent index

澳门新萄京官方网站 12

延迟加载,贪婪加载,为什么需要Virtual

查看数据库

当您启动应用程序时,DbInitializer.Initialize 方法调用 EnsureCreated 。 EF 看到没有数据库,所以它创建了一个,然后 Initialize 方法代码的其余部分用数据填充数据库。 在 Visual Studio 中,您可以使用 SQL Server 对象资源管理器(SSOX)查看数据库。

如果 SSOX 窗口尚未打开,在 Visual Studio 中,点击菜单 “视图” -> “SQL Server 对象资源管理器”。
在 SSOX 中,单击(localdb) MSSQLLocalDB > 数据库,然后单击 ContosoUniversity1,也就是我们前面在 appsettings.json 文件中设置的连接字符串中数据库名称。
展开“表”节点以查看数据库中的表。

澳门新萄京官方网站 13

澳门新萄京官方网站:近期开发项目中用到的编码小技巧汇总说明,入门教程。ssox

右键单击 Student 表,然后单击 “查看数据” 以查看已创建的列和插入到表中的数据行。

澳门新萄京官方网站 14

student table

.mdf 和.ldf 数据库文件位于C:Users<你的用户名> 文件夹中。
因为您在应用程序启动时运行的初始化程序方法中调用 EnsureCreated ,所以现在可以更改 Student 类,删除数据库,再次运行应用程序,并自动重新创建数据库以匹配您的更改。 例如,如果您将 EmailAddress 属性添加到 Student 类,则会在重新创建的表中看到一个新的EmailAddress 列。

4.利用BCP(sqlbulkcopy)来实现两个不同数据库之间进行数据差异传输(即:数据同步)

  延迟加载(lazy load)是(也称为懒加载),延迟加载机制是为了避免一些无谓的性能开销而提出来的,所谓延迟加载就是当在真正需要数据的时候,才真正执行数据加载操作。可以简单理解为,只有在使用的时候,才会发出sql语句进行查询。

约定

基于约定优于配置的原则,Entity Framework 构建一个数据库时,你所需书写的代码很少。

  • DbSet 属性的名称用作表名。 对于未由DbSet属性引用的实体,实体类名用作表名。

  • 实体属性名称用于列名。

  • 名为 ID 或 classnameID 的实体属性被识别为主键属性。

  • 使用 导航属性名 实体主键名 命名的属性,会被自动识别为外键,例如: StudentID 由 Student (导航属性) ID (Student实体主键名 )组成。外键也可以简单只使用实体主键名,例如 EnrollmentID (外键) 与 EnrollmentID (Enrollment 实体的主键)。

约定可以被覆盖。例如,你可以显式指定表名,如本教程前面所看到的。 您可以设置列名称并将任何属性设置为主键或外键,这将在后面的教程中提及。

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 关键字告诉编译器将该方法分为两部分。 第一部分以异步启动的操作结束。 第二部分被放入回调方法,该操作在操作完成时被调用。

  • ToListAsyncToList 扩展方法的异步版本。

当您编写使用实体框架的异步代码时,需要注意的一些事情:

  • 只有会引发查询或将命令发送到数据库的语句才需要异步执行。 这包括例如 ToListAsyncSingleOrDefaultAsyncSaveChangesAsync。 它不应该包括,例如,只是更改IQueryable的语句,类似 var students = context.Students.Where(s => s.LastName == "Davolio") 这样的语句。

  • EF上下文不是线程安全的:不要尝试并行执行多个操作。 当您调用任何异步 EF 方法时,请始终使用 await 关键字。

  • 如果您想利用异步代码的性能优势,请确保您正在使用的任何库包(例如用于分页)也使用异步,如果他们调用任何导致查询发送到数据库的方法。

有关.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);
            }
        }

在EF中,默认是支持延迟加载的。

小结

您现在创建了一个简单的应用程序,使用 Entity Framework Core 和 SQL Server Express LocalDB 存储和显示数据。 在下面的教程中,您将学习如何执行基本的 CRUD(创建,读取,更新和删除)操作。

 

  db.Configuration.LazyLoadingEnabled = false;    //设置禁用延迟加载

        /// <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();

        }

如果想单个贪婪加载某个表。则通过Include("表名")

  

  db.Orders.Include("OrderItems");          //把OrderItems表一并加载进来

 

 

使用示例如下:

Virtual关键字

        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());
            }
        }

通常相关连的表作为Entity属性的时候,使用Virtual关键字修饰符。如果不加Virtual关键字,则关联的类只是一个普通的POCO类型。get,set方法为空实现。

同步原理如下:

使用Virtual关键字后,EF生成一个代理类来重写get和set方法。实现需要的功能。

4.1.定义好查询源服务器的需要同步的表(一般表中我们定义一个用于是否同步的标识字段,如:TranFlag Bit类型,0表示新数据,未同步,1表示已同步);

  

4.2.查询源服务器的需要同步的表的记录(一般是TranFlag=0的记录),利用SqlDataAdapter SqlCommandBuilder 装载Dataset,目的是后续可以利用SqlDataAdapter直接生成更新命令并执行;

绕过EF,直接SQL查询

4.3.利用insert into从目的服务器的将被同步的表复制结构产生一个临时表,表名一般是:#temp_目的服务器的将被同步表名 ,这样临时表与实体表的结构完全一致;

  EF作为一个框架,也不可能做到满足所有需求。EF提供了直接操作Ado.net方式。

4.4.实例化一个SqlBulkCopy,并建立源服务器的需要同步的表字段与目的临时表字段的映射,然后执行跨服务器传输;

有三个 API 支持:

4.5.利用 BuildInsertOrUpdateToDestTableSql 方法 ,生成  目的服务器的将被同步的表 与 临时表的插入与更新SQL语句(现在在同一个库了,想怎么用SQL语句均可)  

  • DbContext.Database.ExecuteSqlCommand
  • DbContext.Database.SqlQuery
  • DbSet.SqlQuery

4.6.为确保一致性,故外层还需包裹事务SQL语句,若还需加入其它处理SQL,可以加在begin tran  ... commit代码块中即可,最后执行SQL语句:gExecCommand(ClsDatabase.gExecCommand是一个SQLDB HELPER 类的执行SQL命令的方法)

         

 

执行sql 返回受影响函数。

5.实现同一个WINDOWS SERVICE程序 COPY多份,然后通过更改自定义的服务ID(ServiceID)配置项来实现:同一个服务程序安装成多个不同的WINDOWS服务进程:

public int ExecuteSqlCommand(string sql, params object[] parameters); 

5.1.创建一个WINDOWS服务项目,在ProjectInstaller设计器界面通过右键弹出菜单选择安装程序(serviceProcessInstaller1、serviceInstaller1)、并设置好ServiceName、DisplayName、Description、Account等,如下图示:

 

澳门新萄京官方网站 15

执行sql 返回查询结果并自动映射到指定类上。(不会被EF跟踪状态)

5.2.在ProjectInstaller构造函数中增加从CONFIG文件中读取自定义的服务ID(ServiceID)配置项的值,然后将ServiceID拼加到预设的ServiceName后面,以便实际根据ServiceID能够安装成不同ServiceID后缀的服务进程,关键点在于改变ServiceName,另一个关键点是从CONFIG文件中获取ServiceID,由于安装时,传统的方式无法正常读取到CONFIG,只能通过Assembly.GetExecutingAssembly().Location 来获取当前执行的程序集的路径再拼成CONFIG文件路径,最后读出ServiceID的值,示例代码如下:

DbRawSqlQuery<TElement> SqlQuery<TElement>(string sql, params object[] parameters);
    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};rn", 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}; rn", DateTime.Now, serviceID, this.serviceInstaller1.ServiceName));
        }
    }

 

5.3.在服务类的构造函数中同样增加从CONFIG中读取自定义的服务ID(ServiceID)配置项的值,然后将ServiceID拼加到预设的ServiceName后面(注意应与上述ProjectInstaller中指定的ServiceName相同),示例代码如下:  

执行sql 返回查询结果并自动映射到指定类上。(被EF跟踪状态。DbSqlQuery继承自DbRawSqlQuery)

public partial class TestService: ServiceBase
{
     public TestService()
    {
          serviceID = ConfigUtil.GetAppSettingValue("ServiceID");
            if (!string.IsNullOrWhiteSpace(serviceID))
            {
                this.ServiceName = "TestService_"   serviceID;
            }
    }

}
virtual DbSqlQuery<TEntity> SqlQuery(string sql, params object[] parameters);

 上述三步就完成了同一个服务程序安装成多个不同的WINDOWS服务进程,这个还是比较实用的哦!上述ConfigUtil是封装的一个配置文件读写帮助类,之前文章有介绍,后面也会发布一个更完整的ConfigUtil类。

 

查看EF生成SQL语句

  当使用EF执行一个复杂的查询时候。我们需要知道EF是否按照我们所需执行。这时,就需要查看EF生成的语句。

  1. 执行IQueryable<T>.ToString(); 即可查看生成的sql语句。
  2. 通过EFProviderWrappers,这里不做赘述。

 

DataAnnotation

  DataAnnotation 特性由.NET 3.5中引进,给.NET中的类提供了一种添加验证的方式。同时在EF中,也是添加约束与个性化设置一种方式。

常用到以下特性。

  1. KeyAttribute:对应数据库中的主键
  2. RequiredAttribute:对应数据库中字段的数据是否可以为null
  3. MaxLengthAttribute:对应数据库中字符串类型字段的最大长度
  4. MinLengthAttribute:在数据库中无对应,但在代码中字符串最小长度
  5. TimestampAttribute:将列的数据类型指定为行版本
  6. DatabaseGeneratedAttribute:标记指定实体属性是由数据库生成的,并指定生成策略(None数据库不生成值,Identity当插入行时,数据库生成值,Computed当插入或更新行时,数据库生成值)
  7. ColumnAttribute:指定实体属性在数据库中的列名及数据类型
  8. TableAttribute:指定实体类对应的数据表名
  9. ForeignKeyAttribute:指定导航属性的外键字段
  10. NotMappedAttribute:标记指定实体属性在创建数据库中不创建对应字段
  11. ConcurrencyCheck:并发标记

    public class Order
    {
        [Key]
        public int Id { get; set; }
        [StringLength(200)]
        public string Name { get; set; }
        public int UserId { get; set; }
        [ForeignKey("UserId")]
        public User User { get; set; }
        public virtual ICollection<OrderItem> OrderItems { get; set; }
    }
    

  

 

FluentAPI

  使用DataAnnotation(DA)非常方便,但有时我们的POCO类不希望受到EF的直接关联。或者DA不能满足需求。这时,我们可以使用FluentAPI方式。

  1. HasKey - KeyAttribute:配置此实体类型的主键属性
  2. IsRequired - RequiredAttribute:将此属性配置为必需属性。用于存储此属性的数据库列将不可以为null
  3. HasMaxLength - MaxLengthAttribute:将属性配置为具有指定的最大长度
  4. IsConcurrencyToken - ConcurrencyCheckAttribute:将属性配置为用作开放式并发标记
  5. IsRowVersion - TimestampAttribute:将属性配置为数据库中的行版本。实际数据类型将因使用的数据库提供程序而异。将属性设置为行版本会自动将属性配置为开放式并发标记。

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<AppInfo>().Property(o => o.Id).HasColumnName("AppId");
        }
    

  

MVC异步控制器做个CRUD例子

  微软自家的东西关联总是那么方便。

在MVC5中,添加控制器的时候选择使用EF即自动生成这一切。

澳门新萄京官方网站 16

 

 

建议

  1.模型改变重新生成数据库,导致表数据丢失。

    在使用CodeFirst中,当模型改变的时候,采用第一节中的Nuget中EF Migration API方式则不会清空数据。

  2.是否使用存储过程,视图这些数据库技术。

    当使用EF 这种ORM框架的时候,就应该轻数据库技术,重业务逻辑层。我的建议是万不得已不要使用存储过程,视图实际只是存储了SQL语句。

 

MVC5代码下载:MVCTest

本文由澳门新萄京官方网站发布于www.8455.com,转载请注明出处:澳门新萄京官方网站:近期开发项目中用到的编

关键词: