I am using SESSION for storing user data, thus avoiding unnecessary access to the database. However, each access controller, data FormsAuthentication are renewed and data SESSION no.
What better way to get around this problem?
Put the lives of the great SESSION, or make a Base Controller or each called a ActionResult renew the life of SESSION.
Detail, SESSION I use this to mount a header of my pages.
This code, create SESSION.
public static void UsuarioLogar(string login)
{
CustomMembershipUser usuario = new CustomMembershipUser();
using (var dbUser = new ERPContext())
{
var dados = (from u in dbUser.Usuario
where u.Login == login
select new
{
Nome = u.Nome,
UsuarioID = u.UsuarioID,
EmpresaID = u.EmpresaID,
EmpresaLogada = u.Empresa,
PessoaLogada = u.PessoaLogada
}).FirstOrDefault();
if (dados != null)
{
usuario.UsuarioID = dados.UsuarioID;
usuario.Nome = dados.Nome;
usuario.EmpresaID = dados.EmpresaID;
usuario.EmpresaLogada = dados.EmpresaLogada;
usuario.PessoaLogada = dados.PessoaLogada;
if (usuario.PessoaLogada != null)
usuario.Acesso = "Restrito";
else
usuario.Acesso = "Full";
HttpContext.Current.Session["usuarioLogado"] = usuario;
}
else
{
HttpContext.Current.Session["usuario"] = null;
FormsAuthentication.SignOut();
}
}
}
I would avoid Session, especially when using MVC - it isn't a good fit with the web.
One alternative would be to use IIdentity and set the details in the Application_AuthenticateRequest method.
Related
I am very new to c# and asp.net mvc. I'm building a HR portal for our company where a user can submit a leave form among other things... So I'm using mssql as the database server and using Entity Frame work to communicate with it. I have 3 entities, user (Containing user details), permissions (the user permissions for allowing actions in the app) and then the leave form table (where the leave form details are stored). There is a one to many relationship between user - permission and then a one to many relationship between user-leave. I am not fazed about the permissions as that gets created when the user account is being created.
The problem I am facing is, how do I add a leave form for a specific user? Below is my controller code:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Leave(MasterViewModel model)
{
DocSubViewModel mv = model.DSModel;
int userId = Convert.ToInt32(Session["userID"]);
try
{
using (HrDcpDBContainer db = new HrDcpDBContainer())
{
var leave = db.leaves.Create();
leave.dateFrom = mv.DateFrom;
leave.dateSubmitted = DateTime.Now;
leave.dateTo = mv.DateTo;
leave.nrDays = mv.NrDays;
leave.reason = mv.SpecialLeave;
leave.TLApproval = null;
leave.TLApprovalDate = null;
leave.TLApprovalID = mv.TeamLeaderID;
leave.DMApprovalDate = null;
leave.DMApprovalID = mv.DepManagerID;
leave.DMApproval = null;
leave.type = mv.Type;
leave.user = userId;
db.leaves.Add(leave);
db.SaveChanges();
}
ViewBag.Message = "Leave Form submitted Successfully. You will be redirected shortly...";
return View("result");
}
catch (Exception ex)
{
ViewBag.Message = ex;
//ViewBag.Message = "Leave Form submitted Successfully. You will be redirected shortly...";
return View("result");
}
The problem comes in leave.user = userId;. It says:
Cannot implicitly convert int to Portal.Model.user
I can't seem to find out how to do this...
You're telling it to put the UserId where your leave model is asking for a User.
Your relationship requires a User to go in there, so you'll have to update your code a little bit:
using (HrDcpDBContainer db = new HrDcpDBContainer())
{
var leave = db.leaves.Create();
leave.user = db.users.First(x => x.Id == userId);
}
This will put reference to the actual user in the new leave record. If you go later and check it out you'll see a column in the leave table called user_Id that has an integer value in it and is set as a foreign key to the users table.
Note that this will error if no user exists having the specified Id value. If you anticipate this to be a problem, rather use .FirstOrDefault() instead of .First() and then account for the value being null before you add it to your new leave object.
That's expected since User is a object and not int. What you should be doing probably is leave.user.UserId = userId; instead [Assuming leave.user is of type User which has a UserId property]
I have a MVC application which is a public site. I am using Log4net to store user's activity in Azure Storage.
I am storing contextual data in threadcontext and use this when message are logged into azure storage.
But for some reason it is taking the wrong values i.e some random values. I doubt that i am doing something wrong using this threadcontext.
Please find below my code snippet.
In my Mvc controller i am using
log4net.ThreadContext.Properties["CustomerID"] = username
log4net.ThreadContext.Properties["CreatedBy"] = createdbyusername
In custom Appender i am using as
var keys = log4net.ThreadContext.Properties.GetKeys();
CustomerID = keys.Contains("CustomerID") ? log4net.ThreadContext.Properties["CustomerID"].ToString() : loggingEvent.Identity,
CreatedBy = keys.Contains("CreatedBy") ? log4net.ThreadContext.Properties["CreatedBy"].ToString() : loggingEvent.Identity,
Can anyone tell me how to achieve this so that it takes the right value for each request?
My appender code:
protected void LogAudit(AuditEventType eventType, object additionalObject, string customerId = "")
{
if (_logger != null)
{
if (!String.IsNullOrEmpty(customerId))
log4net.ThreadContext.Properties["CustomerID"] =
customerId;
AuthSSOProvider sso = new AuthSSOProvider();
var helpDeskUser = sso.GetCookie(ConfigurationManager.AppSettings.Get("cookieKey"));
if (helpDeskUser != null)
log4net.ThreadContext.Properties["CreatedBy"] =
helpDeskUser;
_logger.LogAudit(eventType, additionalObject);
}
}
I am getting List of Users from other application using webservice. I am getting all the Info.
List<User> users = ws.SelectUsers();
I want to store this list of users in between controller actions so that I dont want to hit web service everytime for their roles and other Info.
What would be the best way to do this using C# and MVC
I w
You can use the MemoryCache to store things. The below example would store the users for an hour in cache. If you needed to store it on a per user basis you could change it slightly so that the cache key ("Users" in the below example) is generated using the users ID or something.
MemoryCache cache = MemoryCache.Default;
string baseCacheKey = "Users";
public void DoSomethingWithUsers(int userId)
{
var cacheKey = baseCacheKey + userId.ToString();
if (!cache.Contains(cacheKey))
{
RefreshUserCache(userId);
}
var users = cache.Get(cacheKey) as List<User>
// You can do something with the users here
}
public static RefreshUserCache(int userId)
{
var users = ws.SelectUsers();
var cacheItemPolicy = new CacheItemPolicy();
cacheItemPolicy.AbsoluteExpiration = DateTime.Now.AddHours(1);
var cacheKey = baseCacheKey + userId.ToString();
cache.Add(cacheKey, users , cacheItemPolicy);
}
Edit: I've included the option for a userId if you do want to do it per user
Trying to understand best practises for storing login info after my user has been authenticated. Is it best to use page level propertys , session or what approch.
Ideally this is what I want to be able to perform on my login click im doing the following then on each page after i want to be able to do is
if (userValid and IsAuhtorised and Status =Admin)
then
else do 404 i guess would be best approach.
Sorry I should have sateted not allowed to use ms built in systems.
DBContext _db = new DBContext();
UserValidationResult userStatus=_db.ValidateUser(txtUsername.Text, txtPassword.Text);
Session["level"] = userStatus.userLevel.ToString();
Session["username"] = userStatus.User.username;
Session["logedin"] = true;
if (userStatus.Status == Mercentwarehousecms.User.UserValidationResult.LStatus.InvalidPassword)
{
lblFailed.Text = "Invlaid Password";
}
if (userStatus.Status == Mercentwarehousecms.User.UserValidationResult.LStatus.UserDoesNotExist)
{
lblFailed.Text = "User Does Not Exist";
}
if (userStatus.Status == Mercentwarehousecms.User.UserValidationResult.LStatus.Sucess && userStatus.userLevel == Mercentwarehousecms.User.UserValidationResult.UserLevel.Administrator)
{
Response.Redirect("~/admin/users/default.aspx");
}
if (userStatus.userLevel == Mercentwarehousecms.User.UserValidationResult.UserLevel.StoreManager)
{
lblFailed.Text = "i AM store manager ";
}
Change this:
UserValidationResult userStatus=_db.ValidateUser(txtUsername.Text, txtPassword.Text);
Session["level"] = userStatus.userLevel.ToString();
Session["username"] = userStatus.User.username;
Session["logedin"] = true;
To:
UserValidationResult userStatus=_db.ValidateUser(txtUsername.Text, txtPassword.Text);
Session["userStatus"] = userStatus;
Then later on you can retrieve it:
UserValidationResult userStatus = (UserValidationResult)Session["userStatus"];
I don't know what you mean about encryption. Use SSL if you want to protect the user's password from going over the wire as clear text.
I am electing to do session to store a value that I will need to call and update throughout a handful of controllers and views. I know it is possible to do something like this with a BaseViewModel.cs, or something less session-y, but I am trying to see how Session can possibly solve my needs. That out of the way, here is what I am doing:
Flow
I have a partial view that is rendered on my _layout page like so:
#Html.Action("OrgSwitch", new { controller = "Common", area = "InkScroll" })
This is a drop down list containing a logged in users organizations. It hits a CommonController that takes care of things like rendering model-bound logic on layout pages. In the CommonController I have this view and a postback, like so:
[ChildActionOnly]
public ViewResult OrgSwitch()
{
var userOrgs = new List<SelectListItem>();
var user = Ctx.Users.FirstOrDefault(x => x.UserName == User.Identity.Name);
int key = 0;
if (user.Organizations.Count > 0)
{
TempData["HasOrgs"] = true;
foreach (var org in user.Organizations)
{
if (Session["SelectedOrgKey"] == null)
{
//currently setting selected by primary org id
//todo: set selected to tempdata selectedOrgId if there is one.
userOrgs.Add(org.OrganizationId.ToString() == user.PrimaryOrgId
? new SelectListItem { Text = org.Name, Value = org.OrganizationId.ToString(), Selected = true }
: new SelectListItem { Text = org.Name, Value = org.OrganizationId.ToString(), Selected = false });
}
else
{
key = Convert.ToInt32(Session["SelectedOrgKey"]);
userOrgs.Add(org.OrganizationId == key
? new SelectListItem { Text = org.Name, Value = org.OrganizationId.ToString(), Selected = true }
: new SelectListItem { Text = org.Name, Value = org.OrganizationId.ToString(), Selected = false });
}
}
ViewBag.UserOrgs = userOrgs.OrderBy(x => x.Text);
}
else
{
ViewBag.UserOrgs = userOrgs;
TempData["HasOrgs"] = false;
}
Session["SelectedOrgKey"] = key;
return View();
}
[HttpPost]
public RedirectToRouteResult OrgSwitch(string UserOrgs)
{
Session["SelectedOrgKey"] = UserOrgs;
return RedirectToAction("Index", "Recruiter", new { orgId = UserOrgs, area = "InkScroll" });
}
This more or less on initial load will render the drop down list and default it to the users primary org. If they select something and post back that selection, it will display the selected one by default on subsequent pages.
attempt and failure
I am trying various ways of utilizing the Session value. One area in a viewresult:
[ChildActionOnly]
public ViewResult StaffList()
{
var user = Ctx.Users.FirstOrDefault(x => x.UserName == User.Identity.Name);
var model = new List<StaffListViewModel>();
int key = Convert.ToInt32(Session["SelectedOrgKey"]);
var org = user.Organizations.FirstOrDefault(x => x.OrganizationId == key);
if (org.RegisteredMembers != null)
{
foreach (var req in org.RegisteredMembers)
{
model.Add(new StaffListViewModel
{
UserName = req.UserName,
Name = (req.FirstName ?? "") + " " + (req.LastName ?? ""),
ImageLocation = req.ImageLocation
});
}
}
Session["SelectedOrgKey"] = key;
return View(model);
}
in here 'key' is coming up empty.
Another try is putting it in the layout cshtml file like so:
<li><a href="#Url.Action("Staff", "Recruiter", new {area="",
orgId = Convert.ToInt32(Session["SelectedOrgId"])})">Staff</a></li>
in this one if I hover over the link, the orgId is always equal to 0. Am I messing this up somewhere? Really, I need to have this SelectedOrgId available to any page on the application.
An answer for posterity sake:
The code in the question DOES work. The way I am setting and calling session in asp.net mvc works fine. If you have arrived here in a vain search to get session working, then keep looking I guess. The issue I was have related to my control flow and the parameter i was passing in. Once I removed the 'orgId' from the parameters above, I refactored the if/else stuff to just look at session. So the issue was that somewhere in the app I am working on, orgId was not being changed from '0'. Anyway, I am an idiot, but at least I can give you a quick overview of what the hell Session is in asp.net mvc and how easy it is to use:
Setting up app to use session
Session is defaulted to be in process with a timeout of 20 minutes. If you feel like something is overwriting that default you can set it explicitly in your web.config (root web.config on a standard asp.net mvc app):
<system.web>
<sessionState mode="InProc" timeout="60"></sessionState>
...other stuff..
</system.web>
I set my timeout above to 60 minutes since I wanted it to stay around a little longer. Standard is 20.
Setting a session variable
Pretty damn simple. Do this:
Session["MyStringVariable"] = "Some value I want to keep around";
Session["MyIntegerVariable"] = 42;
Retrieving the session variable
again, pretty simple, you just need to pay attention to casting/converting the variable to what suits you.
var localVar = Session["MyStringVariable"].ToString();
var anotherLocalVar = Convert.ToInt32(Session["MyIntegerVariable"] = 42);
So, yeah, I was doing it right, but due to other complexities in my code above, I blamed the big bad Session and not my brain.
Hope this helps someone! (And if I have the above info wrong, please let me know and I will update!).