仓酷云

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 969|回复: 8
打印 上一主题 下一主题

[学习教程] MVC架构计划—EF-Code First:数据查询

[复制链接]
莫相离 该用户已被删除
跳转到指定楼层
楼主
发表于 2015-1-16 14:19:16 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
归根到底,Java跨平台可以,但是要重新编写代码,否则还分什么J2EE/J2SE/J2ME呢!1、媒介


EF的CodeFirst是个好器材,让我们完整不必思索数据库端(注重,这里并非说不必要对数据库常识举行懂得),统统事情都能够经由过程代码来完成。EF是ORM,已把数据会见操纵封装得很好了,能够间接在营业层中利用,那我们为何还要对其举行那末多封装呢?在我看来,封装最少能带来以下的优点:
1.把EF的相干工具封装在数据会见层中,排除了营业层对EF的依附。
2.一致EF的数据操纵,以包管营业层利用不异的代码标准。
3.埋没EF的敏感设置,下降EF的利用难度。

这里就引进一个成绩,应当如何来举行EF的封装呢,既要包管利用的一致与便利性,又要坚持EF的灵活性,不然,封装将酿成给营业层设置停滞。上面,次要针对数据查询进对大概呈现的误用情形举行剖析。

2、查扣问题剖析


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

在EF中,面向工具的数据查询次要供应了两种体例:

1.TEntityDbSet<TEntity>.Find(paramsobject[]keyValues):针对主键计划的经由过程主键查找单个实体,会先在EF的当地数据集Local中举行查询,假如没有,再往数据库中查询。

2.IQueryable<T>、IEnumerable<T>范例的一切数据查询的扩大办法(因为DbSet<T>承继于IQueryable<T>与IEnumerable<T>),如SingleOrDefault,FirstOrDefault,Where等。个中IQueryable<T>的扩大办法会先搜集需求,到最初一步再天生响应的SQL语句举行数据查询;而IEnumerable<T>的扩大办法则是在查询的第一步就天生响应的SQL语句猎取数据到内存中,前面的操纵都是之内存中的数据为基本举行操纵的。

以上两种体例为EF的数据查询供应了极年夜的自在度,这个自在度是我们在封装的时分必要坚持的。可是,在浏览很多人(个中不乏事情了几年的)对EF的封装,计划一致的数据操纵接口Repository中关于数据查询的操纵中,一般会犯以下几种掉误:

1.计划了良多GetByName,GetByXX,GetByXXX的操纵,这些操纵一般并非一切实体城市用到,只是部分实体的部分营业用到,大概是“估量会用到”。

2.界说了按前提查询的SingleOrDefault,FirstOrDefault,Count,GetByPredicate(predicate)等办法,可是关于前提predicate的范例是利用Expression<Func<TEntity,boo>>仍是Func<TEntity,bool>很纠结,最初爽性两个都计划,相称于把IQueryable<T>,IEnumerable<T>的办法再过一遍。

3.界说了猎取全体数据的GetAll()办法,但却利用了IEnumerable<TEntity>范例的前往值,分明的同砚都晓得,这相称于把全部表的数据都加载到内存中,成绩很严峻,计划者却不晓得。

诸云云类,各类奇葩的查询操纵层见叠出,这些操纵大概损坏了EF数据查询原本的天真性,大概多此一举。

实在,这么多掉误的缘故原由只要一个,计划者健忘了EF是ORM,把EF看成ado.net来利用了。只需记住EF是ORM,以上这些功效已完成了,就不要往反复完成了。那末以上的成绩就十分好办理了,只需:
在数据操纵Repository接口中把EF的DbSet<TEntity>开放成一个只读的IQueryable<TEntity>范例的属性供应给营业层作为数据查询的数据源

