• MVC4学习要点记三


    一、数据迁移
    用来解决code first情况下当增加、删除、改变实体类,或改变DbContext类后,相应地更新数据库结构而不会对现有数据产生影响。

    1、启用迁移
    指令:
    enable-migrations
    add-migration XXX

    enable-migrations指令:
    a.在项目根目录下创建了一个Migrations文件夹
    b.在Migrations文件夹下新建一个Configuration.cs文件。
    可以通过修改Configuration.cs来对Migration做一些配置(如加入一些测试数据等)

    类似初始化数据的AccountInitilizer.cs类,数据迁移的Configuration类也包含一个Seed方法。
    当数据库新建或数据库结构更新后,这个方法会被调用,利用这个方法可以插入或更新test data。

    2、配置Seed方法
    使用drop and re-create的方式时,因为每次model改变时数据库都会被删除,所有数据都会丢失,所以需要使用DALàAccountInitilizer.cs的Seed方法来插入测试数据。
    使用Code First Migrations方式,当数据库改变时测试数据会保留,所以包含test data的Seed方法一般来说是不需要的。
    如果我们要部署数据库到生产环境,事实上这种情况下我们也不想Seed方法来插入测试数据到生产环境中。
    但是,也有特殊情况, 比如在我们部署时获得了实际的初始化数据,如实际存在的组织部门这些初始化的信息,这时候就用到Seed方法。

    与AccountInitilizer.cs类中Seed方法不同的是,以前都是用Add方法,这次用了AddOrUpdate方法来插入数据。如:
    sysUsers.ForEach(s => context.SysUsers.AddOrUpdate(p => p.UserName, s));

    3、执行迁移
    指令:update-database

    前面执行 add-migration时,同样在Migrations文件夹里面,产生一个<timestamp>_InitialCreate.cs的文件。
    里面两个方法,Up和Down:
    Up方法创建数据库表,Down方法删除表。

    update-database指令调用了Up方法来新建database的表(和data model entity set对应), 然后调用Seed方法来填充测试数据。


    数据迁移执行步骤:
    1、enable-migrations(如已启用,则略过此步)
    2、add-migration XXX
    3、update-database



    二、数据注解属性(DataAnnotations)
    对某一些类型来说不需要使用Required, 例如DateTime, int,double,float,因为这些值类型不能被赋予空值,因此他们天生就具有Required的特性。

    Column - 列名称
    Required - not null
    StringLength(20) - nvarchar(20)
    TypeName - 改变SQL数据类型,如把double改成decimal

    三、加载关联数据
    EF有三种方式可以加载关联数据到一个实体的navigation属性中
    Lazy loading(懒加载)
    Eager loading(热加载)
    Explicit loading(显式加载)

    1、Lazy loading(懒加载)
    第一次读取entity的时候不会加载。
    当需要读取navigation property的时候,相关的数据将会被自动读取。
    这种情况会导致多次查询数据库。
    这种加载方式是,默认不加载导航属性关联的数据,但是如果用到了,就加载。
    例如:
    using (var context = new TestEntities())
    {
        IQueryable<Team> teams = from t in context.Teams select t;
        foreach (Team t in teams)
        {
            Console.WriteLine(t.Players.Count());
        }
        Console.Read();
    }
    再如:

     
    关闭懒加载方法:
    (1)context.ContextOptions.LazyLoadingEnabled = false;
    (2)EDMX文件属性LazyLoadingEnabled设置为False;
    (3)对特定的navigation properties来说,省略property的virtual关键字就可以了(待确定);
    导航属性加virtual修饰,为的是Entity Framework Code First的延迟加载功能(懒加载)。当然Entity Framework Code First延迟加载并不是必须的,所以virtual修饰符也可以不加。
    优势和劣势:
    当打开Lazy Loading时,我们可以不用去在意某实体是否已经加载,不会出现在调用某一实体时,出现null的尴尬,省去程序员不少心力,但同时劣势也非常明显,如果我们有大量实体,且频繁去调用相关实体,程序就会频繁地访问数据库,这很显然地会影响程序的性能。


    2、Eager loading(贪婪加载)
    当读取entity的时候,相关数据会被一起读取。
    一般来说这种方式会产生一个join query来获取所有需要的数据。
    通过Include方法来指定eager loading.
    例如:
    using (var context = new TestEntities())
    {
        var players = from p in context.Players.Include("Team").Include("PlayerDetails") where (p.PlayerDetails.Any(d => d.Height > 200)) select p;
        foreach (var v in players)
        {
            string output = string.Format("Team: {0} Player: {1}", v.Team.TeamName, v.PlayerName);
             Console.WriteLine(output);
        }
        Console.Read();
    }
    再如:

     

    3、Explicit loading(显式加载)
    和lazy loading类似,除了需要在代码中明确指定需要获取的关联数据。
    在读取navigation property时explicit loading 不会自动发生,你需要手动加载相关数据。
    通过获取object state manager entry for entity,调用Collection.Load method for collections或者Reference.Load method for properties that hold a single entity.
    一般来说,只有在关闭lazying loading的时候才会使用explicit loading.
    例如:
    using (TestEntities context = new TestEntities())
    {
         IQueryable<Player> players = from p in context.Players select p;
         foreach (Player p in players)
         {
             if (p.Age > 30)
             {
                 p.TeamReference.Load();
                 Console.WriteLine(p.PlayerName + " -> " + p.Team.TeamName);
             }                   
         }
         Console.Read();
    }
    再如:

     
    总结:
    1、lazy loading 和 explicit loading都不立即获取property values,它们也被称作deferred loading.
    2、无论使用Lazy Loading还是将Load方法放入foreach循环语句(Explicit loading),都会导致程序频繁访问数据库,导致程序性能下降。我们可以选择性地加载需要的实体。

    自己总结:
    1、lazy loading 和 explicit loading都会有频繁访问数据库的性能问题;所以用Eager loading(贪婪加载)比较保险点;
    2、Include(Eager loading)生成其实是用的left join进行表关联,并且select所有关联的表字段(包括ID)。
    下面是两个生成的SQL示例:
    示例1:
    var user = from a in db.sysUsers.Include("SysUserRoles").Include("SysDepartment")
                           where a.ID == id
                           select a;
    生成的SQL:
    SELECT
    [Project1].[ID] AS [ID],
    [Project1].[UserName] AS [UserName],
    [Project1].[Email] AS [Email],
    [Project1].[PassWord] AS [PassWord],
    [Project1].[CreateDate] AS [CreateDate],
    [Project1].[SysDepartmentID] AS [SysDepartmentID],

    [Project1].[ID1] AS [ID1],
    [Project1].[DepartmentName] AS [DepartmentName],
    [Project1].[DepartmentDesc] AS [DepartmentDesc],

    [Project1].[C1] AS [C1],
    [Project1].[ID2] AS [ID2],
    [Project1].[SysUserID] AS [SysUserID],
    [Project1].[SysRoleID] AS [SysRoleID]

    FROM ( SELECT
        [Limit1].[ID] AS [ID],
        [Limit1].[UserName] AS [UserName],
        [Limit1].[Email] AS [Email],
        [Limit1].[PassWord] AS [PassWord],
        [Limit1].[CreateDate] AS [CreateDate],
        [Limit1].[SysDepartmentID] AS [SysDepartmentID],
        [Extent2].[ID] AS [ID1],
        [Extent2].[DepartmentName] AS [DepartmentName],
        [Extent2].[DepartmentDesc] AS [DepartmentDesc],
        [Extent3].[ID] AS [ID2],
        [Extent3].[SysUserID] AS [SysUserID],
        [Extent3].[SysRoleID] AS [SysRoleID],
        CASE WHEN ([Extent3].[ID] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1]
        FROM    (SELECT TOP (1) [Extent1].[ID] AS [ID], [Extent1].[UserName] AS [UserName], [Extent1].[Email] AS [Email], [Extent1].[PassWord] AS [PassWord], [Extent1].[CreateDate] AS [CreateDate], [Extent1].[SysDepartmentID] AS [SysDepartmentID]
            FROM [dbo].[SysUser] AS [Extent1]
            WHERE [Extent1].[ID] = @p__linq__0 ) AS [Limit1]
        LEFT OUTER JOIN [dbo].[SysDepartment] AS [Extent2] ON [Limit1].[SysDepartmentID] = [Extent2].[ID]
        LEFT OUTER JOIN [dbo].[SysUserRole] AS [Extent3] ON [Limit1].[ID] = [Extent3].[SysUserID]
    )  AS [Project1]
    ORDER BY [Project1].[ID] ASC, [Project1].[ID1] ASC, [Project1].[C1] ASC

    示例2:分页
    var users = from u in db.sysUsers.Include("SysDepartment") select u;
    return View(users.ToPagedList(pageNum,pageSize));

    SELECT TOP (3)
    [Join1].[ID1] AS [ID],
    [Join1].[UserName] AS [UserName],
    [Join1].[Email] AS [Email],
    [Join1].[PassWord] AS [PassWord],
    [Join1].[CreateDate] AS [CreateDate],
    [Join1].[SysDepartmentID] AS [SysDepartmentID],

    [Join1].[ID2] AS [ID1],
    [Join1].[DepartmentName] AS [DepartmentName],
    [Join1].[DepartmentDesc] AS [DepartmentDesc]

    FROM ( SELECT [Extent1].[ID] AS [ID1], [Extent1].[UserName] AS [UserName], [Extent1].[Email] AS [Email], [Extent1].[PassWord] AS [PassWord], [Extent1].[CreateDate] AS [CreateDate], [Extent1].[SysDepartmentID] AS [SysDepartmentID], [Extent2].[ID] AS [ID2], [Extent2].[DepartmentName] AS [DepartmentName], [Extent2].[DepartmentDesc] AS [DepartmentDesc], row_number() OVER (ORDER BY [Extent1].[UserName] ASC) AS [row_number]
        FROM  [dbo].[SysUser] AS [Extent1]
        LEFT OUTER JOIN [dbo].[SysDepartment] AS [Extent2] ON [Extent1].[SysDepartmentID] = [Extent2].[ID]
    )  AS [Join1]
    WHERE [Join1].[row_number] > 0
    ORDER BY [Join1].[UserName] ASC

    参考:
    http://www.cnblogs.com/kevin2013/p/5239186.html
    http://www.cnblogs.com/itjeff/p/4137733.html

  • 相关阅读:
    《c程序设计语言》读书笔记--大写转小写
    《c程序设计语言》读书笔记--字符串比较
    《c程序设计语言》读书笔记--反转字符串
    spring接收json字符串的两种方式
    logback的使用
    初识Vim
    Chrome控制台
    构造有层次的大纲
    让chrome浏览器快的不要不要的
    排序算法Java版
  • 原文地址:https://www.cnblogs.com/zhaow/p/9754007.html
Copyright © 2020-2023  润新知