ASP.NET MVC4+BootStrap 实战(三)

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


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

wKioL1SHALHicWSQAAOZAIQSvxk119.jpg

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

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

@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事件。c#

$("#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,咱们看一下控制器。数组

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。tomcat

说到这个AutoResetEvent,它能够有一个初始的状态,若是是true,则代表是一个终止状态,反之则是非终止状态,那么看一下咱们的程序。多线程

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);
        }

在这个方法中咱们先拿到数据库的全部用户的数据ide

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方法。

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

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接口的实现。

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功能的实现。

相关文章
相关标签/搜索