Maintain state of the object for same browser session - c#

Following is my ASP.Net Web API controller code. Here as you can see there's a private class object, BL, that is used and both the Get methods implemented. For the first method FetchAllDashboardByUserId(int userId), I pass the user id so that the BL object can be initiated. Within the same browser session, if the second get method is called, then I do not want to pass the userid, since BL should be by default initiated, but currently that's not the case. BL is null for the second method, so I have to add userid to the call to the method - GetCardDataUI(int userId, int dashBoardID, int cardID). My question is how to avoid it. Is my thinking incorrect that:
A single open browser where I make the Consecutive call to the following URLs are a single session:
webapi/ViewR?userId=1
webapi/ViewR?userId=1&dashBoardID=1&cardID=3
I don't want to pass the userId in the second URL. Please note that if I declare class object as static then it works as expected, but that's not what I want, it has to be tied to a user:
public class ViewRController : ApiController
{
// BL object for a user
private static BL accessBL = null;
// HTTP GET for Webapi/ViewR (Webapi - name of API, ViewR - Controller with implementation)
[AcceptVerbs("Get")]
public List<DashboardUI> FetchAllDashboardByUserId(int userId)
{
if (accessBL == null)
accessBL = new BL(userId);
// Use BL object for entity processing
}
[AcceptVerbs("Get")]
public CardDataGetUI GetCardDataUI(int userId, int dashBoardID, int cardID)
{
if (accessBL == null)
accessBL = new BL(userId);
// Use BL object for entity processing
}
}
How I want the second method implementation to be:
[AcceptVerbs("Get")]
public CardDataGetUI GetCardDataUI(int dashBoardID, int cardID)
{
// Use BL class object created in last call for entity processing
// Should not pass userid again
}

You can easily store data in Session:
... first request:
Session["userID"] = userID;
... next request:
int userID = (int)Session["userID"]; // should check for null first, but you get the idea...
But keep the following points in mind:
Session variables are stored as objects, so you'll need to cast and/or type-check
Session variables can be null
Session expires after a configurable (in web.config) about of time
Default session state is in-memory, meaning if the app pool is restarted session state is gone - you can store session in files or databases to keep longer
Session doesn't scale out unless you use persistent storage (file, database)
Objects stored in persistent storage must be serializable

Related

Domain Driven Design | Invoking external services to enrich entity properties

For study purposes I have designed a product-service which allows to perform CRUD operations against a product entity made up by the following properties:
string ID
string Name
decimal ListPrice
decimal FinalPrice
the ID, the Name and the ListPrice are stored in the product-service's database, while the FinalPrice is retrieved by an external service, the price-card-service, which exposes the following endpoint: GET /price-card-by-id/{product-id} which returns an object containing the product's final price.
The Create and Update operations of the product-service don't allow to do anything with the product's FinalPrice, but when retrieving a Product it will have its FinalPrice if returned by the price-card-service
By now the logic of retrieving and setting the FinalPrice is handled withing the GetProductByIdRequestHandler:
public class GetProductByIdRequestHandler : IAppRequestHandler<GetProductByIdRequest, GetProductByIdResponse>
{
private readonly IProductRepository _productRepository;
private readonly PriceCardServiceClient _priceCardServiceClient;
public GetProductByIdRequestHandler(IProductRepository productRepository, PriceCardServiceClient priceCardServiceClient)
{
_productRepository = productRepository;
_priceCardServiceClient = priceCardServiceClient;
}
public async Task<OneOf<GetProductByIdResponse, IError>> Handle(GetProductByIdRequest request, CancellationToken cancellationToken)
{
var product = await _productRepository.GetById(request.ProductId);
if (product == null)
{
return new NotFoundError
{
Message = $"Could not find product {request.ProductId}"
};
}
var priceCardList = await _priceCardServiceClient.ActiveAsync(product.Id, cancellationToken);
if (!priceCardList.Items.Any())
{
product.FinalPrice = product.Price;
return new GetProductByIdResponse(product);
}
var priceCard = priceCardList.Items.First();
if (priceCard.NewPrice < 0)
{
return new PriceCardNewPriceLessThanZeroError
{
Message = $"Price {priceCard.NewPrice} for PriceCard {priceCard.Id} for Product {product.Id} must be greater or equal to 0"
};
}
product.FinalPrice = ProductPrice.From(System.Convert.ToDecimal(priceCard.NewPrice));
return new GetProductByIdResponse(product);
}
}
I'm wondering if DDD promotes a different way for handling this kind of operations, perhaps by injecting the PriceCardServiceClient into the Product entity and creating a readonly FinalPrice property which handles the logic.
What are the possible approches?
It depends from your domain, mainly.
Is the Product, as a domain object, requiring this information? Then you could:
store it directly, requiring it before via the service,
obtain it every time you need the entity.
The first solution requires a 'update' process, that at least for each valid Product, aligns the stored value with the current valid one. The second one gives you the current value every time, but it also makes that part of the domain dependent from another service. In other words, you could not get the Product if the service PriceCardServiceClient is not responding. Or, you should manage this special case, once again with extra code.
If, instead, your domain entity doesn't require it, if you load/show/manage it is just related to the UI. Hence, using the DDD will not cause any change in the way you get this information.

