ASP.NET MVC4+BootStrap 实战(三)

简介:


上节我们剩余Compare和Fix按钮没有讲,本节我们就来讲一下Compare按钮的功能,年底了,要开年会了,一个个积极的跟邱少云似得,给员工涨工资的时候能不能也积极点。不说了,说多了都是泪。


还记得我之前写的大数据实战之环境搭建,我们今天主要是拿DB的数据和solr的数据作比较,得出比较结果。首先,先启动centOS,启动tomcat,这样我们才能利用SolrNet调用solr公开的API。关于solr的启动再次我就不再赘述。

wKioL1SHALHicWSQAAOZAIQSvxk119.jpg

OK,我们将solr实例启动起来后,用firefox浏览管理界面,没问题。目前solr中并没有任何数据。

接下来我们就要看点击Compare按钮了,点击Compare按钮,Compare按钮在哪里呢,在Partial页。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@ using  Bruce.GRLC.Model.ViewModel;
@model UserInfoViewModel
<div  class = "col-md-6 form-group" >
     <label  class = "control-label" >
         The Same:
     </label>
     < select  id= "selsamelist"  class = "form-control"  multiple= "multiple"  style= "max-height:500px;min-height:400px" >
         @ if  (Model!= null &&Model.SameWithSolrEntityList !=  null  && Model.SameWithSolrEntityList.Count > 0)
         {
             foreach  ( var  entity  in  Model.SameWithSolrEntityList)
             {
                 <option value= "@entity.UserNo" >@entity.Name</option>
             }
         }
     </ select >
     <input id= "btncompare"  type= "button"  class = "btn btn-info"  value= "Compare"  style= "width: 80px;margin-top:5px"  />
</div>
<div  class = "col-md-6 form-group" >
     <label  class = "control-label" >
         The Difference:
     </label>
     < select  id= "seldifflist"  class = "form-control"  multiple= "multiple"  style= "max-height: 500px;min-height: 400px" >
         @ if  (Model.DifferenceWithSolrEntityList !=  null  && Model.DifferenceWithSolrEntityList.Count > 0)
         {
             foreach  ( var  entity  in  Model.DifferenceWithSolrEntityList)
             {
                 <option value= "@entity.UserNo" >@entity.Name</option>
             }
         }
     </ select >
     <input type= "button"  class = "btn btn-info"  value= "Fix"  style= "width:80px;margin-top:5px"  />
</div>

