ASP .NET MVC ORM 数据库优先

来源:互联网 发布:淘宝代销发货退货地址 编辑:程序博客网 时间:2024/06/07 15:26

1.概述

Code First指“代码优先”,我们只需要编写代码,来自动态创建模型和数据库。
    模型和数据库通过c#类来定义,而非使用基于XML的edmx文件。采用Database First生成代码,

我们需要使用EF设计器辅助工作,而使用Code First可以直接通过使用POCO类
    (Plain Old CLR Object,简单传统的CLR对象)来定义模型,无须使用工具


    使用Code First 是生成数据库的一种方式,大量的开发者正在使用这种方式定义模型,但是这种

方式并不适合所有情况。
    这取决于开发者喜欢如何创建c#类,以及在类与数据库之间的映射。如果使用代码创建,则Code 

First 较为合适;如果喜欢用VisualStudio的EF设计器,那么DatabaseFirst或
    ModalFirst较为合适,特别是在数据库已经存在,且不允许被改动的情况下


注意
    Code First开发模式打破了服务器程序开发时“如果数据库没有准备就绪,不要轻举妄动”的基本规则。
    Code First允许开发人员重点关注业务领域并根据类来为该领域建模。
    Code First模式鼓励在.NET环境中应用“领域驱动设计”原则。业务领域有相互关联的实体构成,这些

实体通过属性对外公开自己的数据,通过方法和事件对外公开自己的行为。
    更重要的是,每个实体都可能处于某一状态,并且与一组动态的验证规则想绑定



使用类创建模型

    模型创建步骤如下
    1.创建ASP.Net MVC应用程序
    2.在Models文件夹上右键选择新建类,带virtual的属性也称为“导航属性”。
        virtual表示启用EF框架的延迟加载功能。延迟加载意味着,尝试访问这些属性的内容时,将自动从数据库加载
    3.创建数据库上下文类,用于表示数据库的一个绘画,以便我们查询和保存数据
    定义一个派生自System.Data.Entity.DbContext的上下文对象,并为模型中的每个类公开一个类型化DbSet<TEntity>。

同样是在Models文件夹右击创建DbContext类
    创建DbContext派生类,需要相关名称控件的引用:using System.Data.Entity


    生成数据库
    1.打开Web.config,查找数据库链接字符串“DefaultConnection”,将其修改为
    <connectionStrings>
     <add name="MeixinContext" connectionString="Data Source=.;Initial Catalog=MeiXC;Uid=sa;Pwd=123;" providerName="System.Data.SqlClient" />
    </connectionStrings>


    connectionString 服务器名 Initial Catalog 新建数据库名字


    2.在控制器的操作方法中,编写数据库访问代码


    3,启用程序,调用代码
    注意我们并未设置主键,但是在数据库中ID被设置成了主键。这是因为EF中,以ID或“实体类名+ID”命名的属性(如UserID),

都会让创建数据库时自动设置成主键,并且以int类型标记
    的主键会被设置成自动增长列。同时EF自动为表创建外键


    模型的默认约定
    EF常见的默认约定分以下几种情况
    (1)关于表和字段名的约定
     Code First约定表名使用EF框架的复数化服务,即使用英语语法的类名复数形式来命名表名(这个功能可以关闭)
    默认情况下,每个表都使用dbo构架创建,实体类属性映射的列与类中字段的名称一致命名
        添加在上下文类
        //解除复数契约
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            //移除复数表名
            modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
        }


    (2)关于主键的约定
    Code First能够意识到类的"ID" 和类的"类名+ID"的意思是键值,然后将这些键映射为数据库的主键,这些键都是非空主键。

