Weird caching issue with ASP.net/Linq - asp.net

I'm writing an application involving storing a profile. I'm using Linq to access the database, but having a weird issue when saving a profile. When I save it, it writes to the DB correctly - but when I leave the page and come back, the old values still remain in profile form.
My profile page:
if(!Page.IsPostBack) {
Profile p = Student.GetProfile(Int32.Parse(Session["userID"].ToString()));
if (p != null)
{
FirstNameTextBox.Text = p.FirstName;
LastNameTextBox.Text = p.LastName;
Address1TextBox.Text = p.Address1;
.....
}
And my Student class:
public static Profile GetProfile(int uID)
{
var profile = (from p in db.Profiles
where p.uID == uID
select p).FirstOrDefault();
return profile;
}
I'm not doing any fancy caching anywhere, so not sure where the old values are stored...
** EDIT **
So, it seems that it's down to a global LinqDataContext. In my Student class, I had:
public class Student
{
private static LinqClassesDataContext db = new LinqClassesDataContext() { CommandTimeout = 36000 };
public static Profile GetProfile(int uID)
{
var profile = (from p in db.Profiles
where p.uID == uID
select p).FirstOrDefault();
return profile;
}
If I give the GetProfile method it's own DataContext, problem solved.
Still being very new to Linq, what's the best way to have a class with numerous methods that use the same access to a database? Having a global context like this? Or each method using it's own data context?

Assuming you're storing the userID in Session["userID"] somewhere and not clearing it out when you save, this could be where the "caching" occurs. Session objects will live (roughly) for the life of the browser session (or the life of the server process if in-memory session is enabled).

i guess even if linq send the query to the database it use the old value stored in the cache.so you have to clear the cache if you want the new value.

Going on your added comments my guess is that the page you are getting is cached by the browser / http server. If the url is the same as a previously requested page some default setting will tell the browser / server to use the cache html. To avoid this and get new html for each request you could try adding a meta tag within the head tags of your html.
<meta http-equiv="Pragma" CONTENT="no-cache">

It ended up being the DataContext I was using. I'm not exactly sure why this fixed the issue, but I changed my class from:
public class Student
{
private static LinqClassesDataContext db = new LinqClassesDataContext() { CommandTimeout = 36000 };
public static Profile GetProfile(int uID)
{
var profile = (from p in db.Profiles
where p.uID == uID
select p).FirstOrDefault();
return profile;
}
}
to:
public class Student
{
public static Profile GetProfile(int uID)
{
LinqClassesDataContext db = new LinqClassesDataContext();
var profile = (from p in db.Profiles
where p.uID == uID
select p).FirstOrDefault();
return profile;
}
}

Related

Adding a location that is tied to a userID via Foreign Key

I'm learning .NET here and have build a small API that allows me to register users, retrieve user lists, or a single user, as well as edit them. Microsoft Identity is used for most of this process.
I am now trying to add new section to this API that will handle physical locations. Each location will be tied back to a specific user, so there may be one or multiple locations per user. I'm having trouble building this part of it though. I cant seem to figure out how to tie the ID of the user to the location as a foreign key. I've been battling this one now for quite some time.
I have the controller build for the location calls, and the DTO's, but it does not seem to want to actually work correctly.
Anyone up to talking a look and letting me know what needs to be done and how to do this? I'm a bit lost and really wanting to learn how this works. The github repo with the full working project is here:
https://github.com/sapper6fd/API
The issue at hand here was the way it was configured.
The following adjustments fixed the issue:
CreateLocation() method:
[HttpPost]
public async Task<IActionResult> CreateLocation(int clientId, LocationCreateDto locationCreateDto)
{
// Grab the current users roles to verify they have access to this API call
var userRole = User.FindFirst(ClaimTypes.Role).ToString();
if (userRole != "http://schemas.microsoft.com/ws/2008/06/identity/claims/role: GlobalAdmin")
return Unauthorized();
// Create the new location
var newLocation = _mapper.Map<Locations>(locationCreateDto);
_repo.Add(newLocation);
if (await _repo.SaveAll())
{
var locationToReturn = _mapper.Map<LocationCreateDto>(newLocation);
return CreatedAtRoute(nameof(GetLocation), new { id = locationToReturn.Id, userid=clientId }, locationToReturn);
}
throw new Exception("Unable to create new location. Failed to save.");
}
HttpGet Method:
[HttpGet("{id}", Name = nameof(GetLocation))]
public async Task<IActionResult> GetLocation(int id)
{
var location = await _repo.GetLocation(id);
var locationToReturn = _mapper.Map<LocationCreateDto>(location);
return Ok(locationToReturn);
}

Inserting Sub-Array into Azure's Document DB

I am trying to insert a sub class (document) of "Video" into my Organization document.
However, when I try to add a record, I get "Object reference is not set to an instance of an object."
I tried to use Add and Insert, but neither worked. I looked at the Dcoument explorer and I can see that Videos is returning "null."
I am assuming my problem is that Document DB doesn't know that Video is a list. (in my model, I have defined it as a list though)
Also, I have tried created new objects for Organization and Video. Also, I have a class called Category, it has the exact same code (except the object is Category) and it is inserting fine.
Below is the action that I am using.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create([Bind(Include = "name,description,link")] Video video)
{
if (ModelState.IsValid)
{
UserSession usersession = new UserSession();
usersession = (UserSession)Session["user"];
Organization organization = (Organization)DocumentDBRepository<Organization>.GetItem(d => d.Id == usersession.organizationId);
video.DateAdded = DateTime.Now;
organization.Videos.Add(video);
await DocumentDBRepository<Organization>.UpdateItemAsync(organization.Id, organization);
return RedirectToAction("Index");
}
return View(video);
}
Set organization.Videos to a non-null value. Document db simply preserves what you stored. Apparently, you previously stored null.

How to persist SignalR connection ID

I’m trying to build a chat apps whereby users id are represented by their auto generated signalR connection id. On page refresh, the connection id changes when a new connection is instantiated. Is there a way to persist the state of the connection id of a user until the browser session is ended (i.e until he ends his session on client).
Any guide or documentation? It really would help.
i am new in signalr. so trying to know many things searching Google. from this url i got a similar snippet http://kevgriffin.com/maintaining-signalr-connectionids-across-page-instances/
they are saying it is possible. the problem is signalr often create a new connection id if we referesh the page. i want to prevent this but how.......
this code snippet.
public class MyConnectionFactory : IConnectionIdFactory
{
public string CreateConnectionId(IRequest request)
{
if (request.Cookies["srconnectionid"] != null)
{
return request.Cookies["srconnectionid"];
}
return Guid.NewGuid().ToString();
}
}
$.connection.hub.start().done(function () {
alert("Connected!");
var myClientId = $.connection.hub.id;
setCookie("srconnectionid", myClientId);
});
function setCookie(cName, value, exdays) {
var exdate = new Date();
exdate.setDate(exdate.getDate() + exdays);
var c_value = escape(value) + ((exdays == null) ? "" : "; expires=" + exdate.toUTCString());
document.cookie = cName + "=" + c_value;
}
my doubt is does it work in all signalr version? if not then how to handle it in new version specially for not generate a new connection id if page gets refreshed. looking for suggestion.
if we work with Persistent connection class instead of hub then what happen.....in this case connection id will persist if we refresh the page at client side? please guide.
thanks
SignalR allows you to send messages to a user via their IPrincipal.Identity.Name. Just use Clients.User(userName) instead of Clients.Client(connectionId).
If you for some reason cannot address a user using their IPrincipal.Identity.Name you could create your own IUserIdProvider. This is the replacement for IConnectionIdFactory which no longer exists in SignalR >= 1.0.0.
The equivalent IUserIdProvider would look like this:
public class MyConnectionFactory : IUserIdProvider
{
public string GetUserId(IRequest request)
{
if (request.Cookies["srconnectionid"] != null)
{
return request.Cookies["srconnectionid"];
}
return Guid.NewGuid().ToString();
}
}
public class Startup
{
public void Configuration(IAppBuilder app)
{
var idProvider = new MyConnectionFactory();
GlobalHost.DependencyResolver.Register(typeof(IUserIdProvider), () => idProvider);
app.MapSignalR();
}
}
public class MyHub : Hub
{
public Task Send(string userName, string message)
{
return Clients.User(userName).receive(message);
}
}
It would be really trivial to spoof a user given this MyConnectionFactory. You could make it more secure by using an HMAC.
Ideally you would just use the default IUserIdProvider which retrieves the user ID from IRequest.User.Identity.Name.
The user id provider doesn't work, because the connection id doesn't come from it. I implemented a solution (https://weblogs.asp.net/ricardoperes/persisting-signalr-connections-across-page-reloads) that uses a pseudo-session id stored in session storage. Every SignalR connection id is then mapped to this pseudo-session id.

asp.net mvc global variables without cookies and session[""]

Is there any method for storing global variables without using cookies or session[""] in asp.net mvc ?
I know that cookies and session[""] have some disadvantages and I want to use the best method if exit.
If they are indeed global variables, you should implement the singleton pattern and have an Instance globally accessible that holds your variables.
Here is a simple example:
public sealed class Settings
{
private static Settings instance = null;
static readonly object padlock = new object();
// initialize your variables here. You can read from database for example
Settings()
{
this.prop1 = "prop1";
this.prop2 = "prop2";
}
public static Settings Instance
{
get
{
lock (padlock)
{
if (instance == null)
{
instance = new Settings();
}
return instance;
}
}
}
// declare your global variables here
public string prop1 { get; set; }
public string prop2 { get; set; }
}
The you can use them in your code like this:
var globalvar1 = Settings.Instance.prop1;
This class with its variables will be initialized only once (when the application starts) and it will be available in your application globally.
Basically you have following options:
Cookies - valid as long as you set, must be allowed by client's browser, can be deleted by user, stored on user's PC.
Session - valid for all requests, not for a single redirect, stored on server.
ViewData - after redirect it's cleared (lives only during single request).
TempData - it's useful for passing short messages to view, after reading a value it's deleted.
ViewBag - is available only during the current request, if redirection occurs then it’s value becomes null, is dynamic so you don't have intellisense and errors may occur only in runtime.
Here - http://www.dotnet-tricks.com/Tutorial/mvc/9KHW190712-ViewData-vs-ViewBag-vs-TempData-vs-Session.html - you can find fantastic article which describes them.
Sure: HttpContextBase.Application (no expiration) or HttpContextBase.Cache (with expiration). You can access the HttpContextBase instance through the HttpContext property of the Controller class.
So... HACK ALERT... There is no good way to do an MVC 5 or 6 web app using session variables that I have found (yet). MVC doesn't support Session variables or Cookies, which are implemented via session variables. Global variables will be set for ALL users, which is not how Session variables work.
However, you can store "session variables" based on the User.Identity.Name or the underlying User.Identity.Claims.AspNet.Identity.SecurityStamp into a database along with a timestamp and viola! You have implemented primitive session variables. I had a very specific need to save two weeks of programming by not interfering with the GUI that our user interface specialist had written. So I returned NoContent() instead of the normal View() and I saved my hacky session variable based on the user's login name.
Am I recommending this for most situations? No. You can use ViewBag or return View(model) and it will work just fine. But if you need to save session variables in MVC for whatever reason, this code works. The code below is in production and works.
To retrieve the data...
string GUID = merchdata.GetGUIDbyIdentityName(User.Identity.Name);
internal string GetGUIDbyIdentityName(string name)
{
string retval = string.Empty;
try
{
using (var con = new SqlConnection(Common.DB_CONNECTION_STRING_BOARDING))
{
con.Open();
using (var command = new SqlCommand("select GUID from SessionVariablesByIdentityName md where md.IdentityName = '" + name + "' and LastSaved > getdate() - 1", con))
{
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
retval = reader["GUID"].ToString();
}
}
}
}
}
catch (Exception ex)
{
}
return retval;
}
To save the data...
merchdata.SetGUIDbyIdentityName(User.Identity.Name, returnedGUID);
internal void SetGUIDbyIdentityName(string name, string returnedGUID)
{
RunSQL("exec CRUDSessionVariablesByIdentityName #GUID='" + returnedGUID + "', #IdentityName = '" + name + "'");
}
internal void RunParameterizedSQL(SqlConnection cn, SqlCommand cmd, object sqlStr)
{
string retval = string.Empty;
try
{
cn.Open();
cmd.ExecuteNonQuery();
cn.Close();
}
BTW: The SQL table (named SessionVariablesByIdentityName here) is fairly straightforward and can store lots of other things too. I have a LastSaved datetime field in there so I don't bother retrieving old data from yesterday. For example.

LiveAuthClient broken?

It seems very much that the current version of LiveAuthClient is either broken or something in my setup/configuration is. I obtained LiveSDK version 5.4.3499.620 via Package Manager Console.
I'm developing an ASP.NET application and the problem is that the LiveAuthClient-class seems to not have the necessary members/events for authentication so it's basically unusable.
Notice that InitializeAsync is misspelled aswell.
What's wrong?
UPDATE:
I obtained another version of LiveSDK which is for ASP.NET applications but now I get the exception "Could not find key with id 1" everytime I try either InitializeSessionAsync or ExchangeAuthCodeAsync.
https://github.com/liveservices/LiveSDK-for-Windows/issues/3
I don't think this is a proper way to fix the issue but I don't have other options at the moment.
I'm a little late to the party, but since I stumbled across this trying to solve what I assume is the same problem (authenticating users with Live), I'll describe how I got it working.
First, the correct NuGet package for an ASP.NET project is LiveSDKServer.
Next, getting user info is a multi-step process:
Send the user to Live so they can authorize your app to access their data (the extent of which is determined by the "scopes" you specify)
Live redirects back to you with an access code
You then request user information using the access code
This is described fairly well in the Live SDK documentation, but I'll include my very simple working example below to put it all together. Managing tokens, user data, and exceptions is up to you.
public class HomeController : Controller
{
private const string ClientId = "your client id";
private const string ClientSecret = "your client secret";
private const string RedirectUrl = "http://yourdomain.com/home/livecallback";
[HttpGet]
public ActionResult Index()
{
// This is just a page with a link to home/signin
return View();
}
[HttpGet]
public RedirectResult SignIn()
{
// Send the user over to Live so they can authorize your application.
// Specify whatever scopes you need.
var authClient = new LiveAuthClient(ClientId, ClientSecret, RedirectUrl);
var scopes = new [] { "wl.signin", "wl.basic" };
var loginUrl = authClient.GetLoginUrl(scopes);
return Redirect(loginUrl);
}
[HttpGet]
public async Task<ActionResult> LiveCallback(string code)
{
// Get an access token using the authorization code
var authClient = new LiveAuthClient(ClientId, ClientSecret, RedirectUrl);
var exchangeResult = await authClient.ExchangeAuthCodeAsync(HttpContext);
if (exchangeResult.Status == LiveConnectSessionStatus.Connected)
{
var connectClient = new LiveConnectClient(authClient.Session);
var connectResult = await connectClient.GetAsync("me");
if (connectResult != null)
{
dynamic me = connectResult.Result;
ViewBag.Username = me.name; // <-- Access user info
}
}
return View("Index");
}
}

Resources