就能够了。这个数据源是只读的,而且范例是IQueryable<T>,就包管了它只能作为数据查询的数据源,而不像开放了DbSet<T>范例那样能够在营业层中挪用EF的外部办法举行增、删、改等操纵。别的IQueryable<T>范例坚持了EF原本的查询自在性与天真性,复杂了然。这个数据集还能够传送到营业层的各个条理,以完成在哪必要数据就在哪查的天真性。

(二)轮回中的查询圈套

EF的导航属性是提早加载的,提早加载的长处就是不必到不加载,一次只加载需要的数据,这削减了每次加载的数据量,但弱点也不言自明:极年夜的增添了数据库毗连的次数,好比以下这么个复杂的需求:
输入每一个用户具有的脚色数目

依据这个需求,很简单就写出了以下的代码:


遍历一切用户信息,输入每一个用户信息中脚色(导航属性)的数目。

下面这段代码逻辑很明晰,看似没有甚么成绩。我们来剖析一下代码的实行历程:
1.132行,从IOC容器中猎取用户仓储接口的实例,这没甚么成绩。
2.133行,掏出一切用户信息(memberRepository.Entities),实行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].[Timestamp]AS[Timestamp],
[Extent2].[Id]AS[Id1]
FROM[dbo].[Members]AS[Extent1]
LEFTOUTERJOIN[dbo].[MemberExtends]AS[Extent2]ON[Extent1].[Id]=[Extent2].[Member_Id]

固然EF天生的SQL有些庞大,但仍是没甚么成绩

3.136行,就入手下手有成绩了,每次轮回城市毗连一次数据库,实行一次以下查询(最初一个1是用户编号):
execsp_executesqlNSELECT
[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].[Timestamp]AS[Timestamp]
FROM[dbo].[RoleMembers]AS[Extent1]
INNERJOIN[dbo].[Roles]AS[Extent2]ON[Extent1].[Role_Id]=[Extent2].[Id]
WHERE[Extent1].[Member_Id]=@EntityKeyValue1,N@EntityKeyValue1int,@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].[Timestamp]AS[Timestamp],
[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].[Timestamp]AS[Timestamp],
[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].[Timestamp]AS[Timestamp1],
CASEWHEN([Join2].[Member_Id]ISNULL)THENCAST(NULLASint)ELSE1ENDAS[C1]
FROM[dbo].[Members]AS[Extent1]
LEFTOUTERJOIN[dbo].[MemberExtends]AS[Extent2]ON[Extent1].[Id]=[Extent2].[Member_Id]
LEFTOUTERJOIN(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].[Timestamp]AS[Timestamp]
FROM[dbo].[RoleMembers]AS[Extent3]
INNERJOIN[dbo].[Roles]AS[Extent4]ON[Extent4].[Id]=[Extent3].[Role_Id])AS[Join2]ON[Extent1].[Id]=[Join2].[Member_Id]
)AS[Project1]
ORDERBY[Project1].[Id]ASC,[Project1].[Id1]ASC,[Project1].[C1]ASC

(三)导航属性的查询圈套

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


能够看到,汇合类的导航属性是一个ICollection<T>范例的汇合,实在现类能够是一般利用List<T>大概HashSet<T>。用了ICollection<T>,就限制了汇合类的导航属性是一个内存汇合,只需用到这个导航属性,就必需把汇合中的一切数据都加载到内存中,才干举行后续操纵。好比下面的例子中,我们的需求只是想晓得用户具有脚色的数目,原意只是要实行一下SQL的Count语句便可,却想不到EF是把这个汇合加载到内存中(下面的语句,是把以后用户的一切脚色信息查询出来),再在内存中举行计数,这有形中是一个很年夜的资本华侈。好比在一个商城体系中,我们想懂得一种商品的销量(product.Orders.Count),那便可能把几万条定单信息都加载到内存中,再举行计数,这将是劫难性的资本损耗。

读到这里,是否是对EF十分扫兴?

3、查询应当怎样计划


