莫相离 发表于 2015-1-16 14:19:16

MVC架构计划—EF-Code First:数据查询

归根到底,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
.AS,
.AS,
.AS,
.AS,
.AS,
.AS,
.AS,
.AS,
.AS
FROM.AS
LEFTOUTERJOIN.ASON.=.

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

3.136行,就入手下手有成绩了,每次轮回城市毗连一次数据库,实行一次以下查询(最初一个1是用户编号):
execsp_executesqlNSELECT
.AS,
.AS,
.AS,
.AS,
.AS,
.AS,
.AS
FROM.AS
INNERJOIN.ASON.=.
WHERE.=@EntityKeyValue1,N@EntityKeyValue1int,@EntityKeyValue1=1

试想,假如有100个用户,就要毗连100次数据库,这么一个复杂的需求,毗连了101次数据库,还不得让数据库疯失落了。

固然,有同砚能够要说,这里用了提早加载才会多了良多毗连数据库的次数,你能够当即加载啊,把Role脚色一次性加载出去。好吧,我们来看看当即加载:


143行,在取一切用户信息的时分利用Include办法把与用户联系关系的一切脚色信息也一并查询出来了,如许在轮回遍历的时分就不会再毗连数据库往查询脚色信息了。可是假如看到实行的SQL语句,估量你想逝世的心境都有了。实行的查询以下:
SELECT
.AS,
.AS,
.AS,
.AS,
.AS,
.AS,
.AS,
.AS,
.AS,
.AS,
.AS,
.AS,
.AS,
.AS,
.AS,
.AS,
.AS
FROM(SELECT
.AS,
.AS,
.AS,
.AS,
.AS,
.AS,
.AS,
.AS,
.AS,
.AS,
.AS,
.AS,
.AS,
.AS,
.AS,
.AS,
CASEWHEN(.ISNULL)THENCAST(NULLASint)ELSE1ENDAS
FROM.AS
LEFTOUTERJOIN.ASON.=.
LEFTOUTERJOIN(SELECT.AS,.AS,.AS,.AS,.AS,.AS,.AS,.AS
FROM.AS
INNERJOIN.ASON.=.)ASON.=.
)AS
ORDERBY.ASC,.ASC,.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
.AS,
(SELECT
COUNT(1)AS
FROM.AS
WHERE.=.)AS
FROM.AS

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

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

匿名工具的计划固然到达了我们想要的效果,但对照实体工具计划,又有甚么分歧呢,上面我们来对照一下:
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;
}
ParameterExpressionparam=Expression.Parameter(typeof(T));
MemberExpressionbody=Expression.Property(param,propertyName);
LambdaExpressionkeySelector=Expression.Lambda(body,param);
Cache=keySelector;
returnkeySelector;
}
}

到此,有了后面的筹办,属性称号的排序就十分好写了。为了利用便利,应当做成IQueryable<T>的扩大办法:
///<summary>
///把IQueryable汇合按指定属性与排序体例举行排序
///</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汇合按指定属性排序前提举行排序
///</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汇合持续按指定属性排序体例举行排序
///</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汇合持续指定属性排序体例举行排序
///</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汇合持续指定属性排序体例举行排序
///</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汇合中查询指定分页前提的子数据集
///</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
{

publicclassHomeController:Controller
{

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
.AS,
.AS,
.AS,
.AS,
.AS,
.AS,
.AS,
.AS,
.AS
FROM(SELECT
.AS,
.AS,
.AS,
.AS,
.AS,
.AS,
.AS,
CASEWHEN(.ISNULL)THENCAST(NULLASint)ELSE1ENDAS,
.AS
FROM(SELECTTOP(20).AS,.AS,.AS,.AS,.AS,.AS,.AS
FROM(SELECT.AS,.AS,.AS,.AS,.AS,.AS,.AS,row_number()OVER(ORDERBY.ASC)AS
FROM(SELECT
.AS,
.AS,
.AS,
.AS,
.AS,
.AS,
(SELECT
COUNT(1)AS
FROM.AS
WHERE.=.)AS
FROM.AS
)AS
)AS
WHERE.>0
ORDERBY.ASC)AS
LEFTOUTERJOIN(SELECT.AS,.AS
FROM.AS
INNERJOIN.ASON.=.)ASON.=.
)AS
ORDERBY.ASC,.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页面代码)传回给客户。

谁可相欹 发表于 2015-2-9 20:32:17

弱类型造成潜在的出错可能:尽管弱数据类型的编程语言使用起来回方便一些,但相对于它所造成的出错几率是远远得不偿失的。

金色的骷髅 发表于 2015-2-27 21:28:06

ASP.Net摆脱了以前ASP使用脚本语言来编程的缺点,理论上可以使用任何编程语言包括C++,VB,JS等等,当然,最合适的编程语言还是MS为.NetFrmaework专门推出的C(读csharp)。

冷月葬花魂 发表于 2015-3-9 14:43:13

HTML:当然这是网页最基本的语言,每一个服务器语言都需要它的支持,要学习,这个肯定是开始,不说了.

透明 发表于 2015-3-17 00:10:07

使用普通的文本编辑器编写,如记事本就可以完成。由脚本在服务器上而不是客户端运行,ASP所使用的脚本语言都在服务端上运行,用户端的浏览器不需要提供任何别的支持,这样大提高了用户与服务器之间的交互的速度。

变相怪杰 发表于 2015-3-23 09:48:54

我觉得什么语言,精通就好,你要做的就是比其他80%的人都厉害,你就能得到只有20%的人才能得到的高薪。
页: [1]
查看完整版本: MVC架构计划—EF-Code First:数据查询