Code First默认约定将命名为Id或"类名+Id"的属性视为表的键
    由于这些属性都是整类型,Code First还会将他们配置为数据库的标识字段。这表示在插入数据时,数据库会为这一字段自动生成值


    (3)字符串属性的约定
    实体类的string类型约定为映射到不限长度的非空列中。由于数据库引擎来负责确映射到何种类型
    对于SQl Server而言,默认数据类型为Nvarchar(max),同时还允许存储空值


    (4)一对多关系的约定
    类似一个用户多个订单,
    因此主表有一个list<外键>属性,允许获取特定用户的所有订单信息。
    同时外键表也有一个主表类型的属性,因此可查询到某一订单和某一用户的特定关联
    Code First将这种情况视为一对多关系,约定外键表有一个外键约束


    注意尽管两个类没有外键属性,Code First仍然使用默认规则创建了m_类名_类名的外键,这是Code First所为
    EF框架从数据库中查询或保存时要使用外键


    数据库初始化
    控制数据库初始化
    初始化过程可以清除,并在模型变化时重建数据库。初始化包括两个主要步骤。
    首先,使用Code First在内存中根据默认规则和配置创建模型
    其次,使用已设置的数据库初始器将数据库初始化


    默认情况下,该初始化将使用Code First创建一个数据架构的模型。
    初始化会发生在每一个.NET Framework应用程序的实例上。
    当数据上下文对象被使用时,初始化第一次被引发。初始化是延迟加载的,所以必须执行对模型的操作,如查询或添加实体才会发生完全初始化。


    在开发阶段,我们要确保数据库总是匹配当前的模型,如果Code First检测到数据库和模型两者不匹配,数据库将被删除并重建以满足匹配关系。
    我们可以使用DataBase.SetInitializer方法来实现这一效果。该方法属于手动设置数据库初始化,如下面代码在发现模型改动都自动重建数据库
    方法
    在Models文件夹创建InitDatabase类,该类继承DropCreateDatabaseIfModelChanges<上下文类>并重写Seed方法,以便创建数据库后,自动添加初始化数据。
    最后在控制器操作方法 查询,访问添加的初始化数据


    DropCreateDatabaseIfModelChanges可以在模型修改后重建数据库,
    如我们可以为类手动添加一个外键属性,执行程序后EF将自动更新数据库结构




    主从表查询
    加载关联表的方式
    项目中的表不可能都是单股的表,通常存在一定的主外键关系。
    在多表查询中,主从表查询情况最为常见,查询主表的同时需要访问关联表。
    加载关联表数据就是加载主表数据的同时加载出和主表有外键关系的从表数据


    EF为我们提供了三种方式加载从表数据
    延迟加载  
    只在需要的时候加载数据,当对象是勇士,再去数据库中加载。
    当实体对象读取数据时,关联的数据并不会被获取。
    只有访问关联属性时,被导航属性关联的数据才会被自动读取。
    这可能导致多次查询被发送到数据库,一次是读取实体本身,对于关联的每个实体也需要分别读取。




    贪婪加载
    一次性组织好数据,并加载到内存。当实体加载时,相关联的数据也一同被加载。
    典型运用于一次链接查询后返回所有需要的相关数据的情况、


    
    显示加载
    这中方式类似与延迟加载,除非需要在代码中显示获取数据。在访问导航属性时,不会出现自动加载。
    手动加载关联的数据,通过访问对象状态管理器来获取实体,
    调用Collection.Load方法获取集合,或调用持有单个实体的属性的Reference.Load方法


    一般情况下,如果需要每个实体的关联属性,贪婪加载提供了最好的性能。
    因为只有一次查询被发送到数据库,比对每个实体都要像数据库发出一次查询更加有效


    如果不常访问实体的导航属性,或仅访问一小部分实体的导航属性,延迟加载则更加有效


    通常情况下,在关闭延迟加载时使用显示加载,一个关闭延迟加载的场景是在序列化时,
    因为此时无须所有的导航属性数据加载。如果延迟加载启用,则所有的导航属性都会自动加载,
    因为序列化会访问所有的属性


     注意 
    EF默认支持延迟加载,有两种方法可以关闭延迟加载
    (1)对于特定的导航属性,在定义属性时取消virtual
    (2)对于所有的导航属性,设置LazyLoadingEnabled为false




    延迟加载
    使用延迟加载必须标注导航属性为virtual 


    贪婪加载
    使用Include方法,加载主表的所有的数据并贪婪加载了所有相关联的从表数据
    不同于延迟加载,贪婪加载方式将主表和从表同时加载而非两次加载
    为了比较可以在上下文类的构造函数中显示关闭延迟加载


    贪婪加载方式在关闭延迟加载后依然可以获得正确结果




    显示加载
    使用显示加载访问数据库中所有的信息




    显示加载是先由对象状态管理器来获取实体对象,所以需要使用Entry方法获得实体的状态(EntityState)
    然后在调用Load方法。
    其中配合使用Reference和Collection方法,分别查询单个实例和整个集合的数据



3.代码示例


 public ActionResult Contact()        {            //延迟加载的主从表查询            //using (MeixinContext con = new MeixinContext())            //{            //    通查邮箱查询用户            //    var user = (from u in con.m_users where u.email == "123@123.com" select u).SingleOrDefault();            //    查询该用户订单            //    var order = user.m_order.FirstOrDefault();            //    if (order != null)            //    {            //        ViewBag.p = order.phone;            //        ViewBag.a = order.address;            //    }            //}            //贪婪加载            //string html = "";            //using (MeixinContext con=new MeixinContext())            //{            //    //查询            //    var user = con.m_users.Include("m_order");            //    //查询全部            //    foreach (m_user item in user)            //    {            //        html += "<h2>用户" + item.email + "</h2>";            //        foreach (m_order item2 in item.m_order)            //        {            //            html += "<h3>用户" + item2.phone + "</h2>";            //        }            //    }            //}            //return Content(html);            //显示加载            string html = "";            using (MeixinContext con = new MeixinContext())            {                //查询                var user = (from u in con.m_users where u.email== "123@123.com" select u).SingleOrDefault();                html += "<h2>用户" + user.email + "</h2>";                //显示加载从表                con.Entry(user).Collection("m_order").Load();                //查询                foreach (m_order item in user.m_order)                {                    html += "<h2>用户" + item.address + "</h2>";                                 }            }            return Content(html);        }

0 0
原创粉丝点击