MVC4+EFcodefirst中如何处理数据库历史记录的保存和查询

MVC4+EFcodefirst中如何处理数据库历史记录的保存和查询,第1张

二、查询问题分析

(一) 数据查询应该在哪做

在EF中,面向对象的数据查询主要提供了两种方式:

TEntity DbSet<TEntity>Find(params object[] keyValues):针对主键设计的通过主键查找单个实体,会先在EF的本地数据集Local中进行查询,如果没有,再去数据库中查询。

IQueryable<T>、IEnumerable<T>类型的所有数据查询的扩展方法(由于DbSet<T>继承于IQueryable<T>与IEnumerable<T>),如SingleOrDefault,FirstOrDefault,Where等。其中IQueryable<T>的扩展方法会先收集需求,到最后一步再生成相应的SQL语句进行数据查询;而IEnumerable<T>的扩展方法则是在查询的第一步就生成相应的SQL语句获取数据到内存中,后面的 *** 作都是以内存中的数据为基础进行 *** 作的。

以上两种方式为EF的数据查询提供了极大的自由度,这个自由度是我们在封装的时候需要保持的。但是,在阅读不少人(其中不乏工作了几年的)对EF的封装,设计统一的数据 *** 作接口Repository中关于数据查询的 *** 作中,通常会犯如下几种失误:

设计了很多GetByName,GetByXX,GetByXXX的 *** 作,这些 *** 作通常并不是所有实体都会用到,只是部分实体的部分业务用到,或者是“估计会用到”。

定义了按条件查询的SingleOrDefault,FirstOrDefault,Count,GetByPredicate(predicate)等方法,但是对于条件predicate的类型是使用Expression<Func<TEntity, boo>>还是Func<TEntity, bool>很纠结,最后干脆两个都设计,相当于把IQueryable<T>,IEnumerable<T>的方法再过一遍。

定义了获取全部数据的GetAll()方法,但却使用了IEnumerable<TEntity>类型的返回值,明白的同学都知道,这相当于把整个表的数据都加载到内存中,问题很严重,设计者却不知道。

诸如此类,各种奇葩的查询 *** 作层出不穷,这些 *** 作或者破坏了EF数据查询原有的灵活性,或者画蛇添足。

其实,这么多失误的原因只有一个,设计者忘记了EF是ORM,把EF当作adonet来使用了。只要记着EF是ORM,以上这些功能已经实现了,就不要去重复实现了。那么以上的问题就非常好解决了,只要:

在数据 *** 作Repository接口中把EF的DbSet<TEntity>开放成一个只读的IQueryable<TEntity>类型的属性提供给业务层作为数据查询的数据源

就可以了。这个数据源是只读的,并且类型是IQueryable<T>,就保证了它只能作为数据查询的数据源,而不像开放了DbSet<T>类型那样可以在业务层中调用EF的内部方法进行增、删、改等 *** 作。另外IQueryable<T>类型保持了EF原有的查询自由性与灵活性,简单明了。这个数据集还可以传递到业务层的各个层次,以实现在哪需要数据就在哪查的灵活性。

(二) 循环中的查询陷阱

EF的导航属性是延迟加载的,延迟加载的优点就是不用到不加载,一次只加载必要的数据,这减少了每次加载的数据量,但缺点也不言自明:极大的增加了数据库连接的次数,比如如下这么个简单的需求:

输出每个用户拥有的角色数量

根据这个需求,很容易就写出了如下的代码:

遍历所有用户信息,输出每个用户信息中角色(导航属性)的数量。

上面这段代码逻辑很清晰,看似没有什么问题。我们来分析一下代码的执行过程:

132行,从IOC容器中获取用户仓储接口的实例,这没什么问题。

133行,取出所有用户信息(memberRepositoryEntities),执行SQL如下:

SELECT [Extent1][Id] AS [Id], [Extent1][UserName] AS [UserName], [Extent1][Password] AS [Password], [Extent1][NickName] AS [NickName], [Extent1][Email] AS [Email], [Extent1][IsDeleted] AS [IsDeleted], [Extent1][AddDate] AS [AddDate], [Extent1]1682974547 AS 1682974547, [Extent2][Id] AS [Id1] FROM [dbo][Members] AS [Extent1] LEFT OUTER JOIN [dbo][MemberExtends] AS [Extent2] ON [Extent1][Id] = [Extent2][Member_Id]

虽然EF生成的SQL有些复杂,但还是没什么问题

3 136行,就开始有问题了,每次循环都会连接一次数据库,执行一次如下查询(最后一个1是用户编号):

exec sp_executesql N'SELECT [Extent2][Id] AS [Id], [Extent2][Name] AS [Name], [Extent2][Description] AS [Description], [Extent2][RoleTypeNum] AS [RoleTypeNum], [Extent2][IsDeleted] AS [IsDeleted], [Extent2][AddDate] AS [AddDate], [Extent2]1682974547 AS 1682974547 FROM [dbo][RoleMembers] AS [Extent1] INNER JOIN [dbo][Roles] AS [Extent2] ON [Extent1][Role_Id] = [Extent2][Id] WHERE [Extent1][Member_Id] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=1

试想,如果有100个用户,就要连接100次数据库,这么一个简单的需求,连接了101次数据库,还不得让数据库疯掉了。

当然,有同学可以要说,这里用了延迟加载才会多了很多连接数据库的次数,你可以立即加载啊,把Role角色一次性加载进来。好吧,我们来看看立即加载:

143行,在取所有用户信息的时候使用Include方法把与用户关联的所有角色信息也一并查询出来了,这样在循环遍历的时候就不会再连接数据库去查询角色信息了。但是如果看到执行的SQL语句,估计你想死的心情都有了。执行的查询如下:

SELECT [Project1][Id] AS [Id], [Project1][UserName] AS [UserName], [Project1][Password] AS [Password], [Project1][NickName] AS [NickName], [Project1][Email] AS [Email], [Project1][IsDeleted] AS [IsDeleted], [Project1][AddDate] AS [AddDate], [Project1]1682974547 AS 1682974547, [Project1][Id1] AS [Id1], [Project1][C1] AS [C1], [Project1][Id2] AS [Id2], [Project1][Name] AS [Name], [Project1][Description] AS [Description], [Project1][RoleTypeNum] AS [RoleTypeNum], [Project1][IsDeleted1] AS [IsDeleted1], [Project1][AddDate1] AS [AddDate1], [Project1][Timestamp1] AS [Timestamp1] FROM ( SELECT [Extent1][Id] AS [Id], [Extent1][UserName] AS [UserName], [Extent1][Password] AS [Password], [Extent1][NickName] AS [NickName], [Extent1][Email] AS [Email], [Extent1][IsDeleted] AS [IsDeleted], [Extent1][AddDate] AS [AddDate], [Extent1]1682974547 AS 1682974547, [Extent2][Id] AS [Id1], [Join2][Id] AS [Id2], [Join2][Name] AS [Name], [Join2][Description] AS [Description], [Join2][RoleTypeNum] AS [RoleTypeNum], [Join2][IsDeleted] AS [IsDeleted1], [Join2][AddDate] AS [AddDate1], [Join2]1682974547 AS [Timestamp1], CASE WHEN ([Join2][Member_Id] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1] FROM [dbo][Members] AS [Extent1] LEFT OUTER JOIN [dbo][MemberExtends] AS [Extent2] ON [Extent1][Id] = [Extent2][Member_Id] LEFT OUTER JOIN (SELECT [Extent3][Member_Id] AS [Member_Id], [Extent4][Id] AS [Id], [Extent4][Name] AS [Name], [Extent4][Description] AS [Description], [Extent4][RoleTypeNum] AS [RoleTypeNum], [Extent4][IsDeleted] AS [IsDeleted], [Extent4][AddDate] AS [AddDate], [Extent4]1682974547 AS 1682974547 FROM [dbo][RoleMembers] AS [Extent3] INNER JOIN [dbo][Roles] AS [Extent4] ON [Extent4][Id] = [Extent3][Role_Id] ) AS [Join2] ON [Extent1][Id] = [Join2][Member_Id] ) AS [Project1] ORDER BY [Project1][Id] ASC, [Project1][Id1] ASC, [Project1][C1] ASC