下面的成绩,在项目标开辟阶段,基本不是成绩,由于软件还是能跑得起来,并且跑得好好的。可是等网站上线的时分,用户量下去的时分,这些功能杀手就原形毕露了。是成绩,总要想举措办理的。

上面就来讲说我的办理计划,至于计划靠谱不靠谱,读者自行判别。
(一)查询数据集计划

在后面的计划中,实体的数据仓储接口已向下层表露了一个IQueryable<TEntity>的接口了,为何表露这个接口,下面也说了良多了。上面,以账户模块为例,我们就来看看如何把这个查询数据集往上传送。

起首,不要忘了,我们的项目布局是如许的:


1.对注进的Repository接口举行回护
在中心营业完成类(AccountService)中,我们举行了各个相干实体的Repository接口的注进


这里要注重,实体的Repository接口只能在营业层中利用,以避免开辟者在展示层中挪用增、删、改等数据操纵以完成营业,而不是在营业层中举行营业完成。因此,注进的实体的Repository接口属性可会见性要修正为protected。

2.开放查询数据集供展示层利用
营业层中的Repository接口都设置为protected了,那末在展示层没法会见IEntityRepository.Entities数据集了,如何完成展示层的数据的查询呢,很复杂,只需在营业接口中把IEntityRepository.Entities数据集再包装成一个IQueryable<T>的查询数据集开辟进来,就能够了。


3.在营业完成类中举行IEntityRepository.Entities数据集的包装:


经由如许的封装,在营业层中,我们可使用IEntityRepository.Entities数据集举行数据查询,在展示层中利用营业左券中开放的数据集举行查询。因为开辟的数据集还是IQueryable<T>范例,对EF的查询自在度没有消耗。

(二)查询圈套的应对计划

关于后面提到的EF的查询圈套,我提出的办理计划就是
经由过程IQueryable<T>的Select(selector)扩大办法来按需查询。

起首剖析好以后营业中必要甚么数据,要甚么取甚么,最初的数据用匿名工具装载。

好比后面提到的输入用户具有的脚色数目这个需求,完成计划以下:


以上代码实行的查询语句以下:
SELECT
[Extent1].[Id]AS[Id],
(SELECT
COUNT(1)AS[A1]
FROM[dbo].[RoleMembers]AS[Extent2]
WHERE[Extent1].[Id]=[Extent2].[Member_Id])AS[C1]
FROM[dbo].[Members]AS[Extent1]

相称简便,这才是我们必要的效果。

(三)匿名工具计划与实体工具计划对照

匿名工具的计划固然到达了我们想要的效果,但对照实体工具计划,又有甚么分歧呢,上面我们来对照一下:
1.数据传送性、复用性:
-匿名工具:基础上属于一次性数据,没法全体传送,没法复用。
+实体工具:传送性,复用性优秀。

2.对重构、办法提取的撑持:
-匿名工具:因为数据没法传送,写出的代码很难举行重构,我就普写过几百行代码而没法提取子办法重构的办法。
+实体工具:数据对代码重构、办法提取撑持优秀。

3.对缓存射中率的影响:
-匿名工具:数据与详细的营业场景(参数、前提等)亲切联系关系,缓存射中率大概会较低。
+实体工具:数据易复用,缓存射中率大概会较高。

4.分歧条理的数据模子主动映照转换(AutoMapper等)
-匿名工具:属性不定,范例不定,难以转换。
+实体工具:轻松完成映照转换。

5.数据使用率:
+匿名工具:数据按需猎取,使用率高,基础无华侈。
-实体工具:数据都是全体掏出,使用率低,华侈年夜。

6.程序功能影响:
+匿名工具:简单写出运转高效的代码,功能优秀。
-实体工具:简单写出功能低下的代码。

经由过程下面的对照,但愿能对计划的选择供应一些参考,至于怎样弃取,终极选择甚么计划,只能本人依据营业的特性来衡量了,符合用哪一个就用哪一个。