Data in memory is re-used instead of executing a new sql query

I have a WCF service FooService.
My service implements a method LoginAsync which takes a User object as parameters.
public async Task<Token> LoginAsync(User user)
{
var result = await _userManager.GetModelAsync(user.Uname, user.Pword);
if (result != null && result.Id > 0)
return await _tokenManager.GetModelAsync(result);
return null;
}
Inside this method we call _userManager.GetModelAsync(string, string) which is implemented as follows:
public async Task<User> GetModelAsync(string username, string password)
{
var result =
(from m in await _userRepo.GetModelsAsync()
where m.Uname.Equals(username, StringComparison.InvariantCulture)
&& m.Pword.Equals(password, StringComparison.InvariantCulture)
select m).ToList();
if (result.Any() && result.Count == 1)
{
var user = result.First();
user.Pword = null;
return user;
}
return null;
}
To mention it again: this is all server-side code.
I never want my service to send back the Pword field, even though it is not clear text. I just don't want that information to be on my client-side code.
This is why I'm setting this property to NULL when I found a User by comparing username and password.
Here's how _userRepo.GetModelAsync() is implemented (don't get confused with _userManager and _userRepo):
public async Task<IList<User>> GetModelsAsync()
{
return await MediaPlayer.GetModelsAsync<User>(_getQuery);
}
private readonly string _getQuery = "SELECT ID, Uname, DateCreated, Pword FROM dbo.[User] WITH(READUNCOMMITTED)";
And here MediaPlayer.GetModelsAsync<T>(string, params IDbParameter[])
public static async Task<IList<T>> GetModelsAsync<T>(string query, params DbParameter[] parameters)
{
IList<T> models;
using (SqlConnection con = new SqlConnection(Builder.ConnectionString))
using (SqlCommand command = Db.GetCommand(query, CommandType.Text, parameters))
{
await con.OpenAsync();
command.Connection = con;
using (SqlDataReader dr = await command.ExecuteReaderAsync(CommandBehavior.SequentialAccess))
models = ReadModels<T>(dr);
}
return models;
}
This code works fine the first time executing it after publishing or restarting this service (the service is consumed by a WPF application).
But calling FooService.LoginAsync(User) a second time, without publishing or restarting the service again, it will throw a NullReferenceException in my _userManager.GetModelAsync LINQ because Pword is NULL.
Which is really strange to me, because as you can see there is no logic implemented where my data is explicit stored in memory.
Normally my code should execute a sql query everytime calling this method, but somehow it doesn't. It seems like WCF does not get its data from my database, instead re-uses it from memory.
Can somehow explain this behavior to me and what I can do against it?
Edit 26.09.2018
To add some more details:
The method _userManager.GetModelAsync(string, string) always gets called, same for _userRepo.GetModelsAsync. I did some file logging at different points in my server-side code. What I also did, is to take the result of _userRepo.GetModelsAsync, iterated through every object in it and logged Uname and Pword. Only Pword was NULL (did this logging before doing my LINQ).
I also logged the parameters _userManager.GetModelAsync(user.Uname, user.Pword) receives. user.Uname and user.Pword are not NULL.
I just noticed that this question was reposed. My diagnosis is the same:
What I am thinking right now, is that my service keeps my IList with the cleared Pword in memory and uses it the next time without performing a new sql query.
LINQ to SQL (and EF) reuse the same entity objects keyed on primary key. This is a very important feature.
Translate will give you preexisting objects if you use it to query an entity type. You can avoid that by querying with a DTO type (e.g. class UserDTO { public string UserName; }).
It is best practice to treat entity objects as a synchronized mirror of the database. Do not make temporary edits to them.
Make sure that your DataContext has the right scope. Normally, you want one context per HTTP request. All code inside one request should share one context and no context should ever be shared across requests.
So maybe there are two issues: You modifying entities, and reusing a DataContext across requests.