(三) 导航属性的查询陷阱

我们再来回顾一下导航属性的长相(以用户信息中的角色信息为例):

可以看到,集合类的导航属性是一个ICollection<T>类型的集合,其实现类可以是通常使用List<T>或者HashSet<T>。用了ICollection<T>,就限定了集合类的导航属性是一个内存集合,只要用到这个导航属性,就必须把集合中的所有数据都加载到内存中,才能进行后续 *** 作。比如上面的例子中,我们的需求只是想知道用户拥有角色的数量,原意只是要执行一下SQL的Count语句即可,却想不到EF是把这个集合加载到内存中(上面的语句,是把当前用户的所有角色信息查询出来),再在内存中进行计数,这无形中是一个很大的资源浪费。比如在一个商城系统中,我们想了解一种商品的销量(productOrdersCount),那就可能把几万条订单信息都加载到内存中,再进行计数,这将是灾难性的资源消耗。

1查询生命周期

在进入正题时候,我们先来了解EF Core查询的生命周期。

11LINQ查询会由Entity Framework Core处理并生成给数据库提供程序可处理的表示形式(说白了就是生成给数据库可识别数据形式)。

发送的查询结果(查询表示形式)会被缓存,以便每次执行查询时无需进行11中处理。

12查询结果(查询表示形式)会传递到数据库提供程序

数据库提供程序会识别出查询的哪些部分可以在数据库中求值。

查询的这些部分会转换为特定数据库的查询语言(例如,关系数据库的T-SQL)。

一个或多个查询会发送到数据库并返回结果集(返回的是数据库中的值,而不是实体实例中的)。

13对于结果集中的每一项

131如果这是跟踪查询(后续会讲到),EF会检查数据是否表示已在上下文实例的更改跟踪器中的实体中。

如果是,则会返回现有实体。

如果不是,则会创建新实体、设置更改跟踪并返回该新实体。

132如果这是非跟踪查询(后续会讲到),EF会检查数据是否表示已在此查询的结果集中的实体中。

如果是,则会返回现有实体。非跟踪查询使用弱引用跟踪已返回的实体。如果具有相同标识的上一个结果超出范围,并运行垃圾回收,则可能会获得新的实体实例。

如果不是,则会创建新实体并返回该新实体。

14执行查询时

当调用LINQ运算符时,只会生成查询的内存中表示形式。当我们使用查询结果(查询表示形式)时才会发送到数据库。导致查询发送到数据库的最常见 *** 作如下:

在for循环中循环访问结果:

var blogs = from b in _contextBlog

select new

{

bBlogId,

bUrl

};

//触发数据库查询

foreach(var blog in blogs)

{

var id = blogBlogId;

}

当我们执行完LINQ运算符的时候,从SQL Server Profiler监控里面可以看到,并没有执行的SQL语句,也就是说查询结果blogs并没有立即发送给数据库获取返回数据结果集。

而当我们调试进去for循环时候,SQL Server Profiler监控里面可以看到出现了执行SQL语句。也就是说这时候查询结果blogs才执行发送给数据库返回结果集。

使用ToList、ToArray、Single、Count等运算符

_contextBlogToList();

_contextBlogToArray();

_contextBlogCount();

_contextBlogSingle();

_contextBlogFirst();

执行这种形式运算符也会立即发送到数据库获取结果集的。具体执行过程呈现,这里大伙自行测试吧。

将查询结果数据绑定到UI

2跟踪查询与非跟踪查询

在1小节生命周期里面我们有提及过跟踪与非跟踪查询,现在我们来了解下这两种查询区别。

21跟踪查询

返回实体类型的查询是默认会被跟踪的,这表示如果这些实体实例有更改行为,会通过SaveChanges()持久化将更改的值更新到数据库中,但是如果更改的值跟实体实例的值相同,则不会持久化提交数据到数据库,这就是跟踪查询。在以下示例中,将检测到对博客链接所做的更改,并在 SaveChanges() 期间将这些更改持久化到数据库中。