4、需务实现


后面已说过很多次了,这里在明白的提一次,在这个架构计划中,假如现有查询办法不克不及满意营业需求,必要增加一个响应的查询功效,你不必要到数据层往举行操纵,你只必要:

扩大IQueryable<T>,给IQueryable<T>增加一个扩大办法。

(一)按属性称号排序

查询离不开分页查询,分页查询之前一般会先排序,再查出指定页的单页数据,先来讲说按属性排序的成绩吧。

排序可使用IQueryable<T>的OrderBy、OrderByDescending两个扩大办法来举行,比方:
source.OrderBy(m=>m.AddDate).ThenByDescending(m=>m.IsDeleted);

这是体系供应的排序办法,但只撑持Expression<Func<TSource,TKey>>keySelector范例的参数,而我们在点击表格的表头的时分,一般猎取到的是实体的属性称号的字符串,以是我们还必要扩大一个撑持属性称号的排序办法。

起首,界说一个类来封装排序前提,排序前提一般包含属性称号与排序偏向:
namespaceGMF.Component.Tools
{
///<summary>
///属性排序前提信息类
///</summary>
publicclassPropertySortCondition
{
///<summary>
///机关一个指定属性称号的升序排序的排序前提
///</summary>
///<paramname="propertyName">排序属性称号</param>
publicPropertySortCondition(stringpropertyName)
:this(propertyName,ListSortDirection.Ascending){}

///<summary>
///机关一个排序属性称号和排序体例的排序前提
///</summary>
///<paramname="propertyName">排序属性称号</param>
///<paramname="listSortDirection">排序体例</param>
publicPropertySortCondition(stringpropertyName,ListSortDirectionlistSortDirection)
{
PropertyName=propertyName;
ListSortDirection=listSortDirection;
}

///<summary>
///猎取或设置排序属性称号
///</summary>
publicstringPropertyName{get;set;}

///<summary>
///猎取或设置排序偏向
///</summary>
publicListSortDirectionListSortDirection{get;set;}
}
}

其次,我们吸收的是排序前提是属性称号的字符串,实践仍是要挪用体系供应的Expression<Func<TSource,TKey>>keySelector范例参数的排序办法举行排序。以是我们还必要一个把字符串前提转换为排序表达式,并挪用体系的排序办法。
privatestaticclassQueryableHelper<T>
{
//ReSharperdisableStaticFieldInGenericType
privatestaticreadonlyConcurrentDictionary<string,LambdaExpression>Cache=newConcurrentDictionary<string,LambdaExpression>();

internalstaticIOrderedQueryable<T>OrderBy(IQueryable<T>source,stringpropertyName,ListSortDirectionsortDirection)
{
dynamickeySelector=GetLambdaExpression(propertyName);
returnsortDirection==ListSortDirection.Ascending
?Queryable.OrderBy(source,keySelector)
:Queryable.OrderByDescending(source,keySelector);
}

internalstaticIOrderedQueryable<T>ThenBy(IOrderedQueryable<T>source,stringpropertyName,ListSortDirectionsortDirection)
{
dynamickeySelector=GetLambdaExpression(propertyName);
returnsortDirection==ListSortDirection.Ascending
?Queryable.ThenBy(source,keySelector)
:Queryable.ThenByDescending(source,keySelector);
}

privatestaticLambdaExpressionGetLambdaExpression(stringpropertyName)
{
if(Cache.ContainsKey(propertyName))
{
returnCache[propertyName];
}
ParameterExpressionparam=Expression.Parameter(typeof(T));
MemberExpressionbody=Expression.Property(param,propertyName);
LambdaExpressionkeySelector=Expression.Lambda(body,param);
Cache[propertyName]=keySelector;
returnkeySelector;
}
}

