I am very much a novice in HttpHandlers. This is actually my first attempt at it.
I have this Ajax handler that responds to a jQuery ajax call in my web app front end:
public class ajaxMeetingHandler : IHttpHandler {
public void ProcessRequest(HttpContext context) {
string resultsJSON = "";
string requestType = context.Request.Params["requestType"];
if (!String.IsNullOrEmpty(requestType)) {
switch (requestType) {
case "RecentMXMeetings":
resultsJSON = SerialiseRecentMeetings(context, "Maintenance");
// SerialiseRecentMeetings() is a method in the class
// that works fine and is not included for brevity.
break;
// more cases (not included for brevity)
}
}
public bool IsReusable {
get {
return false;
}
}
}
}
And this works perfectly.
However, if I add either of these two statements anywhere in the code:
var x = context.Server.MapPath("/");
var y = HttpContext.Current.Server.MapPath("/");
...my Context.Request.Params[] collection becomes null, and IsNullOrEmpty(requestType) now sees requestType as null. In fact, ALL the Request.Params[] are null.
If I comment out those statements (and completely rebuild the solution) the thing goes back to working properly.
In the meantime, I am going to move the calls to MapPath() out to a static "RunTimeEnvironment" class so I can get the path I need from there without touching MapPath() from inside this HttpHandler. Is that a viable or recommended solution?
It turns out my problem was not related to the code itself, per se, but how I was running the project.
Visual Studio, when you click on the "Start Debug" button will start the solution at it's root document (ie, index.html). That is UNLESS the current open document is of a runnable type, such as .html. If your current open window is one of your class files (ie, .cs), it will not attempt to run the class file, but will start the debug session at your root document.
However, it WILL attempt to run a Generic Handler (.ashx) all by itself if that is the document you currently have open. And, by doing so, it was not starting at the index.html page which issues my ajax calls and sends parameters to the Handler. So, my Params collection was null because it was literally null. Running the .ashx by itself supplies no parameters.
So, the reason it worked after changing my call type from GET to POST and Back to GET again is because in doing so, I opened the index.html file to make that change, and when I started my debug session again, my current document was the index.html file, not the Generic Handler .ashx file.
I should probably lose a hundred reputations points just for making this dumb of a mistake. But in case it helps others, there it is.
Related
Okay, so this is a really bizarre one that has been bugging me. It will be hard to explain so please bear with me!
I am using autofac to register some components by scanning references.
public static void RegisterHandlersKeyedByEnum(this ContainerBuilder builder, Assembly[] assembliesToScan, Type typeToRegister)
{
var handlers = assembliesToScan.SelectMany(a => a.GetTypes()).Where(t => t.GetInterfaces().Contains(typeToRegister));
foreach (var handler in handlers)
{
var handlesAttributes = handler.GetCustomAttributes(typeof(HandlesEnumOf), true).Cast<HandlesEnumOf>();
foreach (var handlesAttribute in handlesAttributes)
{
if (handlesAttribute != null)
{
builder.RegisterType(handler).AsImplementedInterfaces().Keyed(handlesAttribute.Value, typeToRegister);
}
}
}
}
With assemblesToScanBeing:
var assembliesToScan = AppDomain.CurrentDomain.GetAssemblies();
When I run the web app, I get the following error:
Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information.
I know what the error is, and have fixed it many times. However if I just refresh the page after ~10-15 seconds, the error no longer appears and the web app is okay to use from then on.
When I looked into this further and inspected LoaderExceptions, the following type was failing to load "Viper.ViewModels.Users.ADUserViewModel". This is where it get's weird. That file does NOT exist, anywhere. Not by searching for files, not even by searching for text. I found out it was a class that was created on a completely separate branch that was never actually merged into master (using GIT).
Now to make it even more weird, if I add in the class ADUserViewModel to Viper.ViewModels/Users folder and even leave the class completely empty. The project runs without any issues/errors whatsoever.
I mean, I could leave the empty class there, it's not doing any harm. But it will really bug me that A) It's unnecessary and B) I don't actually know why this is happening.
Any ideas would be greatly appreciated and put my mind at rest! I'm not sure what code can be posted to help explain this better, but more than happy to provide some if needed.
After deploying a new version of a website the browser loads everything from its cache from the old webpage until a hard, force refresh is done.
In ASP.NET MVC if the file becomes in Bundle, it handled by Optimization framework. a version added to your file link, and if a change occurs in your bundle's file a new token generate. follow below code :
for example, js file name is: datatables
when you put it in a bundle with the same name, you will see the
datatables?v=anY9_bo7KitrGnXQr8ITP3ylmhQe9NDzSjgLpLQWQFE1
as a file name.
change datatables and watch again the name of the file in the browser, surely it will change:
datatables?v=r8yhQBxKyDgrOGyqr1ndtdG92Ije09nqTY7yogrOSTk1
But there's two questions:
What we can do if our file wasn't in Bundle?
Is a way to force the browser to refresh cache?
we have one solution with some different way for implementation. we use above solution for it.
datatables?v=1
we can handle the version of the file, it's mean that every time that we change our file, change the version of it too. but it's not a suitable way.
another way used Guide, it wasn't suitable too, because each time it fetches the file and doesn't use from the browser cache.
datatables?v=Guid.NewGuid()
The last way that is the best Way is :
when file change occur , change version too. check follow code :
<script src="~/scripts/main.js?v=#File.GetLastWriteTime(Server.MapPath("/scripts/main.js")).ToString("yyyyMMddHHmmss")"></script>
by this way, when you change the file, LastWriteTime change too, so the version of the file will change and in the next when you open the browser, it detects a new file and fetch it.
Assuming you cannot use bundling for some reason, the solution suggested by the original poster is good enough, however it's better to put the logic inside a helper method.
It makes the code testable, it helps to change the logic without changing .cshtml , and also helps to not repeat the filename twice. Then you can have have a much cleaner code:
<script src="#Url.ContentWithVersion("~/scripts/main.js")"></script>
To do so, you can add ContentWithVersion extension method to the existing UrlHelper:
using System;
using System.IO;
using System.Web;
using System.Web.Mvc;
public static class UrlHelperExtensions
{
public static string ContentWithVersion(this UrlHelper urlHelper, string path)
{
if (urlHelper == null)
throw new ArgumentNullException(nameof(urlHelper));
var result = urlHelper.Content(path);
var file = HttpContext.Current.Server.MapPath(path);
if (File.Exists(file))
result += $"?v={File.GetLastWriteTime(file).ToString("yyyyMMddHHmmss")}";
return result;
}
}
I have a custom configuration property in my app. It looks something like this:
public class OverrideConfiguration : ConfigurationSection
{
[ConfigurationProperty(PROP_ADMIN_CONNSTR)]
public StringConfigurationElement AdminConnectionString
{
get { return base[PROP_ADMIN_CONNSTR] as StringConfigurationElement; }
set { base[PROP_ADMIN_CONNSTR] = value; }
}
// .. Various other properties, but you get the idea
}
However, what I'd like is to allow the .config file to be pointed to an external file source. Something like this:
<ServiceOverrides file="Overrides.local.config" />
Now, the built-in configSource attribute is close to what I need, but it has two major issues.
Files must exist. If the file doesn't exist, it errors out.
Files must be in the current directory or in a deeper directory. In other words, I can't point to ..\Overrides.local.config
What I want is pretty much identical to the <appSettings file="..." /> configuration element. However, that attribute seems to be something appSettings implemented, and is not part of the base ConfigurationSection class.
My Question:
Is it possible to override something in ConfigurationSection that will basically read XML data from a different location? I don't want to change any other aspect of my class or do my own XML deserialization or anything. I simply want to check if a file exists, if so, load in the XML contents from that file, otherwise load in the default XML contents.
Ok, I have a working solution. I'm not sure if it's the best approach, but it does appear to work exactly how I want.
private readonly Queue<String> externalSources = new Queue<String>();
protected override void DeserializeElement(XmlReader reader, bool serializeCollectionKey)
{
var externalFile = reader.GetAttribute("File");
if(!String.IsNullOrWhiteSpace(externalFile))
{
externalSources.Enqueue(externalFile);
}
base.DeserializeElement(reader, serializeCollectionKey);
}
protected override void PostDeserialize()
{
base.PostDeserialize();
// Override data with local stuff
if (externalSources.Count == 0) return;
string file = externalSources.Dequeue();
if (System.IO.File.Exists(file))
{
var reader = XmlReader.Create(file);
base.DeserializeSection(reader);
}
}
First, I trap the DeserializeElement event, which happens when we read the <ServiceOverrides> element. We check if it has a File attribute, and if so we add it to a queue of external sources to load.
Next, we trap the PostDeserialize event, which gets called after all the local XML is parsed. If there's an external source in the queue, we dequeue it, check if it actually exists, then create an XmlReader with the contents of that file. Now we can simply call DeserializeSection again and pass in our new reader. The ConfigurationSection class is smart enough to just overwrite or append any new data to the existing configuration. What I get at the end is an aggregation of both configuration files, where the include file wins in the event of a duplicate.
Now, what's this nonsense with the queue? Well, it seems every time you call DeserializeSection, it'll call PostDeserialize again. So, if we simply trapped PostDeserialize, check the File attribute, and call DeserializeSection again, we'd get in an infinite loop. We could just use a flag to remember if we already loaded the external file, but a queue has the added benefit of allowing the include file to load more include files (not that I'd ever want to do that, but you might).
Tips: This will probably work fairly well, and is simple to understand, but if you're using it in production code, there's a few things you could improve on. First, externalSources doesn't really need to be a queue, since these calls aren't actually recursive. You can probably just use a string, and set it to null after you're done processing that file. Second, this could cause an infinite loop in the event of a circular include chain. You could create a List<T> of previously included files, then check if the include already exists in that list before adding it to the queue.
Hope this helps someone!
I'm running coded ui automation and defined a method attribute called [ExternalDataSource()] to read a document (csv, xml...) and parse the data into some dictionaries. I'll copy it here so you can have a better insight:
[System.AttributeUsage(System.AttributeTargets.Method)]
public class ExternalDataSource : System.Attribute
{
public ExternalDataSource(string filename)
{
DirectoryInfo di = new DirectoryInfo(Assembly.GetExecutingAssembly().Location);
string file = Path.Combine(Path.GetDirectoryName(di.FullName), filename);
try
{
code
}
catch (Exception)
{
throw new UITestException("Cannot load data source document");
}
}
}
In it I try to access Assembly.GetExecutingAssembly().Location to get a file that is copied to the TestResult/Out folder. I assigned this attribute to only one TestMethod() in the whole application and while debugging, I found out that the application enters the attribute's c'tor twice. Both times the Location is different. Once it's from the bin/Debug folder, the other time it's from the TestResults/Out folder. Two questions:
Why does the debugger enter that attribute twice if I call it only once in my application?
Why does the location of the same assembly change?
Well it seems nobody had an answer, but while debugging a run from the command line using mstest.exe with the vs2012 JIT Debugger i found out a strange thing:
When putting a System.Diagnostics.Debugger.Break() in the class where this attribute is the jitter was called from MSTest.exe but when this breakpoint was in the testmethod decorated with this attribute, QTAgent32.exe was called. I had implemented a singleton class to handle my parameters, and while it was populated in ExternalDataSource in this attribute by MSTest, when entering QTAgent32 (the test) it was empty.
The solution that worked for me was just to initialize that Singleton with the data on [TestInitialize()].
Hope this helps somebody.
This may be a long shot, but I'm using ComponentOne's Spellchecker control for Silverlight. I made a test project, added a plain textbox and a button to it, added the references to the C1.Silverlight and C1.Silverlight.SpellChecker bits, and added the dictionary file to my project.
In the code, I called up the spellchecker on button1's click event and it worked SPLENDIDLY. The spellchecker dialog shows up, and works exactly as it should.
Since that test was successful, I then tried to implement this into my existing project. I've had no success for absolutely NO reason that I can determine, since I used the EXACT SAME code.
Here's the code I use to call the component:
using C1.Silverlight;
using C1.Silverlight.SpellChecker;
using C1.Silverlight.Resources;
public partial class MainPage : UserControl
{
C1SpellChecker spellChecker = new C1SpellChecker();
public MainPage()
{
InitializeComponent();
spellChecker.MainDictionary.LoadAsync("C1Spell_en-US.dct");
}
private void btnSpelling_Click(object sender, RoutedEventArgs e)
{
var dlg = new C1SpellDialog();
spellChecker.CheckControlAsync(txtArticle, false, dlg);
}
The references to C1.Silverlight and C1.Silverlight.Spellchecker are added to this project as well, and the dictionary as been added in the same fashion as well. The issue seems to be that for whatever reason the dictionary is not loading, because the spellChecker.Enabled method returns whether or not the main dictionary has been loaded. If I call MessageBox.Show("SpellChecker Enabled = " + spellChecker.Enabled.ToString()); it shows false, even though the call to load the dictionary is there (as you can see).
What would cause the dictionary to not load? Have I added it to my project incorrectly somehow?
EDIT: I suspect that I have added the dictionary to the project incorrectly, because the ComponentOne reference states:
If C1SpellChecker cannot find the
spelling dictionary, it will not throw
any exceptions. The Enabled property
will be set to false and the component
will not be able to spell-check any
text.
I just don't know what's wrong though because it was added in the same way that it was in the test project (Right clicked on the project.web->Add->Existing Item)
As always, thank you!
-Sootah
You could add the dictionary to the Silverlight app as an embedded resource and then load it using this code:
public MainPage()
{
InitializeComponent();
// load C1SpellChecker dictionary from embedded resource
var asm = this.GetType().Assembly;
foreach (var res in asm.GetManifestResourceNames())
{
if (res.EndsWith(".dct"))
{
using (var s = asm.GetManifestResourceStream(res))
{
sc.MainDictionary.Load(s);
break;
}
}
}
}
I think this post is duplicated in our forum as well, but will answer first here. Please try this:
1) Try to access the .dct file using your browser. If you cannot see it, it's probably because your web server is not serving that type of files. You need ton configure the web server to allow it.
2) verify the URL you are using is correct.http://helpcentral.componentone.com/CS/silverlight_161/f/78/p/86955/241328.aspx#241328
3) Check you are setting everything correctly: http://helpcentral.componentone.com/CS/silverlight_161/f/78/p/81924/227790.aspx#227790
Hope this helps!