//返回blog实体类型的查询是默认会被跟踪

var blog = _contextBlogSingleOrDefault(b => bBlogId == 1);

//检测对博客链接所做的更改

blogUrl = "1";

//持久化保存到数据库中

_contextSaveChanges();

实体初始链接值是1,当我们点击Save按钮保存的时候,检测到对博客链接所做的更改值还是1的时候,并不会提交更改值到数据库中的。看看下图SQL Server Profiler监控就知道,并没有监控到对应有更新的T-SQL语句,也就是说并没有执行更新 *** 作:

当我们再把链接值更改为2点击保存时候,EF Core检测到博客链接值已经从1更改为2,就会持久化保存到数据库中。

blogUrl = "2";

废话少说,直接上图:

22非跟踪查询

如果不需要更新从数据库中检索到的实体,则应使用非跟踪查询。可以将单个查询替换为非跟踪查询。

var blogs = contextBlogs

//不用跟踪查询

AsNoTracking()

ToList();

//或者在上下文实例级别更改默认跟踪行为

contextChangeTrackerQueryTrackingBehavior = QueryTrackingBehaviorNoTracking;

var blogs = contextBlogsToList();

还是一样老谭秘方事例,当你加上非跟踪查询标识后,无论怎么更改博客链接值,都不会持久化保存数据到数据库中的。

var blogs = _contextBlog

//不用跟踪查询

AsNoTracking()

SingleOrDefault(m => mBlogId == 1);

blogsUrl = "2";

_contextSaveChanges();

直接上图跟踪结果:

在这相信大家从该小节跟踪与非跟踪查询中事例描述中总算对1小节查询生命周期有一定理解吧。

23跟踪和自定义投影

即使查询的结果类型不是实体类型,默认情况下EF Core也会跟踪结果中包含的实体类型。在以下返回匿名类型的查询中,结果集中的Blog实例会被跟踪。

var blog = contextBlogs

Select(b =>

new

{

Blog = b,

PostCount = bPostsCount()

});

如果结果集包含来自LINQ组合的实体类型,EF Core将跟踪它们。

var blog = contextBlogs

Select(b =>

new

{

Blog = b,

Post = bPostsOrderBy(p => pRating)LastOrDefault()

});

如果结果集不包含任何实体类型,则不会执行跟踪。在以下查询中,我们返回匿名类型(具有实体中的某些值,但没有实际实体类型的实例)。查询中没有任何被跟踪的实体。

var blog = contextBlogs

Select(b =>

new

{

Id = bBlogId,

Url = bUrl

});

EF Core支持执行顶级投影中的客户端评估。如果EF Core具体化实体实例以进行客户端评估,则会跟踪该实体实例。此处,由于我们要将blog实体传递到客户端方法StandardizeURL,因此EF Core也会跟踪博客实例。

var blogs = contextBlogs

OrderByDescending(blog => blogRating)

Select(blog => new

{

Id = blogBlogId,

Url = StandardizeUrl(blog)

})

ToList();

public static string StandardizeUrl(Blog blog)

