what is the best way to save data in a state-machine-like application?
How the application works:
There are multiple states, like Loging, MainMenu, Registration, etc. There is a loop that working until the state reaches Exit.
while(currentState != States.Exit)
{
switch (currentState)
{
case Login:
// Do everything needed for the login, including showing the Login-Window.
LoginProcess();
break;
case MainMenu:
MainMenuProcess();
break;
// Etc...
}
}
The problem:
I want to save data in between these processes. For example I want to have a User Object after the login containing everything that has to do with the user. There are many variables I could have to save and they are not always initialized (i.e. the User can only exist after login).
How it's done until now:
Right now there are just "public" members that can be null if the respective process has not started. They are defined in the class of the State-Machine loop. This can get messy easily.
Expectations:
I would like to have a way to do this data-saving in a clean way. Maybe even extract it from the state-machine or something similar. Maybe there is a way to restrict processes to access members they should not change?
Thanks in advance.
You could persist it to a database, or serialize your model into a JSON object, this object could be saved, then loaded up later and deserialized into your domain model.
You can also consider sagas, there are frameworks that support the notion of this and might help solve the problem.
http://www.cs.cornell.edu/andru/cs711/2002fa/reading/sagas.pdf
Instead having every possible field for each process in the state machine, I would create small POCO objects which are in charge of passing information to each step of the state machine.
For example:
public class LoginProcessInfo
{
private readonly string username;
private readonly string password;
public LoginProcessInfo(string username, string password)
{
this.username = username;
this.password = password;
}
}
Now, with each iteration of the state, pass the relevant arguments to the method. You could either create a new one each time, or pool the created object if you're simply re-using them:
while(currentState != States.Exit)
{
switch (currentState)
{
case Login:
LoginProcess(new LoginProcessInfo(username, password));
break;
case MainMenu:
MainMenuProcess();
break;
}
}
Related
This is my scenario: I'm working on an e-commerce system (ASP.NET MVC) which users can 'Like' products. I have a method in my web service (.svc) which if the user has already liked that product, it removes the like, and if the user has not, it adds a like for product.
I have an issue here: I call the 'Like' method via Ajax asynchronously, if the user clicks on the like button multiple times continuously, so, the method is called multiple times continuously. since this method is not thread-safe, I might have issues here. (For example, it might insert the like multiple times for one single user in the database)
I've been trying to make it Thread-safe by using lock statement, but apparently, It still doesn't work.
This is my web service method:
private static Object productLikeLock = new Object();
[WebMethod, ScriptMethod(ResponseFormat = ResponseFormat.Json)]
public bool ToggleProductLike(int productId)
{
try
{
if (User.Identity.IsAuthenticated)
{
lock (productLikeLock)
{
if (/* User has already liked the product */)
{
// Remove it from the database
return true;
}
else
{
// Add it to the database
return true;
}
}
}
return false;
}
catch (Exception ex)
{
return false;
}
}
Can anyone help me and let me know what is wrong with my code and approach?
Thanks in advance
What you really want is the lock to only be active for the user, not for the whole application. A private static is not a good fit for the lock here. You could potentially use a Dictionary<UserId, LockObject> for the lock, but this is still not great.
What I would suggest is not trying to implement this function as a toggle. Toggling state will always get you into this sort of trouble.
Try re-engineering the application to have an explicit Like and Dislike action. That way you don't have to lock anything.
Due to lack of tutorials and information i am unable to find how i can save the information in bots. Lets say i ask user to make a selection like this:
public enum City
{
Cleveland, Columbus, Kentucky, Mason, Akron
};
[Serializable]
public class SandwichOrder
{
[Prompt("Please select what {&} you are in? {||}")]
public City? City;
public static IForm<SandwichOrder> BuildForm()
{
return new FormBuilder<SandwichOrder>()
.Message("Welcome to the my bot!")
.Build();
}
};
I just want to ask for city once how can i do that? How can i preserve the value of user selection and only call this method if it is first user interaction.
Controller class:
internal static IDialog<SandwichOrder> MakeRootDialog()
{
return Chain.From(() => FormDialog.FromForm(SandwichOrder.BuildForm));
}
[ResponseType(typeof(void))]
public virtual async Task<HttpResponseMessage> Post([FromBody] Activity activity)
{
if (activity != null)
{
// one of these will have an interface and process it
switch (activity.GetActivityType())
{
case ActivityTypes.Message:
await Conversation.SendAsync(activity, MakeRootDialog);
break;
}
}
var response = Request.CreateResponse(HttpStatusCode.OK);
return response;
}
The SDK includes several ways of persisting data relative to a user or conversation:
userData stores information globally for the user across all conversations.
conversationData stores information globally for a single conversation. This data is visible to everyone within the conversation so care should be used to what’s stored there. It’s disabled by default and needs to be enabled using the bots persistConversationData setting.
privateConversationData stores information globally for a single conversation but its private data for the current user. This data spans all dialogs so it’s useful for storing temporary state that you want cleaned up when the conversation ends.
dialogData persists information for a single dialog instance. This is essential for storing temporary information in between the steps of a waterfall.
Bots built using Bot Builder are designed to be stateless so that they can easily be scaled to run across multiple compute nodes. Because of that you should generally avoid the temptation to save state using a global variable or function closure. Doing so will create issues when you want to scale out your bot. Instead leverage the data bags above to persist temporary and permanent state.
More info here:
https://docs.botframework.com/en-us/node/builder/guides/core-concepts/#adding-dialogs-and-memory
Once authenticatated I use HttpContext.Current.User.Identity.Name; to ensure user is authorized to view a part of my site.
When I access certain parts of my site I need to get the User and get which context (organization they are logged into), url would be something like settings/supercompany/profile. where supercompany is the current context.
For each user I would need to check if they are admin in that company or a general user, if a general user then they cannot see certain things.
public class SettingsApi
{
private readonly string _userId;
private readonly string _contextId;
public SettingsApi(string userId, string contextId)
{
_userId = userId;
_contextId = contextId;
}
}
If I instantiate the class above from a controller (post or get), would caching somehow mess things up? Users role changed and I don't pick it up? Would something like the below work well?
var settings = new SettingsApi(HttpContext.Current.User.Identity.Name, currentContextId);
settings.IsAdmin();
Note: I would have used attributes to authorize but my requirements are I need to pick out the currentContext from the URL plus I need to use the class above elsewhere in my code.
Update
AuthorizeAttribute works well with caching, but the method used to authorize i.e.
protected override bool AuthorizeCore(HttpContextBase httpContext)
Will not hand me back an instance of the class I need...
Update 2 I don't want this class or an instance of this class to be cached in anyway, everytime I ask for a new instance I don't mind fetching one from the DB...
My Question - is the way I am coding ok? Will my user and his permissions NOT be cached?
It is possible, if you're not careful, to let MVC cache the output of the first request by an authenticated user. I use VaryByCustom and the current identity's name.
[OutputCache(VaryByCustom="user")]
public class SomeController : Controller
{
// etc.
}
In my Global.asax.cs I define:
public override string GetVaryByCustomString(HttpContext context, string custom)
{
if (custom.Equals("user", StringComparison.OrdinalIgnoreCase))
{
return context.User.Identity.IsAuthenticated ? context.User.Identity.Name : string.Empty;
}
return base.GetVaryByCustomString(context, custom);
}
If you are proposing to add instances of the SettingsApi to the cache then it definitely will not work as caching is app wide and so all users will end up sharing the same SettingsApi. Using the OutputCache should be fine (as long as you dont do something like put userid in a hidden field and use [OutputCache(VaryByCustom="user")] or similar).
If you are looking to cache the SettingsApi you should do so through SessionState which is per user/session and wont affect the authentication.
I was wondering if anyone could help me understand if what I am doing is a lot of overhead or not. It is currently working but I am not sure if this could slow down the site or not.
I have a workflowobj class in which i set all the session variables. This class in instantiated on the pages that need it:
WorkFlowObj wfo = new WorkFlowObj(this.Session, this.Response);
wfo.VendorRedirect();
I need this because I need to be able to keep track of session variables and at the same time be able to keep track of a more complicated page workflow in one place. This solution already already works for me, but the only problem is that I am not sure if passing around the session and the response objects creates a lot of OVERHEAD. Can anyone tell me if this is terribly inefficient?? Below is the code for the workflowobj class.
public class WorkFlowObj
{
private System.Web.SessionState.HttpSessionState _pagesession;
private HttpResponse _HttpResponse;
private int _userid;
private string _vendorname;
///private other vars here
}
public int UserID
{
get
{
return _userid;
}
}
public WorkFlowObj(System.Web.SessionState.HttpSessionState pagesession, HttpResponse _response)
{
_pagesession = pagesession;
_HttpResponse = _response;
Initialize();
}
private void Initialize()
{
//initialize variables from session
_userid=_pagesession["userid"].ToString();
}
public void VendorRedirect()
{
switch (this._vendorname)
{
case "1":
this._HttpResponse.Redirect(page1);
break;
case "2":
this._HttpResponse.Redirect(page2);
break;
//etc
default:
//dostuff;
break;
}
}
}
As Rick says, I wouldn't create dependencies to System.Web in your middle-tier objects if you can avoid it.
But if you can't avoid it, you can avoid passing around the Session object by using the static System.Web.HttpContext class. This lets you do something like:
userid = (String)System.Web.HttpContext.Current.Session["userid"];
As long as it's executing on the same thread (and therefore in the same context) as the request from the browser.
I would not create dependencies to System.Web in your workflow objects, just pass the variables that the workflow objects need to make decision and execute business logic. There is no overhead passing objects around, they are just pointers under the hood.
One issue I could see happening is accidental use of statics in another layer that get tied to your Page state, thus not allowing the GC to clean up ie: classic out of memory exception or app pool recycle.
I've never seen this done but I had an idea of doing authorization in a more purely OO way. For each method that requires authorization we associate a delegate. During initialization of the class we wire up the delegates so that they point to the appropriate method (based on the user's rights). For example:
class User
{
private deleteMemberDelegate deleteMember;
public StatusMessage DeleteMember(Member member)
{
if(deleteMember != null) //in practice every delegate will point to some method, even if it's an innocuous one that just reports 'Access Denied'
{
deleteMember(member);
}
}
//other methods defined similarly...
User(string name, string password) //cstor.
{
//wire up delegates based on user's rights.
//Thus we handle authentication and authorization in the same method.
}
}
This way the client code never has to explictly check whether or not a user is in a role, it just calls the method. Of course each method should return a status message so that we know if and why it failed.
Thoughts?
This is basically the null object pattern for authorization. It's an interesting idea if you can figure out a way to design StatusMessage such that the calling code doesn't need special cases. For instance, for certain actions, you'll want to indicate "You can't do that as a guest, but would you like to login or sign up for an account?" So certain StatusMessages might need to redirect to a login/sign up page.