到此,有了后面的筹办,属性称号的排序就十分好写了。为了利用便利,应当做成IQueryable<T>的扩大办法:
///<summary>
///把IQueryable[T]汇合按指定属性与排序体例举行排序
///</summary>
///<paramname="source">要排序的数据集</param>
///<paramname="propertyName">排序属性名</param>
///<paramname="sortDirection">排序偏向</param>
///<typeparamname="T">静态范例</typeparam>
///<returns>排序后的数据集</returns>
publicstaticIOrderedQueryable<T>OrderBy<T>(thisIQueryable<T>source,stringpropertyName,
ListSortDirectionsortDirection=ListSortDirection.Ascending)
{
PublicHelper.CheckArgument(propertyName,"propertyName");
returnQueryableHelper<T>.OrderBy(source,propertyName,sortDirection);
}

///<summary>
///把IQueryable[T]汇合按指定属性排序前提举行排序
///</summary>
///<typeparamname="T">静态范例</typeparam>
///<paramname="source">要排序的数据集</param>
///<paramname="sortCondition">列表属性排序前提</param>
///<returns></returns>
publicstaticIOrderedQueryable<T>OrderBy<T>(thisIQueryable<T>source,PropertySortConditionsortCondition)
{
PublicHelper.CheckArgument(sortCondition,"sortCondition");
returnsource.OrderBy(sortCondition.PropertyName,sortCondition.ListSortDirection);
}

///<summary>
///把IOrderedQueryable[T]汇合持续按指定属性排序体例举行排序
///</summary>
///<typeparamname="T">静态范例</typeparam>
///<paramname="source">要排序的数据集</param>
///<paramname="propertyName">排序属性名</param>
///<paramname="sortDirection">排序偏向</param>
///<returns></returns>
publicstaticIOrderedQueryable<T>ThenBy<T>(thisIOrderedQueryable<T>source,stringpropertyName,
ListSortDirectionsortDirection=ListSortDirection.Ascending)
{
PublicHelper.CheckArgument(propertyName,"propertyName");
returnQueryableHelper<T>.ThenBy(source,propertyName,sortDirection);
}

///<summary>
///把IOrderedQueryable[T]汇合持续指定属性排序体例举行排序
///</summary>
///<typeparamname="T">静态范例</typeparam>
///<paramname="source">要排序的数据集</param>
///<paramname="sortCondition">列表属性排序前提</param>
///<returns></returns>
publicstaticIOrderedQueryable<T>ThenBy<T>(thisIOrderedQueryable<T>source,PropertySortConditionsortCondition)
{
PublicHelper.CheckArgument(sortCondition,"sortCondition");
returnsource.ThenBy(sortCondition.PropertyName,sortCondition.ListSortDirection);
}

这里利用了ListSortDirection来暗示排序偏向,固然,你也能够界说ThenByDescending扩大办法来举行反序排序。下面的排序能够写成以下所示:
source.OrderBy("AddDate").ThenBy("IsDeleted",ListSortDirection.Descending);

(二)分页查询

上面来讲说分页查询,一般分页查询的计划办法是在仓储操纵Repository中界说特定的办法来猎取分页的数据,如今我们面临的是IQueryable<T>数据集,就不必那末贫苦了。只需界说一个公用于分页查询的扩大办法便可。代码以下:
///<summary>
///把IOrderedQueryable[T]汇合持续指定属性排序体例举行排序
///</summary>
///<typeparamname="T">静态范例</typeparam>
///<paramname="source">要排序的数据集</param>
///<paramname="sortCondition">列表属性排序前提</param>
///<returns></returns>
publicstaticIOrderedQueryable<T>ThenBy<T>(thisIOrderedQueryable<T>source,PropertySortConditionsortCondition)
{
PublicHelper.CheckArgument(sortCondition,"sortCondition");
returnsource.ThenBy(sortCondition.PropertyName,sortCondition.ListSortDirection);
}