{

var url = blogUrlToLower();

if (!urlStartsWith(">

PPT自动跳转到下一页怎么回事

PPT自动跳转到下一页是由于幻灯片放映方式设置为自动播放,可以通过改变设置,取消自动换片,方法如下:

1、点击“幻灯片放映”;

2、点击“设置放映方式”;

3、在换片方式中选择手动。

Web自动跳转到下一页

给你一个方向   具体代码 有不会的 再问我

如果你用的不是EF框架  可以采用以下方法:

用于分页的存储过程  参数有表名   页数  和 每页多少条   当然有更多的条件 可以继续传进去 此存储过程返回的是DataSet

在你的程序里执行此存储过程 将收到的数据  构造成  代码的 字符串  在后台等待调用

前端页面 使用ajax像后台请求数据 请求的参数 就是存储过程需要的参数  你把你后台构造好的代码 再传给前端ajax  用js直接更新页面即可

幻灯片不能播放某一页的内容且自动跳转到下一页是怎么回事

有没有给ppt设置自定义放映?如果有的话,也许没播放的那一页不在你的放映范围里。

请问,想要设置ppt自动播放,但是不自动跳转到下一页,怎么 *** 作?

打开插入的动画,在右侧动画窗格把“开始”设置成“之前”即可。如果要该动画在进入该幻灯片就启动,在动画窗格将该动画效果放在所有动画效果的最上面。并且注意在动画窗格调整好所有动画启动的顺序。

WPS 文字插入连续分节符之后,自动跳转到下一页怎么办

1、如果是页码连续。那是因为在本节“设置页码格式”中采用了“续前节”模式,取消“续前节”在“起始页码”中输入想要的起始数字即可;

2、如果是“页眉/页脚”连续,那是因为有了“链接到前一条页眉”的设置,在word “设计”选项卡中,取消“链接到前一条页眉”的设置即可。

ppt自动跳转到原网页是怎么回事

是不是上有超链接,选中——右击删除超链接。

Axure的低保真图怎么跳转到下一页

axure强大的是交互事件,可以通过事件来实现页面的跳转,更多的案例参考axure夜话案例教程网91axure

掌阅阅读为什么点击下一页自动跳转到评论

如果阅读到最后一页再翻页就是直接跳转到评论页面了,这种方式的评论能直接给书籍评分啊,也是提醒读者写书评吧……都看完书不写评论的话,网站也是蛮无奈的。随便评论的更无奈……书评没想好怎么写的话,评个分也行,以后想修改,找到自己的发言可以修改。

Flash AS3自动跳转到下一帧

你这个结构还不太清楚, 有可能是其他元件为Null导致的

试下移除那个enterFrame监听

function overHandler(e:Event) {

if (etargetcurrentFrame==etargettotalFrames) {

YP01removeEventListener(EventENTER_FRAME,overHandler);

etargetparentgotoAndS(11);

}

}

你可以建一个包含3张表实体的类,例如

public class ModelList

{

public List<A> a{get;set;}

public List<B> b{get;set;}

public List<C> c{get;set;}

}

查询的时候你就可以这样

ModelList ml=new ModelList();

mla=A_BllGet();

mlb=B_BllGet();

mlc=C_BllGet();

然后视图@model ModelList这样就可以了

RIPSeeker使用具有负二项分布概率的双态HMM(two-state HMM with negative binomial emission probability)从RIP-Seq比对中推断和区分RIP峰值。虽然RIPSeeker是专门为RIP-seq数据分析量身定做的,但它也提供了一套集成在这个独立软件包中的生物信息学工具,全面解决了从比对后处理到可视化和注释的各种问题。此外,还提供了一种基于规则的方法,作为一个名为 [rulebaseRIPSeek](>

<view>父组件msg的值:{{msg}}</view>

<Header msg="{{msg}}" bindchildChange="change" ></Header>

<block wx:for="{{list}}" wx:key="index">

<ListItem rItem="{{item}}" bindchildGO="childGO"></ListItem>

</block>

<Header  msg="{{msg}}"></Header>

/ pages/list/listwxss /

item{

    padding: 5px;

}

img1{

    width: 120px;

    height: 120px;

    border-radius: 5px;

}

row{

    flex: 1;

    height: 120px;

}

title{

    padding: 10px;

}

dec{

    padding:0 10px;

}

// pages/list/listjs

Page({

    /

      页面的初始数据

     /

    data: {

        msg:"你是我的小宝贝",

     list:[{

         url:">

以上就是关于MVC4+EFcodefirst中如何处理数据库历史记录的保存和查询全部的内容,包括:MVC4+EFcodefirst中如何处理数据库历史记录的保存和查询、efcore检测数据是不是创建、PPT自动跳转到下一页怎么回事等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/web/9805871.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-05-02
下一篇2023-05-02

发表评论

登录后才能评论

评论列表(0条)

    保存