解决 MVC 用户上线下线状态问题
以前工作项目中就有一个微博类功能,其中也出现了用户在线和离线的问题。
但是因为初入程序猿 使用的是 Session _end 上个事件。 Session _end 这个事件不需要怎么解释吧 就是在seesion过期的时候所触发的事件,但有BUG啊! 因为 iis 中由于有进程回收机制,系统繁忙的话Session会丢失。 当然 微软解决又弄了个 进程外seesion 来解决了一下 。额 当时楞是没想起来为啥 ,还是经验少啊。
今天突然看了些 在线状态的问题 突发奇想 想自己不适用Seesion _end 来做一个 在线状态的实现。 百度了半天 还是没啥收获,最后决定 得 自己写吧!
首先想到就是 利用静态对象 得特性 (在程序执行前就创建好对象并且知道程序结束之前不会被释放掉。)前几天从新看了一下进程和线程 进程和线程执行是通过操作系统维护的一个进程表维护着的 调度器 调整执行的。 所以就联想到 在管道事件 Application_Start() 中来维护一个 静态集合对象 通过定时器 实现 session_end 这个事件
Globel.asax.cs 文件中添加一个事件
public class MvcApplication : System.Web.HttpApplication { protected void Application_Start() { AreaRegistration.RegisterAllAreas(); WebApiConfig.Register(GlobalConfiguration.Configuration); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); // 开启一个定时器 并且执行一个方法 StatusMy.GetStatusMy().TestTimer(); } }
StatusMy 对象类 是关键的问题 代码并不多 应该很好理解
using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace OnlineStatus.Models { using System.Data.Entity; using System.Data.Entity.Infrastructure; using System.Data.Entity.Validation; using OnlineStatus.Models.Model; public class StatusMy : System.Web.HttpApplication { #region 单例 private StatusMy() { } private static StatusMy IStatusMy; public static StatusMy GetStatusMy() { return IStatusMy ?? (IStatusMy = new StatusMy()); } #endregion /// <summary> /// 定义定时器 如果是public 很有可能被GC掉 /// </summary> private System.Threading.Timer timerClose; /// <summary> /// 定义个静态集合 /// </summary> private static List<MyUser> listMy; /// <summary> /// 定义一个 Lock 的时候使用的 Object 可以使用this 但是如果当前的这个this 是public 的话会有可能出现异常 保险期间还是使用MSDN 官方 /// </summary> private object thisLock = new object(); /// <summary> /// 执行定时器 /// </summary> public void TestTimer() { timerClose = new System.Threading.Timer(ToMyContent, null, 0, 30000); } /// <summary> /// 利用Application的特性 维护一个 集合表 表中记录了最后一次网站时间 如果最后一次访问时间 小于服务器时间20分钟者在数据库中设置为 离线状态 /// </summary> /// <param name="o"> /// The o. /// </param> private void ToMyContent(object o) { if (listMy == null || listMy.Count <= 0) { return; } DbContext db = MyDbContext.GetCurrentEFContext();//使用的是CallContxt 确保当前进程内唯一 // 定义一个变量用来判断 维护着的List 对象是否有值 方便最后EF统一执行SQL 语句 int i = 0; foreach (MyUser myContent in listMy) { TimeSpan s = new TimeSpan(0, 0, 0, 20);// 这是时间差为20分钟 System.TimeSpan ts = DateTime.Now.Subtract(myContent.UTime); //通过当前服务器时间减去用户最后一次访问服务器的时间的出来的 时间差 if (ts < s) { continue; // 跳出这次循环 不执行 if 后续代码 } // 做修改标注 User u = new User { ID = myContent.ID, Status = false, UTime = myContent.UTime, Name = myContent.Name }; DbEntityEntry<User> entry = db.Entry<User>(u); entry.State = System.Data.EntityState.Modified; entry.Property(a => a.Status).IsModified = true; i++; } if (i == 0) { return; } try { db.SaveChanges(); } catch (DbEntityValidationException dbEx) { } } /// <summary> /// 用户访问的 将其添加维护到 集合中 /// </summary> /// <param name="u"></param> public void AddList(User u) { if (u == null && u.ID <= 0) return; MyUser myU = new MyUser { ID = u.ID, Name = u.Name, UTime = DateTime.Now, Status = u.Status }; if (listMy == null) { lock (thisLock) { listMy = new List<MyUser> { myU }; } return; } MyUser m = listMy.FirstOrDefault(c => c.ID == myU.ID); if (m != null && m.ID != 0) { lock (thisLock) { listMy.Remove(m); listMy.Add(myU); } } else { lock (thisLock) { listMy.Add(myU); } } } } }
MyDbContext类 线程唯一 EF上下文的问题 你懂的
public static class MyDbContext { public static DbContext GetCurrentEFContext() { DbContext dbContext = CallContext.GetData("EFContext") as DbContext; if (dbContext==null) { dbContext = new Model1Container(); dbContext.Configuration.ValidateOnSaveEnabled = true; CallContext.SetData("EFContext", dbContext); } return dbContext; } }
MVC 的全局过滤器 可以做到 不管用户访问那个服务器页面 都可以进行操作。 额 面向切面编程?
在 FilterConfig 类中添加过滤器类
public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new HandleErrorAttribute()); filters.Add(new OnlineStatus.Controllers.LoginValidateAttribute()); } }
LoginValidateAttribute 自定义过滤器的实现
public class LoginValidateAttribute : System.Web.Mvc.AuthorizeAttribute { public override void OnAuthorization(System.Web.Mvc.AuthorizationContext filterContext) { // 判断 请求的 控制器是不是等于指定的控制器 如果不留下一个不用验证的控制器 那么就会一直在服务器跳转 if (filterContext.RouteData.Values["controller"].ToString() != "RegisterLogin") { #region 1.验证用户是否登陆(Session && Cookie) //1.验证用户是否登陆(Session && Cookie) if (!OnlineStatus.Controllers.Help.ToHelp().IsLogin()) { filterContext.Result = filterContext.Result = new RedirectResult("/RegisterLogin/index"); } #endregion } } }
ToHelp 类 的实现
public class Help { private Help() { } private static Help Ihelp; public static Help ToHelp() { return Ihelp ?? (Ihelp = new Help()); } #region 0.1 Http上下文 及 相关属性 /// <summary> /// Http上下文 /// </summary> HttpContext ContextHttp { get { return HttpContext.Current; } } HttpResponse Response { get { return ContextHttp.Response; } } HttpRequest Request { get { return ContextHttp.Request; } } System.Web.SessionState.HttpSessionState Session { get { return ContextHttp.Session; } } #endregion #region 2.1 当前用户对象 +MODEL.Ou_UserInfo Usr // <summary> /// 当前用户对象 /// </summary> public Models.User Usr { get { return Session["name"] as Models.User; } set { Session["name"] = value; } } #endregion /// <summary> /// 验证用户是否登录 /// </summary> /// <returns></returns> public bool IsLogin() { if (Session["name"] == null) { if (Request.Cookies["name"] == null) { return false; } string strUser = Request.Cookies["name"].Value; int userid = int.Parse(strUser); DbContext db = MyDbContext.GetCurrentEFContext(); OnlineStatus.Models.User use = db.Set<OnlineStatus.Models.User>().FirstOrDefault(c => c.ID == userid); if (use == null && use.ID <= 0) { return false; } Usr = use; //将其添加到维护的 状态集合中 StatusMy.GetStatusMy().AddList(use); } return true; } /// <summary> /// 登录 这里就简单写了 名称密码都对 就true 不对 false 额 以为是想简单写一下 数据库居然没弄密码 额 /// </summary> /// <param name="name"></param> /// <param name="pwd"></param> /// <returns></returns> public bool Login(string name) { DbContext db = MyDbContext.GetCurrentEFContext(); OnlineStatus.Models.User use = db.Set<OnlineStatus.Models.User>().FirstOrDefault(c => c.Name == name); if (use == null && use.ID >= 0) { return false; } //将其添加到维护的 状态集合中 StatusMy.GetStatusMy().AddList(use); return true; } }
我试了试 OK
一直都是自己一个人研究来研究去的 冒泡出来 纯粹是来 希望和大神们交流一下 ,请大神们指点一下。
不喜勿喷。
看帖 求评论啊, 真的很希望能有人 指点指点啊