OK,我们看到了按钮在这里,我们设置按钮的样式为btn btn-info,这都是bootStrap提供的样式,我们直接拿来用就ok了。这个页面其实就是循环相同数据的List和不同数据的List,加载到一个可多选的下拉列表。大家注意到multiple="multiple",可以多选。另外"form-control"也是bootStrap提供的样式,用来装饰表单元素的样式。上面的代码没有什么,我们主要是看控制器一级一级往下是怎么处理的,我们先看Compare按钮的click事件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$( "#btncompare" ).click( function  () {
         $( "#selsamelist" ).empty();
         $( "#seldifflist" ).empty();
 
         $.ajax({
             url:  "/Home/GetCompareResult?pam="  new  Date().toTimeString(),
             type:  "POST" ,
             datatype:  "Html" ,
             beforeSend:  function  () {
                 $( "#divcompare" ).show();
             },
             complete:  function  () {
                 $( "#divcompare" ).hide();
             },
             success:  function  (data) {
                 $( "#divcompareresult" ).html(data);
             },
             error:  function  () {
                 alert( "比较失败!" );
             }
         });

首先我们先清除两个多选下拉列表的数据,因为我们这里并不是返回整个Partial页面来替换,所以我们必须先把两个多选下拉数据清除掉。OK,我们看一下控制器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public  PartialViewResult GetCompareResult()
         {
             GRLCBiz instance = GRLCBiz.GetInstance();
             instance.CompareDBAndSolr();
             UserInfoViewModel userInfoViewModel =  new  UserInfoViewModel();
             userInfoViewModel.DifferenceWithSolrEntityList = instance.differenceUserEntityList;
             List< string > differenceUserIDList = userInfoViewModel.DifferenceWithSolrEntityList.Select(d => d.UserNo.Trim()).ToList();
 
             userInfoViewModel.SameWithSolrEntityList = instance.userEntityList.Where(u => !differenceUserIDList.Contains(u.UserID.Trim()))
                 .Select((userDB, userSolr) =>
                 {
                     return  new  UserSolrEntity()
                     {
                         UserNo = userDB.UserID.Trim(),
                         Name = userDB.UserName ==  null  string .Empty : userDB.UserName.Trim()
                     };
                 }).ToList();
 
             return  PartialView( "~/Views/Partial/DiffAndSameWithSolrPartial.cshtml" , userInfoViewModel);
         }

其实很简单,首先我们调用CompareDBAndSolr方法,这个方法是获取到比较结果的关键。那么比较的话,我们怎么比较呢,大家有经验的同学一定想到多线程。不错,就是多线程,可是有人要说了,多线程的话,你怎么知道线程都执行完了,因为只有多个线程都执行完了,我们才能拿到最终的比较结果,显示到页面上。说到这里,如果你是一个.net4.0以前的用户,你可能会想到AutoResetEvent。

说到这个AutoResetEvent,它可以有一个初始的状态,如果是true,则表明是一个终止状态,反之则是非终止状态,那么看一下我们的程序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public  void  CompareDBAndSolr()
         {
             if  (userEntityList ==  null )
             {
                 userEntityList =  this .GetAllDBUserList();
             }
 
             if  (userEntityList ==  null  || userEntityList.Count == 0)  return ;
 
             int  threadCount = 0;
             int  totalCount = userEntityList.Count;
             threadCount = totalCount % ConstValues.CONN_ComparePerThread == 0 ? totalCount / ConstValues.CONN_ComparePerThread : totalCount / ConstValues.CONN_ComparePerThread + 1;
 
             if  (threadCount > ConstValues.CONN_CompareThreadCount)
             {
                 threadCount = ConstValues.CONN_CompareThreadCount;
             }
 
             differenceUserEntityList =  new  List<UserSolrEntity>();
             autoResetEvents =  new  AutoResetEvent[threadCount];
             for  ( int  i = 0; i < threadCount; i++)
             {
                 autoResetEvents[i] =  new  AutoResetEvent( false );
                 ThreadPool.QueueUserWorkItem( new  WaitCallback(CompareUserInfoByThread), i);
                 Thread.Sleep(ConstValues.CONN_ThreadCreateInterval);
             }
 
             WaitHandle.WaitAll( this .autoResetEvents);
         }

在这个方法中我们先拿到数据库的所有用户的数据

1
2
3
4
5
6
7
8
SELECT 
       A.UseNo,
       ISNULL (B. Name , '' AS  Name ,
       ISNULL (B.Age,0)  AS  Age,
       ISNULL (B.Temper, '' AS  Married
     FROM  Bonus.dbo.[ User ] A  WITH (NOLOCK)
      INNER  JOIN  Bonus.dbo.UerInfo B  WITH (NOLOCK)
         ON  A.UseNo = B.UseNo

然后计算应该使用的线程数。CONN_ComparePerThread配置的是每个线程比较的数据量,所以我们先拿总数据对其进行求余,得到总线程数,然后再判断如果总线程数大于配置的线程数CONN_CompareThreadCount,则取配置的线程数。因为机器的资源有限,不可能开启成百上千个线程,那样CPU资源占用很大,机器肯能会卡死。所以设置最大线程数是必须的。当我们拿到线程数以后,我们实例化一个AutoResetEvent数组,来管理这些线程之间的通信。接下来循环创建AutoResetEvent,设置其初始状态为非终止。然后将线程要执行的方法加入线程池工作队列,并传递线程编号i作为方法参数。最后这句WaitHandle.WaitAll(this.autoResetEvents);意思是等待所有的线程任务结束(状态标记为终止状态)。

我们接下来看CompareUserInfoByThread方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
private  void  CompareUserInfoByThread( object  userState)
         {
             int  threadIndex = ( int )userState;
 
             try
             {
                 UserDBEntity[] copyUserDBEntityList =  null ;
                 while  ( this .movePosition <  this .userEntityList.Count)
                 {
                     lock  ( this .userEntityList)
                     {
                         if  ( this .movePosition >=  this .userEntityList.Count)
                         {
                             break ;
                         }
 
                         if  ( this .movePosition <=  this .userEntityList.Count - ConstValues.CONN_ComparePerThread)
                         {
                             copyUserDBEntityList =  new  UserDBEntity[ConstValues.CONN_ComparePerThread];
                             userEntityList.CopyTo( this .movePosition, copyUserDBEntityList, 0, ConstValues.CONN_ComparePerThread);
                         }
                         else
                         {    //最后几个,count<CONN_ComparePerThread
                             copyUserDBEntityList =  new  UserDBEntity[userEntityList.Count -  this .movePosition];
                             userEntityList.CopyTo( this .movePosition, copyUserDBEntityList, 0, copyUserDBEntityList.Length);
                         }
                         this .movePosition += ConstValues.CONN_ComparePerThread;
                     }
                     this .CompareUserInfoStart(copyUserDBEntityList, threadIndex);
                 }
 
             }
             catch  (Exception ex)
             {
                 LogHelper.WriteExceptionLog(MethodBase.GetCurrentMethod(), ex);
             }
             finally
             {
                 this .autoResetEvents[threadIndex].Set();
             }
         }

这个方法其实就是多线程瓜分数据了,每个线程Copy出一批(CONN_ComparePerThread)DB的数据去和solr作对比,每个线程拿到自己的那批数据后,将计数器movePosition增加CONN_ComparePerThread。直到这些线程将这些数据瓜分完,大家注意到在finally语句块,我们对每个AutoResetEvent对象调用set方法,意思是告诉WaitHandle,我已经执行完了,即终止状态。这样当WaitHanlde收到每个线程执行完毕的信号后,结束等待,否则就会一直等待下去,这就是为什么Set方法的调用一定要放到finally块的原因。OK,继续看下一个方法CompareUserInfoStart

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
private  void  CompareUserInfoStart(UserDBEntity[] userDBEntityList,  int  threadIndex)
         {
             List< string > userIDList = userDBEntityList.Select(u => u.UserID.Trim()).ToList();
             StringBuilder solrFilter =  new  StringBuilder();
 
             foreach  ( var  userID  in  userIDList)
             {
                 solrFilter.Append( "UserNo:" );
                 solrFilter.Append(userID);
                 solrFilter.Append( " OR " );
             }
 
             solrFilter.Length = solrFilter.Length - 4;
 
             List<UserSolrEntity> userSolrEntityList = SolrHelper.GetInstance().QueryByFilter<UserSolrEntity>(solrFilter.ToString());
 
             List<UserSolrEntity> userDBConvertSolrEntityList = userDBEntityList.Select((userDB, userSolr) =>
             {
                 return  new  UserSolrEntity()
                 {
                     UserNo = userDB.UserID.Trim(),
                     Age = userDB.Age,
                     Name = userDB.UserName.Trim(),
                     IsMarried = userDB.Married ==  "1"
                 };
             }).ToList();
 
             lock  (_lockObj)
             {
                 differenceUserEntityList.AddRange(userDBConvertSolrEntityList.Except(userSolrEntityList,  new  UserSolrEntityCompare()));
             }
         }
     }

我们拿到对比的DB数据实体List之后,得到userID,然后拼成solr的查询条件solrFilter,然后调用SolrHelper中的QueryByFilter<T>方法去查询出一个Solr的实体List,然后我们将DB的实体List通过Linq转化为Solr的实体List userDBConvertSolrEntityList。然后通过Except方法找出不同的实体List,放置到differenceUserEntityList。在这里注意IEqualityCompare接口的实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public  class  UserSolrEntityCompare : IEqualityComparer<UserSolrEntity>
     {
         public  bool  Equals(UserSolrEntity original, UserSolrEntity destination)
         {
             original.Name = original.Name ??  string .Empty;
             original.UserNo = original.UserNo ??  string .Empty;
             destination.Name = destination.Name ??  string .Empty;
             destination.UserNo = destination.UserNo ??  string .Empty;
 
             return  original.UserNo.Trim().Equals(destination.UserNo.Trim())
                 && original.Age == destination.Age
                 && original.Name.Trim().Equals(destination.Name.Trim())
                 && original.IsMarried == destination.IsMarried;
         }
 
         public  int  GetHashCode(UserSolrEntity userSolrEntity)
         {
             return  userSolrEntity.UserNo.GetHashCode();
         }
     }

OK,到这里就全部结束了,我们在action中拿到了比较出的结果,然后组成viewModel,返回给Partial页面去绑定。看一下效果Comparing,please wait......

wKioL1SHHJnQXm6jAAJ3pAkk4Bk322.jpg


下面是比较出的结果,因为solr中没有数据,所以都是不相同的,因为取DB数据是INNER JOIN,所以只有四条数据。

wKiom1SHH1SRXi9hAACHHsf5c7k419.jpg

OK,本节到此结束,下节我们看一下Fix功能的实现。



本文转自 BruceAndLee 51CTO博客,原文链接:http://blog.51cto.com/leelei/1588090,如需转载请自行联系原作者

相关文章
|
9天前
|
消息中间件 开发框架 .NET
.NET 8 强大功能 IHostedService 与 BackgroundService 实战
【11月更文挑战第7天】本文介绍了 ASP.NET Core 中的 `IHostedService` 和 `BackgroundService` 接口及其用途。`IHostedService` 定义了 `StartAsync` 和 `StopAsync` 方法,用于在应用启动和停止时执行异步操作,适用于资源初始化和清理等任务。`BackgroundService` 是 `IHostedService` 的抽象实现,简化了后台任务的编写,通过 `ExecuteAsync` 方法实现长时间运行的任务逻辑。文章还提供了创建和注册这两个服务的实战步骤,帮助开发者在实际项目中应用这些功能。
|
1月前
|
开发框架 NoSQL MongoDB
C#/.NET/.NET Core开发实战教程集合
C#/.NET/.NET Core开发实战教程集合
|
2月前
|
开发框架 前端开发 JavaScript
ASP.NET MVC 教程
ASP.NET 是一个使用 HTML、CSS、JavaScript 和服务器脚本创建网页和网站的开发框架。
41 7
|
2月前
|
SQL 关系型数据库 数据库
七天.NET 8操作SQLite入门到实战详细教程(选型、开发、发布、部署)
七天.NET 8操作SQLite入门到实战详细教程(选型、开发、发布、部署)
|
3月前
|
测试技术 API 开发者
.NET单元测试框架大比拼:MSTest、xUnit与NUnit的实战较量与选择指南
【8月更文挑战第28天】单元测试是软件开发中不可或缺的一环,它能够确保代码的质量和稳定性。在.NET生态系统中,MSTest、xUnit和NUnit是最为流行的单元测试框架。本文将对这三种测试框架进行全面解析,并通过示例代码展示它们的基本用法和特点。
287 8
|
3月前
|
开发框架 缓存 前端开发
实战.NET Framework 迁移到 .NET 5/6
从.NET Framework 迁移到.NET 5/6 是一次重要的技术革新,涵盖开发环境与应用架构的全面升级。本文通过具体案例详细解析迁移流程,包括评估现有应用、利用.NET Portability Analyzer 工具识别可移植代码、创建新项目、逐步迁移代码及处理依赖项更新等关键步骤。特别关注命名空间调整、JSON 序列化工具更换及数据库访问层重构等内容,旨在帮助开发者掌握最佳实践,确保迁移过程平稳高效,同时提升应用性能与可维护性。
124 2
|
2月前
|
存储 开发框架 前端开发
ASP.NET MVC 迅速集成 SignalR
ASP.NET MVC 迅速集成 SignalR
58 0
|
3月前
|
API 开发者 Java
API 版本控制不再难!Spring 框架带你玩转多样化的版本管理策略,轻松应对升级挑战!
【8月更文挑战第31天】在开发RESTful服务时,为解决向后兼容性问题,常需进行API版本控制。本文以Spring框架为例,探讨四种版本控制策略:URL版本控制、请求头版本控制、查询参数版本控制及媒体类型版本控制,并提供示例代码。此外,还介绍了通过自定义注解与过滤器实现更灵活的版本控制方案,帮助开发者根据项目需求选择最适合的方法,确保API演化的管理和客户端使用的稳定与兼容。
167 0
|
3月前
|
监控 Cloud Native 开发者
云端精英的.NET微服务秘籍:Azure上的创新实战演练
【8月更文挑战第28天】在现代软件开发中,微服务架构通过分解应用程序提升可维护性和扩展性。结合Azure与.NET框架,开发者能轻松打造高效且易管理的云原生微服务。首先,使用Docker容器化.NET应用,并借助Azure Kubernetes Service(AKS)或Azure Container Instances(ACI)部署。为确保高可用性和伸缩性,可利用Azure Traffic Manager负载均衡及Azure Autoscale动态调整实例数。
28 0
|
3月前
|
Kubernetes Linux 开发者
【实战秘籍】从零开始:用.NET与Docker打造现代化容器化应用之旅
【8月更文挑战第28天】本文详细介绍如何使用 .NET 框架构建并部署 Docker 容器化应用程序,涵盖环境搭建、项目创建、Dockerfile 编写等关键步骤。首先安装必要软件,如 Visual Studio 2022 及 Docker Desktop。接着创建 .NET Core 控制台应用,并在项目根目录编写 Dockerfile 文件。使用 .NET 运行时基础镜像,复制二进制文件,指定入口点。运行命令构建镜像并测试容器。为实现通信,映射端口。最后,标签化镜像并推送到 Docker Hub,为生产环境部署做好准备。掌握这些步骤,即可轻松应对从小型项目到大规模应用的各种需求。
161 0