///<summary>
///从指定IQueryable[T]汇合中查询指定分页前提的子数据集
///</summary>
///<typeparamname="T">静态范例</typeparam>
///<paramname="source">要查询的数据集</param>
///<paramname="predicate">查询前提谓语表达式</param>
///<paramname="pageIndex">分页索引</param>
///<paramname="pageSize">分页巨细</param>
///<paramname="total">输入切合前提的总纪录数</param>
///<paramname="sortConditions">排序前提汇合</param>
///<returns></returns>
publicstaticIQueryable<T>Where<T>(thisIQueryable<T>source,Expression<Func<T,bool>>predicate,intpageIndex,intpageSize,
outinttotal,PropertySortCondition[]sortConditions=null)whereT:Entity
{
PublicHelper.CheckArgument(source,"source");
PublicHelper.CheckArgument(predicate,"predicate");
PublicHelper.CheckArgument(pageIndex,"pageIndex");
PublicHelper.CheckArgument(pageSize,"pageSize");

total=source.Count(predicate);
if(sortConditions==null||sortConditions.Length==0)
{
source=source.OrderBy(m=>m.AddDate);
}
else
{
intcount=0;
IOrderedQueryable<T>orderSource=null;
foreach(PropertySortConditionsortConditioninsortConditions)
{
orderSource=count==0
?source.OrderBy(sortCondition.PropertyName,sortCondition.ListSortDirection)
:orderSource.ThenBy(sortCondition.PropertyName,sortCondition.ListSortDirection);
count++;
}
source=orderSource;
}
returnsource!=null
?source.Where(predicate).Skip((pageIndex-1)*pageSize).Take(pageSize)
:Enumerable.Empty<T>().AsQueryable();
}

如许,要猎取某页数据,只需挪用这个扩大办法便可,跟挪用体系的扩大办法一样便利(个中total是总纪录数)。
inttotal;
varpageData=source.Where(m=>m.IsDeleted,4,20,outtotal);

(三)查询实战

上面,我们来实战一下数据查询。

起首,我们要查询的数据将用上面这个类来显现,个中LoginLogCount为以后用户的登录次数,RoleNames为用户具有的脚色称号汇合,这两个数据都来历于与Member有联系关系的其他表。
namespaceGMF.Demo.Site.Models
{
publicclassMemberView
{
publicintId{get;set;}

publicstringUserName{get;set;}

publicstringNickName{get;set;}

publicstringEmail{get;set;}

publicboolIsDeleted{get;set;}

publicDateTimeAddDate{get;set;}

publicintLoginLogCount{get;set;}

publicIEnumerable<string>RoleNames{get;set;}
}
}

为了简化演示操纵,引进分页控件MVCPager来处置页面上的分页条的处置。

Controller中代码以下,注重数据猎取的查询代码:
namespaceGMF.Demo.Site.Web.Controllers
{
[Export]
publicclassHomeController:Controller
{
[Import]
publicIAccountSiteContractAccountContract{get;set;}

publicActionResultIndex(int?id)
{
intpageIndex=id??1;
constintpageSize=20;
PropertySortCondition[]sortConditions=new[]{newPropertySortCondition("Id")};
inttotal;
varmemberViews=AccountContract.Members.Where(m=>true,pageIndex,pageSize,outtotal,sortConditions).Select(m=>newMemberView
{
UserName=m.UserName,
NickName=m.NickName,
Email=m.Email,
IsDeleted=m.IsDeleted,
AddDate=m.AddDate,
LoginLogCount=m.LoginLogs.Count,
RoleNames=m.Roles.Select(n=>n.Name)
});
PagedList<MemberView>model=newPagedList<MemberView>(memberViews,pageIndex,pageSize,total);
returnView(model);
}
}
}

这里固然利用了MVCPager,但并没有利用她的分页功效。分页处置仍是我们本人做的,只是利用了她的单页数据模子类PageList<T>作为视图模子

