I have a page in my application which always shows updated list of online users.
Now, to keep the list-which is stored in application object- updated, i do the below steps
add user to list when login
remove user on log off
Then to handle browser close/navigate away situations, I have a timestamp along with the username in the collection
An ajax call every 90 seconds updates the timestamp.
The problem:
I need something to clean this list every 120 seconds to remove entries with old timestamps.
How do I do this within my web application? ie Call a function every 2 mins.
PS: I thought of calling a webservice every 2 mins using a scheduler , but the hosting environment do not allow any scheduling.
Do the following inside a global filter.
public class TrackLoginsFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
Dictionary<string, DateTime> loggedInUsers = SecurityHelper.GetLoggedInUsers();
if (HttpContext.Current.User.Identity.IsAuthenticated )
{
if (loggedInUsers.ContainsKey(HttpContext.Current.User.Identity.Name))
{
loggedInUsers[HttpContext.Current.User.Identity.Name] = System.DateTime.Now;
}
else
{
loggedInUsers.Add(HttpContext.Current.User.Identity.Name, System.DateTime.Now);
}
}
// remove users where time exceeds session timeout
var keys = loggedInUsers.Where(u => DateTime.Now.Subtract(u.Value).Minutes >
HttpContext.Current.Session.Timeout).Select(u => u.Key);
foreach (var key in keys)
{
loggedInUsers.Remove(key);
}
}
}
To retrieve the user list
public static class SecurityHelper
{
public static Dictionary<string, DateTime> GetLoggedInUsers()
{
Dictionary<string, DateTime> loggedInUsers = new Dictionary<string, DateTime>();
if (HttpContext.Current != null)
{
loggedInUsers = (Dictionary<string, DateTime>)HttpContext.Current.Application["loggedinusers"];
if (loggedInUsers == null)
{
loggedInUsers = new Dictionary<string, DateTime>();
HttpContext.Current.Application["loggedinusers"] = loggedInUsers;
}
}
return loggedInUsers;
}
}
Don't forget to Register you filter in global.asax. It's probably a good idea to have an app setting to switch this off.
GlobalFilters.Filters.Add(new TrackLoginsFilter());
Also remove users at logoff to be more accurate.
SecurityHelper.GetLoggedInUsers().Remove(WebSecurity.CurrentUserName);
In your Account Controller
public ActionResult Login(LoginModel model, string returnUrl)
{
if (ModelState.IsValid)
{
if (Membership.ValidateUser(model.UserName, model.Password))
{
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
if (HttpRuntime.Cache["LoggedInUsers"] != null) //if the list exists, add this user to it
{
//get the list of logged in users from the cache
List<string> loggedInUsers = (List<string>)HttpRuntime.Cache["LoggedInUsers"];
//add this user to the list
loggedInUsers.Add(model.UserName);
//add the list back into the cache
HttpRuntime.Cache["LoggedInUsers"] = loggedInUsers;
}
else //the list does not exist so create it
{
//create a new list
List<string> loggedInUsers = new List<string>();
//add this user to the list
loggedInUsers.Add(model.UserName);
//add the list into the cache
HttpRuntime.Cache["LoggedInUsers"] = loggedInUsers;
}
if (!String.IsNullOrEmpty(returnUrl))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
public ActionResult LogOff()
{
string username = User.Identity.Name; //get the users username who is logged in
if (HttpRuntime.Cache["LoggedInUsers"] != null)//check if the list has been created
{
//the list is not null so we retrieve it from the cache
List<string> loggedInUsers = (List<string>)HttpRuntime.Cache["LoggedInUsers"];
if (loggedInUsers.Contains(username))//if the user is in the list
{
//then remove them
loggedInUsers.Remove(username);
}
// else do nothing
}
//else do nothing
FormsAuthentication.SignOut();
return RedirectToAction("Index", "Home");
}
in your partial view.
#if (HttpRuntime.Cache["LoggedInUsers"] != null)
{
List<string> LoggedOnUsers = (List<string>)HttpRuntime.Cache["LoggedInUsers"];
if (LoggedOnUsers.Count > 0)
{
<div class="ChatBox">
<ul>
#foreach (string user in LoggedOnUsers)
{
<li>
<div class="r_row">
<div class="r_name">#Html.Encode(user)</div>
</div>
</li>
}
</ul>
</div>
}
}
render this partial view when user log in.
use this script call ever 90 second
<script type="text/javascript">
$(function () {
setInterval(loginDisplay, 90000);
});
function loginDisplay() {
$.post("/Account/getLoginUser", null, function (data) {
});
}
</script>
Here is the white elephant solution.
Instead of maintaining this list in application object, maintain this list in database. Then you can use database jobs to work on this list periodically. Establish SQL notification on this object so that everytime this list is purged you get refreshed data in your application.
Use Ajax to send "I am still online" message to the server in every 30 seconds. This is the best way to find who is really online.
So here what I did:
Create a table in the database
CREATE TABLE [dbo].[OnlineUser]
(
[ID] [int] IDENTITY(1,1) NOT NULL,
[Guid] [uniqueidentifier] NOT NULL,
[Email] [nvarchar](500) NOT NULL,
[Created] [datetime] NOT NULL,
CONSTRAINT [PK_OnlineUser] PRIMARY KEY CLUSTERED
(
[ID] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
Override the OnActionExecution method. This method is in a separate controller in my case is called AuthController then every other controller that required authemtication inherits from this controller.
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
// session variable that is set when the user authenticates in the Login method
var accessSession = Session[Constants.USER_SESSION];
// load cookie is set when the user authenticates in the Login method
HttpCookie accessCookie = System.Web.HttpContext.Current.Request.Cookies[Constants.USER_COOKIE];
// create session from cookie
if (accessSession == null)
{
if (accessCookie != null)
{
if (!string.IsNullOrEmpty(accessCookie.Value))
accessSession = CreateSessionFromCookie(accessCookie);
}
}
// if session does not exist send user to login page
if (accessSession == null)
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary
{
{"controller", "Account"},
{"action", "Login"}
}
);
return;
}
else
{
TrackLoggedInUser(accessSession.ToString());
}
}
private List<OnlineUser> TrackLoggedInUser(string email)
{
return GetOnlineUsers.Save(email);
}
Next I created the following classes in the Data Repository class: GetOnlineUsers
public static class GetOnlineUsers
{
public static List<OnlineUser> GetAll()
{
using (var db = new CEntities())
{
return db.OnlineUsers.ToList();
}
}
public static OnlineUser Get(string email)
{
using (var db = new CEntities())
{
return db.OnlineUsers.Where(x => x.Email == email).FirstOrDefault();
}
}
public static List<OnlineUser> Save(string email)
{
using (var db = new CEntities())
{
var doesUserExist = db.OnlineUsers.Where(x => x.Email.ToLower() == email.ToLower()).FirstOrDefault();
if (doesUserExist != null)
{
doesUserExist.Created = DateTime.Now;
db.SaveChanges();
}
else
{
OnlineUser newUser = new OnlineUser();
newUser.Guid = Guid.NewGuid();
newUser.Email = email;
newUser.Created = DateTime.Now;
db.OnlineUsers.Add(newUser);
db.SaveChanges();
}
return GetAll();
}
}
public static void Delete(OnlineUser onlineUser)
{
using (var db = new CEntities())
{
var doesUserExist = db.OnlineUsers.Where(x => x.Email.ToLower() == onlineUser.Email.ToLower()).FirstOrDefault();
if (doesUserExist != null)
{
db.OnlineUsers.Remove(doesUserExist);
db.SaveChanges();
}
}
}
}
In the Global.asax
protected void Application_EndRequest()
{
// load all active users
var loggedInUsers = GetOnlineUsers.GetAll();
// read cookie
if (Context.Request.Cookies[Constants.USER_SESSION] != null)
{
// the cookie has the email
string email = Context.Request.Cookies[Constants.USER_SESSION].ToString();
// send the user's email to the save method in the repository
// notice in the save methos it also updates the time if the user already exist
loggedInUsers = GetOnlineUsers.Save(email);
}
// lets see we want to clear the list for inactive users
if (loggedInUsers != null)
{
foreach (var user in loggedInUsers)
{
// I am giving the user 10 minutes to interact with the site.
// if the user interaction date and time is greater than 10 minutes, removing the user from the list of active user
if (user.Created < DateTime.Now.AddMinutes(-10))
{
GetOnlineUsers.Delete(user);
}
}
}
}
In one of the controllers (You can create a new one up to you) that inhering from the AuthController, create the following method:
public JsonResult GetLastLoggedInUserDate()
{
string email = Session[Constants.USER_SESSION].ToString();
var user = GetOnlineUsers.Get(email);
return Json(new { year = user.Created.Year,
month = user.Created.Month,
day = user.Created.Day,
hours = user.Created.Hour,
minutes = user.Created.Minute,
seconds = user.Created.Second,
milliseconds = user.Created.Millisecond
}, JsonRequestBehavior.AllowGet);
}
In your _Layout.cshtml file at the very bottom place this Javascript code: This Javascript code will call the GetLastLoggedInUserDate() above to get the last interacted date from the database.
<script>
var lastInteracted, DifferenceInMinutes;
$(window).on('load', function (event) {
$.get("get-last-interaction-date", function (data, status) {
lastInteracted = new Date(data.year.toString() + "/" + data.month.toString() + "/" + data.day.toString() + " " + data.hours.toString() + ":" + data.minutes.toString() + ":" + data.seconds.toString());
});
});
$(window).on('mousemove', function (event) {
var now = new Date();
DifferenceInMinutes = (now.getTime() - lastInteracted.getTime()) / 60000;
if (DifferenceInMinutes > 5) {
$.get("get-last-interaction-date", function (data, status) {
lastInteracted = new Date(data.year.toString() + "/" + data.month.toString() + "/" + data.day.toString() + " " + data.hours.toString() + ":" + data.minutes.toString() + ":" + data.seconds.toString());
});
}
});
</script>
JavaScript explanation:
On page load I am are setting the last datetime the the user interacted with my website.
Since I cannot track what the user stares at on the screen, the next closest thing to real interaction is mouse movement.
So when the user moves the mouse anywhere on the page the following happens:
I compare the last interacted date with the current date.
Then I check if 5 minutes passed since the last updated date occurred.
Since the user happened to love the website and decided to spend more time on it, after the 5 minutes are passed, I send another request to the this method in my controller GetLastLoggedInUserDate() to get the date again. But before we get the date we will execute the OnActionExecuting method which will then update the records Created date and will return the current time. The lastInteracted gets the updated date and we go again.
The idea here is that when the user is not interacting with my website he is not really online for me. Maybe he has 100 tabs open and playing games doing other things but interacting with my website it is possible that they will not even realize they have it open in days or months depends on how often they reboot the PC. In any case I think that 10 minutes is a good threshold to work with but feel free to change it.
Finally AdminController class:
public ActionResult Index()
{
DashboardViewModel model = new DashboardViewModel();
// loading the list of online users to the dashboard
model.LoggedInUsers = GetOnlineUsers.GetAll();
return View("Index", "~/Views/Shared/_adminLayout.cshtml", model);
}
Index.cshtml (admin dashboard page)
#model ILOJC.Models.Admin.DashboardViewModel
#{
ViewBag.Menu1 = "Dashboard";
}
/// some html element and styles
<h5 class="">#Model.LoggedInUsers.Count() Online Users</h5>
<div class="row">
#foreach (var user in Model.LoggedInUsers.OrderByDescending(x => x.Created))
{
<div class="col-md-12">
<h5>#user.Email</h5>
<p><span>Last Inreaction Time: #user.Created.ToString("MM/dd/yyyy hh:mm:ss tt")</span></p>
</div>
}
</div>
Since the original table will only store online users I wanted to have a bit of history/log so I create a history table in the database:
CREATE TABLE [dbo].[OnlineUserHistory](
[ID] [int] IDENTITY(1,1) NOT NULL,
[OnlineUserID] [int] NOT NULL,
[Guid] [uniqueidentifier] NOT NULL,
[Email] [nvarchar](500) NOT NULL,
[Created] [datetime] NOT NULL,
[Updated] [datetime] NOT NULL,
[Operation] [char](3) NOT NULL,
CONSTRAINT [PK_OnlineUserLog] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
Lastly, I created a database Trigger on insert and delete
CREATE TRIGGER [dbo].[trg_online_user_history]
ON [dbo].[OnlineUser]
AFTER INSERT, DELETE
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO OnlineUserHistory(
OnlineUserID,
[Guid],
Email,
Created,
Updated,
Operation
)
SELECT
i.ID,
i.[Guid],
i.Email,
i.Created,
GETDATE(),
'INS'
FROM
inserted i
UNION ALL
SELECT
d.ID,
d.[Guid],
d.Email,
d.Created,
GETDATE(),
'DEL'
FROM
deleted d;
END
Hope this can hep someone. One thing I would improve tho is the way the online users are displaying load in the dashboard. Now, I need to refresh the page to see the updated number. But if you want to see it live, you just add the SignalR library then create a hub and you good to go!
Related
So in my global.asax, I've got the following code:
Inventory.BusinessTier bt = new Inventory.BusinessTier();
string UserLogin = bt.ExtractLogin (Request.ServerVariables ["AUTH_USER"]);
Inventory.User myUser = new Inventory.User (UserLogin);
Session ["User"] = myUser;
It works just fine on one development PC, but using the same version of Visual Studio, it craps out on the third line with this error:
System.TypeInitializationException: 'The type initializer for
'Inventory.DataTier' threw an exception.'
Inner Exception
NullReferenceException: Object reference not set to an instance of an
object.
Other than a line adding impersonation in my web.config (it has to be there now), I haven't changed a single thing. Is there a way to get more info on this? I can't even trace it, because if I put a debug line in the User object constructor, it never hits it. I'm at a bit of a loss. Would appreciate any advice.
EDIT to answer questions below:
InventoryUser is a very simple user object that reads the current from the database and stores some basic user info in properties, such as UserID, Role, RoleID, and IsAdmin.
The DataTier class is a class that interacts with the database. It is used in multiple projects, so I'm quite sure it's not the problem. I tried to paste in the code anyway, but it exceeded the limit for a post.
I'm reasonably sure the problem is related to the user class. It's short, so I can paste it in here:
using System;
using System.Data;
// This is the user business object. It contains information pertaining to the current user of the application. Notably, it
// contains the department ID, which determines what inventory items the user will see when using the application. Only
// specified employees with admin access can see all items for all departments, and that is determined by a specific department ID.
namespace Inventory {
public class User {
private Guid _UserID;
private Guid _RoleID;
private Guid _UserDepartmentID;
private string _UserRole = "";
private string _UserName = "";
private bool _IsAuthorizedUser = false;
private bool _IsAdmin = false;
// Attribute declarations
public Guid UserID {
get {
return _UserID;
}
set {
_UserID = value;
}
}
public string UserRole {
get {
return _UserRole;
}
set {
_UserRole = value;
}
}
public Guid RoleID {
get {
return _RoleID;
}
set {
_RoleID = value;
}
}
public string UserName {
get {
return _UserName;
}
set {
_UserName = value;
}
}
public Guid UserDepartmentID {
get {
return _UserDepartmentID;
}
set {
_UserDepartmentID = value;
}
}
public bool IsAdmin {
get {
return _IsAdmin;
}
set {
_IsAdmin = value;
}
}
public bool IsAuthorizedUser {
get {
return _IsAuthorizedUser;
}
set {
_IsAuthorizedUser = value;
}
}
// -----------------
// - Constructor -
// -----------------
public User (string UserLogin) {
string ShortUserLogin = ExtractLogin (UserLogin);
GetUser (ShortUserLogin);
}
// ------------------
// - ExtractLogin -
// ------------------
public string ExtractLogin (string Login) {
// The domain and "\" symbol must be removed from the string, leaving only the user name.
int pos = Login.IndexOf (#"\");
return Login.Substring (pos + 1, Login.Length - pos - 1);
}
// -------------
// - GetUser -
// -------------
// This method is called to fill the user object based on the user's login. It ultimately gets authorized user data
// from the user table.
public void GetUser (string UserName) {
DataTier dt1 = new DataTier();
DataTable dt = dt1.GetUserInfo (UserName);
int RecordCount = dt.Rows.Count;
switch (RecordCount) {
case 1: // There is one user name match, as there should be. This is the likely situation.
DataRow dr = dt.Rows[0];
UserID = (Guid)dr ["UserID"];
UserRole = (string)dr ["UserRole"];
RoleID = (Guid)dr ["RoleID"];
this.UserName = UserName;
UserDepartmentID = (Guid)dr ["DepartmentID"];
IsAdmin = (bool)dr ["IsAdmin"];
IsAuthorizedUser = true;
break;
case 0: // There are no user name matches (unauthorized use).
IsAdmin = false;
IsAuthorizedUser = false;
break;
default: // There are multiple user name matches (problem!).
IsAdmin = false;
IsAuthorizedUser = false;
break;
}
}
}
}
I have an angular 8 app with .net core web api 2.1 and MS SQL SERVER.
I have login and logout implementation for users.
After the user logins, he can add or delete programming langugages.
When I perform a delete operation, I get the error
database operation expected to affect 1 row(s) but actually effected
5 rows
But the code works properly for finding that particular user and the ID of the programming language that he wants to delete.
Here is the image from the table: https://imgur.com/a/taZtJ7d
As you can see there are no duplicates.
I think it is something about Concurrency Conflicts. That's why I added a try-catch block with DbUpdateConcurrencyException.
It works sometimes and sometimes not.
Here, you can find the definiton for the table https://imgur.com/a/YMCYkNy
When I try the following method to delete Users from MS SQL, it works as it should:
public async Task<IActionResult> DeleteProgrammingLanguage(string userId, int plId)
{
UserPL ps = new UserPL();
try
{
ps = await _context.PlUsers.Where(x => x.UserId == userId && x.ProgrammingLanguageId == plId).SingleAsync();
_context.PlUsers.Remove(ps);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException ex)
{
throw ex;
}
return Ok(ps);
}
I solved my problem with this solution.
public IActionResult DeleteProgrammingLanguage(string userId, int plId)
{
using (_context)
{
// Fetch a person from database and change phone number
var ps = _context.PlUsers.FirstOrDefault(x => x.UserId == userId && x.ProgrammingLanguageId == plId);
// Change the person's name in the database to simulate a concurrency conflict
_context.PlUsers.Remove(ps);
var saved = false;
while (!saved)
{
try
{
// Attempt to save changes to the database
_context.SaveChanges();
saved = true;
}
catch (DbUpdateConcurrencyException ex)
{
foreach (var entry in ex.Entries)
{
if (entry.Entity is UserPL)
{
var proposedValues = entry.CurrentValues;
var databaseValues = entry.GetDatabaseValues();
foreach (var property in proposedValues.Properties)
{
var proposedValue = proposedValues[property];
var databaseValue = databaseValues[property];
// TODO: decide which value should be written to database
// proposedValues[property] = <value to be saved>;
}
// Refresh original values to bypass next concurrency check
entry.OriginalValues.SetValues(databaseValues);
}
else
{
throw new NotSupportedException(
"Don't know how to handle concurrency conflicts for "
+ entry.Metadata.Name);
}
}
}
}
}
return NoContent();
}
I am newbie in ASP.NET. Is there a possible way to save the current logged user into the form database when the form is submitted?
(i.e) When multiple registered users enter a form and click save their user name is saved into the contents of form database.
I had googled it but couldn't find related topics (maybe I used the wrong keywords). If methods are available kindly suggest link.
[Edit]
I have used asp.net identity for authentication.
My current code for save
public ActionResult NewPR()
{
var depts = _context.Depts.ToList();
var currencytypes = _context.Currencytypes.ToList();
var viewModel = new NewPRViewModel
{
PRview = new PRview(),
Currencytypes = currencytypes,
};
return View("NewPR", viewModel);
}
[HttpPost]
public ActionResult Save(PRview prview)
{
if (!ModelState.IsValid)
{
var viewModel = new NewPRViewModel
{
PRview = prview,
Currencytypes = _context.Currencytypes.ToList()
};
return View("NewPR", viewModel);
}
if (prview.Id == 0)
_context.PRviews.Add(prview);
else
{
var prviewInDb = _context.PRviews.Single(c => c.Id == prview.Id);
prviewInDb.SupplierName = prview.SupplierName;
prviewInDb.Brand = prview.Brand;
prviewInDb.Qty = prview.Qty;
prviewInDb.Unitprice = prview.Unitprice;
}
_context.SaveChanges();
return RedirectToAction("Index", "PRview");
}
Is it possible to create a page (View) that is strictly controlled by an Active Directory Group?
There is no login for this page, if you are a member of the "VIP" Active Directory group, then the page is rendered, otherwise if not then you can't see it.
First get your current users windows log in
var windowsUserName= HttpContext.Current.User.Identity.WindowsLogin();
Then get all the AD groups for your user using the System.DirectoryServices
using System.DirectoryServices;
public List<string> GetUsersActiveDirectoryGroups(string windowsUserName)
{
try
{
var allUserGroups = new List<string>();
if (windowsUserName == null) return allUserGroups;
var domainConnection = new DirectoryEntry();
var samSearcher = new DirectorySearcher
{
SearchRoot = domainConnection,
Filter = "(samAccountName=" + windowsUserName + ")"
};
samSearcher.PropertiesToLoad.Add("displayName");
var samResult = samSearcher.FindOne();
if (samResult == null) return allUserGroups;
var theUser = samResult.GetDirectoryEntry();
theUser.RefreshCache(new[] { "tokenGroups" });
_bet365EmployeeFullName = theUser.Properties["CN"].Value.ToString();
foreach (byte[] resultBytes in theUser.Properties["tokenGroups"])
{
var mySid = new SecurityIdentifier(resultBytes, 0);
var sidSearcher = new DirectorySearcher
{
SearchRoot = domainConnection,
Filter = "(objectSid=" + mySid.Value + ")"
};
sidSearcher.PropertiesToLoad.Add("name");
var sidResult = sidSearcher.FindOne();
if (sidResult != null)
{
allUserGroups.Add((string)sidResult.Properties["name"][0]);
}
}
return allUserGroups;
}
You now need to map which groups have access to which view in the application.
Once done, the next step is the restricting of the viewing of "Views".
You need to set up a permissions filter that uses the MVC AuthorizeAttribute. Something like the below.
public class PermissionsFilter : AuthorizeAttribute
{
private readonly string _viewName;
public PermissionsFilter(string viewName)
{
_viewName = viewName;
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
//Check to see if users groups has access to the view
//If not redirect to unauthorized page
}
}
I accomplish the above by have a user object held in session. This contains a list of all the application permissions my user has access too. This is the mapping you need to do. I have all my view names stored in the database along with an ID of which AD groups can access them.
Then finally in the controller, decorate the get action for the view accordingly.
[HttpGet]
[PermissionsFilter("ViewName")]
public ActionResult ReturnMyView(int currentFolderID)
{
return View(); //Etc..
}
Well hope that helps!
I'm working on a social network with ASP.NET and signalr. I have a simple login page, if it finds the user in the database it creates an Application variable and redirect the user to the profile page and in this page i invoke my Connect method declared in my hub class, this method takes the userid in the session and it give the friend list of this user. That works great when two or many users logged in at different time. The thing is, when two or several users logged in at the same time, the connect method declared in my hub takes the last user id stored in the Application variable and it give the friend list of this last user id and it send it to all user connected.
I can't find the correct approach.
Loggin Page code:
protected void btn_login_Click(object sender, EventArgs e)
{
Tbl_User user = new Tbl_User();
user = FonctionCommun.Login(txt_UserName.Text , txt_PassWord.Text);
if (user != null)
{
Application["UserID"] = user.UserID.ToString();
Response.Redirect("Profile.aspx");
}
else {
Label1.Visible = true;
}
}
My connect method code:
public void connect()
{
UserID = Guid.Parse(HttpContext.Current.Application["UserID"].ToString());
string OutPut = "";
if (ListOnlineUser.Count(x => x.UserID == UserID) == 0)
{
ListOnlineUser.Add(new OnlineUsers { UserID = UserID, ConnetionID = Guid.Parse(Context.ConnectionId) });
objchat.SetOnline(UserID);
ListFriends = objchat.GetFriendLoginStatus(UserID);
}
foreach (Tbl_User item in ListFriends)
{
if (item.Status == "1")
{
OnlineUsers onlineFriend = ListOnlineUser.FirstOrDefault(x => x.UserID == Guid.Parse(item.UserID.ToString()));
if (onlineFriend != null)
{
using (FIESTA_ADVISOREntities BD = new FIESTA_ADVISOREntities())
{
Tbl_User Obj_User = BD.Tbl_User.Where(o => o.UserID == UserID).FirstOrDefault();
if (Obj_User.ProfileImage != null)
{
string ext = BD.Assets.Where(o => o.url == Obj_User.ProfileImage).Select(o => o.MimeType).FirstOrDefault();
UserDetaille res = new UserDetaille() { UserID = Guid.Parse(Obj_User.UserID.ToString()), Username = Obj_User.UserName, ProfileImage = Obj_User.ProfileImage.ToString(), Ext = ext };
OutPut = JsonConvert.SerializeObject(res);
}
else {
UserDetaille res = new UserDetaille() { UserID = Guid.Parse(Obj_User.UserID.ToString()), Username = Obj_User.UserName, ProfileImage = "111", Ext = "png" };
OutPut = JsonConvert.SerializeObject(res); }
Clients.Client(onlineFriend.ConnetionID.ToString()).OnNewUserConnect(OutPut);
}
}
}
}
Clients.Caller.ShowFriends(ListFriends);
}
Try session variable instead of application variable. Application variable shared through out application working. So Whenever new user this is override. But if you use session variable that will never override by any other user
Also you can use query string in signalr in which you can pass userid as query string so in each request userid will be in query string
$.connection.hub.qs = 'userid=' + "UserId";