VaryByCustom - Data being shown to wrong users

I'm using Windows Auth in an Intranet setting to Cache information pulled from Active Directory. The purpose of the Cache is to speed up the page, as reading from AD is not particularly fast, and doesn't need to be done every single time. (The data doesn't change all that often)
To do this, i'm setting a custom key in HttpContext.Application.
This is the code located in Global.asax to handle VaryByCustom:
public override string GetVaryByCustomString(HttpContext context, string arg)
{
System.Diagnostics.Debug.Print("GetVaryByCustomString : " + context.Application["BrowsingSession_Key"].ToString());
if (arg == "BrowsingSession_Key")
{
object o = context.Application["BrowsingSession_Key"];
if (o == null)
{
o = Guid.NewGuid();
context.Application["BrowsingSession_Key"] = o;
}
return o.ToString();
}
return base.GetVaryByCustomString(context, arg);
}
In my BaseController (Inherited by all my controllers):
protected override void Initialize(RequestContext requestContext)
{
base.Initialize(requestContext);
//Custom Cache Initiation Variable
if (HttpContext.Application["BrowsingSession_Key"] == null)
{
HttpContext.Application["BrowsingSession_Key"] = Guid.NewGuid();
System.Diagnostics.Debug.Print("BaseController.Initialize : " + HttpContext.Application["BrowsingSession_Key"].ToString());
}
}
And finally, in my method inside a controller:
[OutputCache(Duration = 300, VaryByCustom = "BrowsingSession_Key", Location = OutputCacheLocation.Server)]
public ActionResult Index(HomeViewModel model)
//...
return View("index", model);
}
The issue is simple - the first person to view the page has their info cached, and the Guid for BrowsingSession_Key is set.
However, the next user visits the page within the 5 minute window, and reaches the last users cached content.
As you can see - i'm attempting to give each user a unique BrowsingSession_Key, so that they get their own cached content.
I'm using VaryByCustom so that i can quickly invalidate the cache by assigning a new BrowsingSession_Key (Guid) to that user - and pull a non-cached copy of a page for them.
Can you see what's going wrong here?
From my testing - it seems Initialize is often called, as is GetVaryByCustomString, in the places you'd expect them to be called. However, i can't run debug as multiple users, so i can't see why they're getting the same Guid and the same outputcache.
As it turns out, Application-level variables are not a good place to be storing per-user information, even temporarily.
In the end, i swapped over to using a cookie with a stored GUID, and invalidate the cookie (reset the GUID, or delete & Re-create). This meant multiple users were then able to use the site, with GetVaryByCustomString instead handling information stored in the Cookie.
This works for me - but i need to take into account the possibility of users swapping cookies, so will look further into encryption options.
For now - the answer is - Don't use Application-level variables this way, they're not suited to such tasks.

How to send an object from one view to the next