View代码以下:
@usingWebdiyer.WebControls.Mvc;
@usingGMF.Component.Tools;
@modelPagedList<GMF.Demo.Site.Models.MemberView>
@{
ViewBag.Title="Index";
Layout="~/Views/Shared/_Layout.cshtml";
}

<h2>Index</h2>
@if(!User.Identity.IsAuthenticated)
{
@Html.ActionLink("登录","Login","Account")
}
else
{
<div>
用户@User.Identity.Name已登录
@Html.ActionLink("加入","Logout","Account")
</div>
}
<table>
<tr>
<th>UserName</th>
<th>NickName</th>
<th>Email</th>
<th>IsDeleted</th>
<th>AddDate</th>
<th>LoginLogCount</th>
<th>RoleNames</th>
</tr>

@foreach(variteminModel){
<tr>
<td>@Html.DisplayFor(modelItem=>item.UserName)</td>
<td>@Html.DisplayFor(modelItem=>item.NickName)</td>
<td>@Html.DisplayFor(modelItem=>item.Email)</td>
<td>@Html.DisplayFor(modelItem=>item.IsDeleted)</td>
<td>@Html.DisplayFor(modelItem=>item.AddDate)</td>
<tdstyle="text-align:center;">
@Html.DisplayFor(modelItem=>item.LoginLogCount)
</td>
<td>@item.RoleNames.ExpandAndToString(",")</td>
</tr>
}
</table>
@Html.Pager(Model,newPagerOptions
{
PageIndexParameterName="id"
})

显现效果以下:


查询实行的SQL语句以下:
SELECT
[Project2].[Id]AS[Id],
[Project2].[UserName]AS[UserName],
[Project2].[NickName]AS[NickName],
[Project2].[Email]AS[Email],
[Project2].[IsDeleted]AS[IsDeleted],
[Project2].[AddDate]AS[AddDate],
[Project2].[C2]AS[C1],
[Project2].[C1]AS[C2],
[Project2].[Name]AS[Name]
FROM(SELECT
[Limit1].[Id]AS[Id],
[Limit1].[UserName]AS[UserName],
[Limit1].[NickName]AS[NickName],
[Limit1].[Email]AS[Email],
[Limit1].[IsDeleted]AS[IsDeleted],
[Limit1].[AddDate]AS[AddDate],
[Join1].[Name]AS[Name],
CASEWHEN([Join1].[Member_Id]ISNULL)THENCAST(NULLASint)ELSE1ENDAS[C1],
[Limit1].[C1]AS[C2]
FROM(SELECTTOP(20)[Project1].[Id]AS[Id],[Project1].[UserName]AS[UserName],[Project1].[NickName]AS[NickName],[Project1].[Email]AS[Email],[Project1].[IsDeleted]AS[IsDeleted],[Project1].[AddDate]AS[AddDate],[Project1].[C1]AS[C1]
FROM(SELECT[Project1].[Id]AS[Id],[Project1].[UserName]AS[UserName],[Project1].[NickName]AS[NickName],[Project1].[Email]AS[Email],[Project1].[IsDeleted]AS[IsDeleted],[Project1].[AddDate]AS[AddDate],[Project1].[C1]AS[C1],row_number()OVER(ORDERBY[Project1].[Id]ASC)AS[row_number]
FROM(SELECT
[Extent1].[Id]AS[Id],
[Extent1].[UserName]AS[UserName],
[Extent1].[NickName]AS[NickName],
[Extent1].[Email]AS[Email],
[Extent1].[IsDeleted]AS[IsDeleted],
[Extent1].[AddDate]AS[AddDate],
(SELECT
COUNT(1)AS[A1]
FROM[dbo].[LoginLogs]AS[Extent2]
WHERE[Extent1].[Id]=[Extent2].[Member_Id])AS[C1]
FROM[dbo].[Members]AS[Extent1]
)AS[Project1]
)AS[Project1]
WHERE[Project1].[row_number]>0
ORDERBY[Project1].[Id]ASC)AS[Limit1]
LEFTOUTERJOIN(SELECT[Extent3].[Member_Id]AS[Member_Id],[Extent4].[Name]AS[Name]
FROM[dbo].[RoleMembers]AS[Extent3]
INNERJOIN[dbo].[Roles]AS[Extent4]ON[Extent4].[Id]=[Extent3].[Role_Id])AS[Join1]ON[Limit1].[Id]=[Join1].[Member_Id]
)AS[Project2]
ORDERBY[Project2].[Id]ASC,[Project2].[C1]ASC

