I have a controller in ASP.NET like this:
public class FileUploadController : Controller
{
// ...
static List<ThreePartKey> uploadedFiles = new List<ThreePartKey> ();
// ...
public ActionResult Index ( )
{
// ...
}
[HttpPost]
public ActionResult Index (HttpPostedFileBase file,
string selectedOrgName,
string selectedCatName)
{
// ...
uploadedFiles.Add(new ThreePartKey {
orgname = selectedOrgName,
catname = selectedCatName,
filename = fileNameNoExtension });
ViewBag.uploadedFiles = uploadedFiles;
return View();
}
}
where the second Index function uploads a file that the user chooses and adds its information to a list of uploaded files. That list information is used to build out an HTML table. The reason I ended up prefixing List<ThreePartKey> uploadedFiles with static is because without it my list would only have the last uploaded file each time I invoked Index.
How long does that list stay in memory? I was hoping for it to correspond to user sessions, but I'm not sure.
Static variables are global to an AppDomain and last the life of the AppDomain. The value wouldn't be user-specific and would basically stick around until the web app restarts in the case of ASP.NET.
It's also worth noting that static variables aren't necessarily thread safe, so care should be taken when manipulating the variable. In your case, the List<ThreePartKey> is NOT inherently thread safe, so you should accommodate for that in your code (unless you change the implementation to use a session variable or something).
Related
I'm making an educational game (Windows 10 UWP, C# + XAML) and I need to store user information (in particular, their current score) and retrieve it when they start the app again. I've found a way to do this (see code below) but I have no idea if this is a normal solution to this problem. I'm currently creating a txt file and storing and retrieving data in/from it. Are there more common, or simpler ways to do this?
Here's what I'm currently doing:
Create the file:
StorageFolder storageFolder = ApplicationData.Current.LocalFolder;
StorageFile sampleFile = await storageFolder.CreateFileAsync("nameOfTextFile.txt", CreationCollisionOption.OpenIfExists); //other options are ReplaceExisting
Open the file:
StorageFolder storageFolder = ApplicationData.Current.LocalFolder;
StorageFile sampleFile = await storageFolder.GetFileAsync("nameOfTextFile.txt");
Write text to the file:
await FileIO.WriteTextAsync(sampleFile, "Put the added text here");
Read text from the file:
string someVariableName = await FileIO.ReadTextAsync(sampleFile);
-Thanks in advance for any help!!
While the file-based approach is valid, there are easier ways, at least for simple data: You can use roaming (or local) settings. Roaming settings are roamed between devices, as long as their size don't exceed 64K, and would carry the score from the user's desktop to the user's phone, for example. Local settings stay on the machine.
Settings are easy to use:
IPropertySet propertySet = ApplicationData.Current.RoamingSettings.Values;
// Get previous score (or 0 if none)
int score = (int)(propertySet["Score"] ?? 0);
// ...play game...
// Set updated score:
propertySet["Score"] = score;
The way I go about doing projects and settings like this is creating a propery setting in Visual Studio, then Setting and Getting the setting / Value.
You can access this by going to the application properties.
This allows access to read,write, and save information / onload restore information.
Some Informational Links:
https://msdn.microsoft.com/en-us/library/bb397755(v=vs.110).aspx
and (Suggested)
https://msdn.microsoft.com/en-us/library/aa730869(v=vs.80).aspx
OK, so here goes an example of using a class to store your settings in.
There are many, many more ways you could do this. Too many to list.
Create a settings class:
public class YourSettingsClass
{
public string UserFirstName { get; set; }
public string UserLastName { get; set; }
public string UserScore { get; set; }
}
Create an AppSettings helper
public AppSettings
{
private static YourSettingsClass _settings = new YourSettingsClass();
public static string UserFirstName
{
get { return _settings.UserFirstName; }
set { _settings.UserFirstName = value; }
}
public static string UserLastName
{
get { return _settings.UserLastName; }
set { _settings.UserLastName = value; }
}
public static string UserScore
{
get { return _settings.UserScore; }
set { _settings.UserScore = value; }
}
public static void SaveSettings()
{
// Now, use your "settingsfile.xml" (or whatever you're saving as)
// to write your settings to from your _settings static field object.
// I'll let you have a play as to how you want to do this...
}
public static void LoadSettings()
{
YourSettingsClass tempSettingsClass = new YourSettingsClass();
// Now, use your "settingsfile.xml" (or whatever you've saved it as)
// to load in your settings and assign to your tempSettingsClass variable.
// I'll let you have a play as to how you want to do this...
// Assign the settings from your loaded object.
_settings = tempSettingsClass;
}
}
Now, from any other class, you can call AppSettings.LoadSettings(). You could do this on App Startup, or on-demand.
When you've loaded the settings in, just reference AppSettings.UserFirstName or whatever property you want to either get the value or set the value.
When you're ready to, you can then save the settings back to the XML file on disk, through AppSettings.SaveSettings().
I've purposely omitted the code for loading and saving from the storage, and for se/deserializing class objects as I haven't got any UWP components on this PC and I've done this all from memory so I don't want to put anything in to throw you off.
Plus it's a little more learning (even trial/error) for you to do.
Lastly
In the getters for your AppSettings static properties you could also do a null or string.IsNullOrWhiteSpace check for the _settings' property in question, and call the LoadSettings() method if so.
This would save you having to manually call it in-code elsewhere.
Useful links
XmlSerializer and how to use the Serialize method
All about what you can do with the FileIO.WriteTextAsync
Not an article, but a similar question: UWP C# Read & Write XML File
I really hope this helps, somewhat.
Good luck!
public class MusicController : Controller
{
User currentUser;
public PartialViewResult UploadMusic()
{
return PartialView("_UploadMusic");
}
[HttpPost]
public ActionResult UploadMusic(List<HttpPostedFileBase> files)
{
EntityDBContext db = new EntityDBContext();
List<Song> uploadedSongs = new List<Song>();
foreach (var file in files)
{
if (file != null)
{
string songName = Path.GetFileName(file.FileName);
byte[] songAsBytes = new byte[file.ContentLength];
using (BinaryReader br = new BinaryReader(file.InputStream))
{
songAsBytes = br.ReadBytes(file.ContentLength);
}
//Save new record in database
Song song = new Song
{
SongName = songName,
SongBytes = songAsBytes
};
uploadedSongs.Add(song);
}
}
string userName = User.Identity.Name;
currentUser = db.Users.Where(x => x.Username == userName).First();
currentUser.UserSongs = uploadedSongs;
return ShowSongs(currentUser.UserSongs);
}
public ActionResult ShowSongs(List<Song> UserSongs)
{
return View("ShowSongs", UserSongs);
}
public ActionResult Publish()
{
EntityDBContext db = new EntityDBContext();
foreach (var song in currentUser.UserSongs)
{
if (song != null)
{
db.Songs.Add(song);
db.SaveChanges();
}
}
return View();
}
}
ShowSongs view:
#model List<Vidafo.Models.Song>
#Html.ActionLink("Publish", "Publish")
The Problem
So I declare currentUser at the top of the controller. I then assign a value to that with this line here currentUser.UserSongs = uploadedSongs; This works fine but when the code goes into Publish() currentUser.UserSongs is null.
I need to have access to currentUser.UserSongs in more than one action method after assigning a value but it seems that it resets to null when it enters another action.
Object state isn't maintained across requests, that's not how web applications work. Every time a request is sent to the server, a new instance of the controller object is created. So any instance-level values are new.
In order to persist information across requests you need to persist it somewhere. For something like a user context, session state is a common choice. You'll probably want to wrap it in a common provider interface so as to not couple your controllers to an HTTP context, but at its core storing in session is simple:
HttpContext.Current.Session["someKey"] = someValue;
(You could even re-fetch from the database with each request. It's slightly less performant, but very simple and robust.)
Don't count out the ASP.NET identity system for this, though. ASP.NET is pretty good at abstracting a lot of this for you. You're already using it here:
string userName = User.Identity.Name;
Then you use that value to get the user from the database. You could extend the identity system to store a custom user object which fits your needs. But that's a larger scope effort outside of this question.
For this you can make use of TempData i.e. store value in TempData dictionary. One problem here is MVC doesn't sore value of variable during postback i.e. during different action of same controller or calling another controller for this you can use temporary varialble TempData as suggested.
I have a list of images like this:
public List<Image> imageList = new List<Image>();
I also have a picture class in order to collect and manipulate data about the images in the list:
public Class Pic {
// properties and stuff
}
And then I have a function that takes an integer as an argument. That integer corresponds to an image in the image list. What I want to do in the function is to check if an instance of the Pic class has been created for that particular image. If not, I want to create it, using the value of the variable passed into the function. The following code obviously doesn't work, but it shows what I want:
public void doStuffWithImage(int picNumber) {
// Check if instance called pic + picNumber exists
if(pic + picNumber.toString() == null) {
// Create an instance
Pic pic + picNumber.toString() = new Pic();
}
}
Suggestions on how to accomplish this?
It seems like you're trying to create individual variables pic1, pic2, etc. you'd be better off using a dictionary:
Dictionary<int, Pic> pics = new Dictionary<int, Pic>();
public void doStuffWithImage(int picNumber) {
// Check if instance called pic + picNumber exists
if(!pics.ContainsKey(picNumber)) {
// Create an instance
pics[picNumber] = new Pic();
}
}
You need to create a "registry" of known Pics. DIctionary<int,Pic> would be good collection to hold this registry. You need to store the registry itself somewhere - perhaps in the "factory" object that registers your pictures.
class PicFactory {
private readonly IDictionary<int,Pic> knownPics = new Dictionary<int,Pic>();
public Pic GetOrCreate(int id) {
Pic res;
if (knownPics.TryGetValue(id, out res)) {
return res;
}
res = new Pic(id.ToString()); // This assumes that Pic(string) exists
knownPics.Add(id, res);
return res;
}
}
This way of implementing a registry may be too primitive for your purposes - for example, if you need your registry to be concurrent, you would need to set up some sort if a locking scheme to protect the knownPics dictionary. The class that accesses pictures would need to create an instance of PicFactory, so that it could access pictures through the GetOrCreate(id) call.
If you are using .net 4.0 or more you can use Lazy type which:
Provides support for lazy initialization.
Which means that the object will be constructed not in the moment of declaration, but when first accessed.
So you can basically declare them like
List<Lazy<Pic>> ....
See Lazy<T> and the Lazy Loading Pattern in general - this is actually a common optimization technique as it defers what can add up to a lot at startup to microdelays during runtime.
Be wary about making sure the microdelays are worth it, and I advise leaving methods about which can force loading.
If you're grabbing from a list, preface with a .Any or .Contains check, and since you're looking up by name like that, consider using a Dictionary instead
Good evening; I have an application that has a drop down list; This drop down list is meant to be a list of commonly visited websites which can be altered by the user.
My question is how can I store these values in such a manor that would allow the users to change it.
Example; I as the user, decide i want google to be my first website, and youtube to be my second.
I have considered making a "settings" file however is it practical to put 20+ websites into a settings file and then load them at startup? Or a local database, but this may be overkill for the simple need.
Please point me in the right direction.
Given you have already excluded database (probably for right reasons.. as it may be over kill for a small app), I'd recommend writing the data to a local file.. but not plain text..
But preferably serialized either as XML or JSON.
This approach has at least two benefits -
More complex data can be stored in future.. example - while order can be implicit, it can be made explicit.. or additional data like last time the url was used etc..
Structured data is easier to validate against random corruption.. If it was a plain text file.. It will be much harder to ensure its integrity.
The best would be to use the power of Serializer and Deserializer in c#, which will let you work with the file in an Object Oriented. At the same time you don't need to worry about storing into files etc... etc...
Here is the sample code I quickly wrote for you.
using System;
using System.IO;
using System.Collections;
using System.Xml.Serialization;
namespace ConsoleApplication3
{
public class UrlSerializer
{
private static void Write(string filename)
{
URLCollection urls = new URLCollection();
urls.Add(new Url { Address = "http://www.google.com", Order = 1 });
urls.Add(new Url { Address = "http://www.yahoo.com", Order = 2 });
XmlSerializer x = new XmlSerializer(typeof(URLCollection));
TextWriter writer = new StreamWriter(filename);
x.Serialize(writer, urls);
}
private static URLCollection Read(string filename)
{
var x = new XmlSerializer(typeof(URLCollection));
TextReader reader = new StreamReader(filename);
var urls = (URLCollection)x.Deserialize(reader);
return urls;
}
}
public class URLCollection : ICollection
{
public string CollectionName;
private ArrayList _urls = new ArrayList();
public Url this[int index]
{
get { return (Url)_urls[index]; }
}
public void CopyTo(Array a, int index)
{
_urls.CopyTo(a, index);
}
public int Count
{
get { return _urls.Count; }
}
public object SyncRoot
{
get { return this; }
}
public bool IsSynchronized
{
get { return false; }
}
public IEnumerator GetEnumerator()
{
return _urls.GetEnumerator();
}
public void Add(Url url)
{
if (url == null) throw new ArgumentNullException("url");
_urls.Add(url);
}
}
}
You clearly need some sort of persistence, for which there are a few options:
Local database
- As you have noted, total overkill. You are just storing a list, not relational data
Simple text file
- Pretty easy, but maybe not the most "professional" way. Using XML serialization to this file would allow for complex data types.
Settings file
- Are these preferences really settings? If they are, then this makes sense.
The Registry - This is great for settings you don't want your users to ever manually mess with. Probably not the best option for a significant amount of data though
I would go with number 2. It doesn't sound like you need any fancy encoding or security, so just store everything in a text file. *.ini files tend to meet this description, but you can use any extension you want. A settings file doesn't seem like the right place for this scenario.
I am trying to implement caching using CacheCow. I have two problems:
In some cases I need to invalidate manually the cache of some resources.
For example, I have a resource that it is called purchase, and other that is called pointMovements. They are not totally connected, but doing a post in purchase, implies some changes in pointMovement. Cachecow is not detecting these changes because I am not calling the API of pointmovements. So when I call the endpoint of pointmovements, the values are cached and I cannot get the new values.
To solve this, I need to invalidate that manually, how is that possible?
There are some controllers that I don't want to cache. I am trying to use attributes for doing that but it is not working. I am following this article but the attributes are ignored.
How can I specify which controllers to cache?
I came across the same set of problems and found a solution for problem 2 (disable caching regardless of the default settings).
// This forces the server to not provide any caching by refreshing its cache table immediately (0 sec)
[HttpCacheRefreshPolicy(0)]
// This forces the client (browser) to not cache any data returned from the server (even if ETag is present) by setting the time-out to 0 and no-cache to true.
[HttpCacheControlPolicy(true, 0, true)]
public void MyController : ApiControler {... }
The attributes must be applied together for this to work. You can also control the caching at the action level by providing the same rules to each action.
I've still to figure out the solution for problem 1. but watch this space for updates.
Update
I have found a solution to problem 1.
Register the CachingHandler with your IoC container (in my case it's IUnityContainer)
Inject the ICachingHandler into your Web API controller.
To invalidate the resource, use ICachingHandler.InvalidateResource(HttpRequestMessage)
Please see a code example below. The solution has been tested.
public class Bootstrapper
{
//...
// Create a new caching handler and register it with the container.
public void RegisterCache(HttpConfiguration config, IUnityContainer container)
{
var cachingHandler = new CachingHandler(config);
// ...
container.RegisterInstance<ICachingHandler>(cachingHandler);
}
}
public class ResourceContoller : ApiController
{
private ICachingHandler _cachingHandler;
public ResourceContoller(ICachingHandler cachingHandler)
{
_cachingHandler = cachingHandler;
}
[HttpPost]
public void DeleteResource(int resourceId)
{
// Do the delete
// ...
// Now invalidate the related resource cache entry
// Construct a http request message to the related resource
// HINT: The "DefaultApi" may not be your api route name, so change this to match your route.
// GOTCHA: The route matching mechanism is case sensitive, so be aware!
var relatedResource = new HttpRequestMessage(HttpMethod.Get, Url.Link("DefaultApi", new {controller = "linkedresource", action = "getlinkedresource", id: resourceId}));
// Invalidate the resource with the caching handler.
_cachingHandler.InvalidateResource(relatedResource);
}
}
Sorry for the late response.
As #Tri Q said, the way to do this is to use attributes which I have explained in this blog:
http://byterot.blogspot.co.uk/2013/03/rest-asp-net-wep-api-0.4-new-features-breaking-change-cachecow-server.html
I solved your #1 question using below code. Here I extend the IRoutePatternProvider interface. Remember, what you return in GetRoutePattern should match what you return in GetLinkedRoutePatterns. Only then the adding and removing will work. Try it out.
Inside Application_Start
CachingHandler cacheHandler = new CachingHandler(GlobalConfiguration.Configuration);
cacheHandler.RoutePatternProvider = new CacheRoutePatternProvider();
GlobalConfiguration.Configuration.MessageHandlers.Add(cacheHandler);
Custom Class
public class CacheRoutePatternProvider : IRoutePatternProvider
{
public string GetRoutePattern(HttpRequestMessage request)
{
string path = request.RequestUri.AbsolutePath;
if (!path.EndsWith("/"))
path += "/";
return path;
}
public IEnumerable<string> GetLinkedRoutePatterns(HttpRequestMessage request)
{
string path = request.RequestUri.AbsolutePath;
if(!path.EndsWith("/"))
path += "/";
int segmentIndex;
// return each segment of the resource heirarchy
while ((segmentIndex = path.LastIndexOf("/")) > 0)
{
path = path.Substring(0, segmentIndex);
if(path.Contains("/api/"))
yield return path + "/";
}
yield break;
}
}