Consider the following scenario:
You are in a view with a lot of data (i.e. a view with a ListBox full of items). The data has been downloaded from an external data source, and contains a lot of properties that's not exposed in the current view. You'd like to make a detailed view to show this information.
When you have wired up your ListBoxItems to navigate to your new view with an attached "id=" in the navigation URL, you can easily query your data source (i.e. a REST-service) for the data again with the given ID-parameter. But the data is already available on the parent view - so how could you send this data in some way to the next view - skipping another call to your data source and speeding up your application?
I'm sure there are several ways to do this, but this is the easiest way to accomplish it without using any external libraries.
Create a Utility class - NavigationUtility (or whatever) - and implement the following structure:
public static class NavigationUtility
{
// The object to send
private static object passedObject = null;
// A method to fetch the object
public static object GetObject() {
// implementation below
}
// Utility method to check if an object was passed
public static bool HasObject()
{
return passedObject != null;
}
// A method to navigate to a page
public static void Navigate(string url, object obj = null)
{
// implementation below
}
}
This is the interface you'll be implementing. It has a private variable that keeps your object safe while transitioning between views, and methods for both navigating and fetching the information sent.
Now there are a few things you need to consider in order to implement this in a correct way.
You must only be able to fetch the passed object once - otherwise careless use can make the wrong object show up in the wrong part of the program.
In order to use this instead of the NavigationService.Navigate(Uri) method for navigating throughout your application, it must also be able to handle situations where no object needs to be sent.
So let's look at the first method in our interface - GetObject - and how it's implemented:
public static object GetPassedObject()
{
var obj = passedObject;
passedObject = null;
return obj;
}
As you see, we take care of requirement #1 easily by nulling out the internal passedObject for each time the property is fetched. This would work in the following way (in the receiving view):
NavigationUtility.HasObject(); // returns true if an object was sent
var data = NavigationUtility.GetObject() as SomeModel; // fetches the object and casts it
NavigationUtility.HasObject(); // returns false always;
Now to the funny bit - implementing the Navigate-method:
public static void Navigate(string url, object obj = null)
{
// Saves the object
passedObject = obj;
if( url != null && url.length > 0 )
{
// Creates the Uri-object
Uri uri = new Uri(url, UriKind.Relative);
// Navigates the user (notice the funky syntax - so that this can be used from any project
(Application.Current.RootVisual as PhoneApplicationFrame).Navigate(uri);
}
}
And that's it!
with the help creating your data with application object you can easily navigate your object data to another view when you making call of REST-service be sure that your data will set to your application object too
you can also use json object and set it to Any global object

Synchronizing Access to a member of the ASP.NET session

I'm building a Javascript application and eash user has an individual UserSession. The application makes a bunch of Ajax calls. Each Ajax call needs access to a single UserSession object for the user.
Each Ajax call needs a UserSession object.
Data in the UserSession object is unique to each user.
Originally, during each Ajax call I would create a new UserSession object and it's data members were stored in the ASP.NET Session. However, I found that the UserSession object was being instantiated a lot. To minimize the construction of the UserSession object, I wrapped it in a Singleton pattern and sychronized access to it.
I believe that the synchronization is happening application wide, however I only need it to happen per user. I saw a post here that says the ASP.NET cache is synchronized, however the time between creating the object and inserting it into the cache another Thread could start construction it's another object and insert it into the cache.
Here is the way I'm currently synchronizing access to the object. Is there a better way than using "lock"... should be be locking on the HttpContext.Session object?
private static object SessionLock = new object();
public static WebSession GetSession
{
get
{
lock (SessionLock)
{
try
{
var context = HttpContext.Current;
WebSession result = null;
if (context.Session["MySession"] == null)
{
result = new WebSession(context);
context.Session["MySession"] = result;
}
else
{
result = (WebSession)context.Session["MySession"];
}
return result;
}
catch (Exception ex)
{
ex.Handle();
return null;
}
}
}
}
You don't need to lock Session state access.
The physical values of a session state are locked for the time needed to complete a request. The lock is managed internally by the HTTP module and used to synchronize access to the session state.
http://msdn.microsoft.com/en-us/library/aa479041.aspx
In general, you don't need this kind of code for asp.net session access, since access to each session is limited to a single user. The only reason I can think of for locking access to your session object is if you expect to have multiple simultaneous ajax requests, and even so, I think asp.net would synchronize the access for you.
If you do decide to lock, you only really need to do it if your session object is null:
if (context.Session["MySession"] == null) {
lock(SessionLock) {
if (context.Session["MySession"] == null) {
context.Session["MySession"] = new WebSession(context); // try-catch block removed for clarity (and my laziness)
}
}
}
return (WebSession)context.Session["MySession"];

Categories

Resources