实行的SQL语句固然对照庞大,可是的确是按我们的需求来举行最简查询的,好比我们没有查询Member的Password属性,下面就没有Password相干的语句,LoginLog的计数,Roles的Name属性的选择,也没有触及该类的其他属性的查询。我也不知道,我原来理解的,NET就是C++编程,只是与JAVA相对,呵呵。以为.ET就是高级C++编程。
小妖女 该用户已被删除
沙发
发表于 2015-1-18 12:51:39 | 只看该作者
ASP.NET可以无缝地与WYSIWYGHTML编辑器和其他编程工具(包括MicrosoftVisualStudio.NET)一起工作。这不仅使得Web开发更加方便,而且还能提供这些工具必须提供的所有优点,包括开发人员可以用来将服务器控件拖放到Web页的GUI和完全集成的调试支持。微软为ASP.net设计了这样一些策略:易于写出结构清晰的代码、代码易于重用和共享、可用编译类语言编写等等,目的是让程序员更容易开发出Web应用,满足计算向Web转移的战略需要。
小魔女 该用户已被删除
板凳
发表于 2015-1-25 21:59:36 | 只看该作者
在一个项目中谁敢保证每天几千万甚至几亿条的数据不丢失?谁敢保证应用的高可靠性?有可以借签的项目吗?
深爱那片海 该用户已被删除
地板
发表于 2015-2-4 08:45:07 | 只看该作者
CGI程序在运行的时候,首先是客户向服务器上的CGI程序发送一个请求,服务器接收到客户的请求后,就会打开一个新的Process(进程)来执行CGI程序,处理客户的请求。CGI程序最后将执行的结果(HTML页面代码)传回给客户。
谁可相欹 该用户已被删除
5#
发表于 2015-2-9 20:32:17 | 只看该作者
弱类型造成潜在的出错可能:尽管弱数据类型的编程语言使用起来回方便一些,但相对于它所造成的出错几率是远远得不偿失的。
金色的骷髅 该用户已被删除
6#
发表于 2015-2-27 21:28:06 | 只看该作者
ASP.Net摆脱了以前ASP使用脚本语言来编程的缺点,理论上可以使用任何编程语言包括C++,VB,JS等等,当然,最合适的编程语言还是MS为.NetFrmaework专门推出的C(读csharp)。
冷月葬花魂 该用户已被删除
7#
发表于 2015-3-9 14:43:13 | 只看该作者
HTML:当然这是网页最基本的语言,每一个服务器语言都需要它的支持,要学习,这个肯定是开始,不说了.
透明 该用户已被删除
8#
发表于 2015-3-17 00:10:07 | 只看该作者
使用普通的文本编辑器编写,如记事本就可以完成。由脚本在服务器上而不是客户端运行,ASP所使用的脚本语言都在服务端上运行,用户端的浏览器不需要提供任何别的支持,这样大提高了用户与服务器之间的交互的速度。
变相怪杰 该用户已被删除
9#
发表于 2015-3-23 09:48:54 | 只看该作者
我觉得什么语言,精通就好,你要做的就是比其他80%的人都厉害,你就能得到只有20%的人才能得到的高薪。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|仓酷云 鄂ICP备14007578号-2

GMT+8, 2024-5-19 15:31

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表