Issue with CompileAssemblyFromSource when called from a WCF app - asp.net

I have a WCF service that exposes a method. When a client calls this method, the following occurs:
the method does some processing
it tries to load an assembly if its already there
if the above dll isn't there, it generates C# code, compiles it using CSharpCodeProvider's CompileAssemblyFromSource api
It then loads the just generated assembly
Now the problem. The first time the method it called, it generates the assembly at point 3), and when it tries to return the assembly reference via CompilerResults.CompiledAssembly it throws a file not found exception. However, I can clearly see that the assembly has been generated at the specified location and I can open it with other applications.
If I call the method again via a client, it is able to load the assembly (it was successfully generated as a result of the previous call) and goes ahead to do the remaining set of tasks. Only when the assembly isnt there and it generates it and goes ahead to load it immediately, I get that exception. Any ideas? I have tried playing around with the web.config, changing impersonation to true/false. I have a seperate app pool to run this web application and I tried changing the identity of the app pool from local service to local system, even gave my windows logon credentials which has admin rights but no luck.
Any help would be much appreciated.

Are you sure it's generating the assembly? I have the same problem, except I can't find the generated .dll. I first suspected it was unable to write to the folder, so it now calls CreateDirectory and drops a text file to demonstrate that the folder is writeable.
Anyway, same problem, no success. Is it really the case that nobody else has had this issue??
I'm going to remote debug the server & see if I can step through Microsoft's PDBs...
-- EDIT --
No need to step through Microsoft's code. I had a look at the Errors collection of the CompilerResults and there was 1 item in there: "Metadata file 'c:\Windows\System32\aaclient.dll' could not be opened -- 'An attempt was made to load a program with an incorrect format. '"
When I get Directory.GetCurrentDirectory() to pick up the other DLLs it's usign the Windows System32 directory...
-- EDIT --
Resolved this by adding references from the executing assembly's folder:
CompilerParameters compilerParameters = new CompilerParameters
{
OutputAssembly = Path.Combine(GeneratedAssembliesFolder, string.Format("{0}.Generated.dll", typeName))
};
string executingDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
string[] dllFiles = Directory.GetFiles(executingDirectory, "*.dll");
compilerParameters.ReferencedAssemblies.AddRange(dllFiles.Select(f => Path.Combine(executingDirectory, f)).ToArray());
IEnumerable<string> exeFiles =Directory.GetFiles(executingDirectory, "*.exe").Where(f => !f.Contains(".vshost."));
compilerParameters.ReferencedAssemblies.AddRange(exeFiles.Select(f => Path.Combine(executingDirectory, f)).ToArray());
For greater robustness one aught to add checks for the binaries being valid managed code assemblies. This code could also be shortened by using a Linq .Union between the two GetFiles calls.
To find a suitable folder to write to:
private static string generatedAssembliesFolder;
private static string GeneratedAssembliesFolder
{
get
{
if (generatedAssembliesFolder == null)
{
string[] candidateFolders = new[]
{
Environment.GetEnvironmentVariable("TEMP", EnvironmentVariableTarget.Process),
Environment.GetEnvironmentVariable("TMP", EnvironmentVariableTarget.Process),
Environment.GetEnvironmentVariable("TEMP", EnvironmentVariableTarget.User),
Environment.GetEnvironmentVariable("TMP", EnvironmentVariableTarget.User),
Environment.GetEnvironmentVariable("TEMP", EnvironmentVariableTarget.Machine),
Environment.GetEnvironmentVariable("TMP", EnvironmentVariableTarget.Machine),
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)
};
foreach (string candidateFolder in candidateFolders)
{
try
{
if (!Directory.Exists(candidateFolder)) Directory.CreateDirectory(candidateFolder);
string testFileName = Path.Combine(candidateFolder, Path.GetRandomFileName());
File.WriteAllBytes(testFileName, new byte[0]);
File.Delete(testFileName);
}
catch (Exception ex)
{
continue;
}
generatedAssembliesFolder = candidateFolder;
break;
}
}
return generatedAssembliesFolder;
}
}

Thanks user1796307 for your input. I had resolved this issue, but forgot to update it. Sharing it for everyone's benefit. It was the .NET Fusion at play. It caches assembly load path and won't even try to load an assembly if a previous attempt to load from same location had failed. In other words:
if (Assembly.Load("C:\xyz.dll") == null)
{
Compile("C:\xyz.dll"); // Now the dll exists
Assembly.Load("C:\xyz.dll"); // this will still fail
}
The solution is to change it as:
if (!File.Exists("C:\xyz.dll")
{
Compile("C:\xyz.dll"); // Now the dll exists
Assembly.Load("C:\xyz.dll"); // this will now work
}

Related

Creating a new MobileServiceClient() freezes VS and Android app

Working on a Xamarin Forms app (Android & iOS)
When trying to create my connection to an easy table I have in an Azure Mobile App my Visual Studio Freezes for around 7 seconds and then when it comes back it has exited the code and the Android app, running in debug is permanently frozen.
When stepping through the code it steps over
client = new MobileServiceClient(appUrl);
and then when it hits the next line it freezes. But it doesn't matter what the next line is. I have put many different things after and it still freezes. Also, this is in a try/catch block, yet no exception is thrown.
I also wanted to see if the problem was server side. But both Post and Get with PostMan works fine. So I think my server is fine. Not completely sure though...
Here is some of the code:
public class ChatStorageAzureService : IChatStorageAzureService
{
public MobileServiceClient client { get; set; }
private IMobileServiceSyncTable<MessageViewModel> chatTable;
public static bool UseAuth { get; set; } = false;
private static ChatStorageAzureService instance;
public static ChatStorageAzureService Instance => instance ?? (instance = new ChatStorageAzureService());
public async Task InitializeAsync()
{
if (client?.SyncContext?.IsInitialized ?? false)
return;
var appUrl = "http://"MY-WEBSITE".azurewebsites.net/";
try
{
client = new MobileServiceClient(appUrl);
var path = "syncstore.db";
var store = new MobileServiceSQLiteStore(path);
store.DefineTable<MessageViewModel>();
await client.SyncContext.InitializeAsync(store);
chatTable = client.GetSyncTable<MessageViewModel>();
}
catch (Exception e)
{
Debug.WriteLine("Exception thrown in Initialize: " + e);
throw;
}
}
The InitializeAsync has been called in Async methods. It has been called with .Wait() method in a constructor. It has been called with button presses or in page creations. I have tried a ton of different ways to call. But it always freezes.
One thing that I think is weird is that my server code, is one project containing both the SignalR hub code and the Easy Table, yet you access them through different web addresses, For example
"http://"SignalR".azurewebsites.net/"
and
"http://"EasyTable".azurewebsites.net/"
Again PostMan is able to access both the tables and the SignalR and the SignalR works on the Android project. But I dont know if having to domains is bad. I am new... if you could not tell already, lol!
I followed this Tutorial for the Easy Table integration and when I did it in a separate project it worked fine. I am trying to integrate it into my actual project and that is where I am having all these problems.
I also turned on debugging with Azure and it doesnt seem like my app ever even reaches the service. No call is ever met. I think. But again I am new to debugging with Azure, so I might not know how to do it right. I followed this Tutorial for setting up Azure debugging
Thanks for any and all help!
Your path is incorrect. It needs to be a directory path, for example on iOS it is /<AppHome>/Library/<YourAppName>/syncstore.db.
We can leverage MobileServiceClient.DefaultDatabasePath to get the default database path in a cross-platform manner.
var path = Path.Combine(MobileServiceClient.DefaultDatabasePath, "syncstore.db");
Feel free to reference this Xamarin.Forms Sample App that uses Azure Mobile Service Client:
https://github.com/brminnick/UITestSampleApp/blob/master/Src/UITestSampleApp/Services/AzureService.cs
So I finally got it to work!!! "How?" you ask. I went on 2 week vacation, came back, started a again. Copied the new URL of the tut project into my actual project, did some testing. And then this is the big thing, I then put the same address I had been using back into my app, and... it just worked.... I did NOTHING to the code, and now it seems to work... So almost a month of work time lost and all I had to do was just put a different URL in, run it, and then put the original URL back in.... Lovely. I am guessing it cleared out some weird temp file that was messing up the program or something... even though I erased the temp files countless times... I don't get it, but onward I go!

How to open a SectionGroup in an ASP.NET web application?

I have a small ASP.NET web application hosted in an integration test (executing within NUnit). My product code can usually find configuration data from the web.config or app.config file, but for some reason when hosting ASP.NET I seem to get an ArgumentException when executing the first of these commands:
var configuration = ConfigurationManager.OpenExeConfiguration(null);
return configuration.GetSectionGroup(SectionGroupName);
exePath must be specified when not running inside a stand alone exe.
I don't know what to put here. There isn't a sensible exePath for my product to ever pass into this method as a parameter as it usually runs within a web server. Also, ordinary Sections (not SectionGroups) can typically be opened using:
ConfigurationManager.GetSection(SectionName)
even within unit tests this works, where an App.config file somehow magically gets read. That's what I'd like when reading SectionGroups.
Any ideas?
Inside the web application, try using WebConfigurationManager. You will need a mechanism to detect if you are in a web context or exe context and use some design pattern to switch between contexts.
A simple way to do this is check if HttpContext.Current is null (not null indicates web context and a null value indicates a exe context).
IMO, something like this should work,
Configuration configuration;
if (HttpContext.Current == null)
configuration = ConfigurationManager.OpenExeConfiguration(null); // whatever you are doing currently
else
configuration = WebConfigurationManager.OpenWebConfiguration(HttpContext.Current.Request.ApplicationPath); //this should do the trick
configuration.GetSectionGroup(sectionGroupName);
It will be more complex if you don't want the dependency on the System.web dll
I haven't tested it.
ConfigurationManager.GetSection("SectionGroupName/GroupName")
i.e. e.g.
<configSections>
<sectionGroup name="RFERL.Mvc" type="System.Configuration.ConfigurationSectionGroup, System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<section name="routes" type="RFERL.Mvc.Configuration.RoutesConfigurationSection, RFERL.Mvc"/>
</sectionGroup>
</configSections>
&
var config = ConfigurationManager.GetSection("RFERL.Mvc/routes") as RoutesConfigurationSection;
System.Configuration.Configuration config =
ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
return config.GetSectionGroup(SectionGroupName);
should work in asp.net.
Posting in case this helps anyone. I used this answer to be able to read an Umbraco configuration.
private static string CdnUrl()
{
// Unit test vs web environment
Configuration configuration;
if (HttpContext.Current == null)
{
configuration = ConfigurationManager.OpenExeConfiguration(null);
}
else
{
configuration = WebConfigurationManager.OpenWebConfiguration(HttpContext.Current.Request.ApplicationPath);
}
// Grab Umbraco config
ConfigurationSectionGroup umbracoConfiguration = configuration.GetSectionGroup("umbracoConfiguration");
FileSystemProvidersSection fileSystemProviders = (FileSystemProvidersSection)umbracoConfiguration.Sections.Get("FileSystemProviders");
// Return the information needed
var cdnUrl = fileSystemProviders.Providers["media"].Parameters["rootUrl"].Value;
return cdnUrl;
}

ASP.NET Routing - GetRouteData does not work if path exists

I have a HttpModule which intercepts all requests and loads data from the database based on routing rules. However, I run into one problem all the time; GetRouteData only works if the path does not exist:
var routeData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(HttpContext.Current));
Assuming a request comes in for the url http://localhost/contact, I get the correct routing data relating to that url if that path does not exist in the file system. The problem appears when I want to customize the page at that url which I do by creating an aspx page in the path ~/contact/default.aspx. Once I do that, GetRouteData return null.
I have even tried creating a new HttpContext object, but I still can not retrieve route data if the page exists.
Has anyone ever run into this problem? Is there a solution/workaround?
All help will be greatly appreciated.
Set RouteCollection.RouteExistingFiles to true.
public static void RegisterRoutes(RouteCollection routes)
{
// Cause paths to be routed even if they exists physically
routes.RouteExistingFiles = true;
// Map routes
routes.MapPageRoute("...", "...", "...");
}
Beware though. IIS7 behaves a little differently than the server used when debugging within Visual Studio. I got bit by this when I deployed my application to the web. Check out this feedback I submitted to Microsoft Connection.

How to troubleshoot "System.Web.HttpException (0x80004005): File does not exist"?

Apologies if this has already been answered on this site, I searched but did not find this exact scenario.
I'm adding log4net to a WCF service. I added a handler in the Application_Error event, and it is catching a file not found error on every request.
I've seen this with web sites, and usually the error can be traced down to not having a "favicon" file in the root directory, or to a missing image referenced in a css stylesheet.
However, this is a WCF service, there is no CSS stylesheet, and adding a favicon to the root did not solve the problem.
Does anyone else have a good way to troubleshoot this?
Some clues:
I haven't deployed this yet to the real IIS server, I'm running it locally.
The error does not happen when I am running in DEBUG inside Visual Studio, only when I access the service from a web browser (IE or Chrome)
I added the url and file path to the error message, and this is what they are:
URL: http://localhost:3994/
FilePath: /
Error: System.Web.HttpException (0x80004005): File does not exist.
Edit: the above values are what show up in the logged exception:
protected void Application_Error(object sender, EventArgs e)
{
var objErr = Server.GetLastError().GetBaseException();
if (objErr is System.Web.HttpException)
{
var filePath = Context.Request.FilePath;
var url = ((HttpApplication) sender).Context.Request.Url;
Log.Error("URL: " + url + "; FilePath: " + filePath, objErr);
} else
Log.Error("Application Error", objErr);
}
Any help would be greatly appreciated.
The reason is likely that the service has not been specified. When a web server (the local dev one as well) recieve a request for a folder they look inside that folder for the default page (usually called: index.htm, index.html, default.asp, default.aspx, etc) and present that (unless you are using a REST based service description). When you run from VS the debug will take you straight to the actual service.
In this case because you have built a service you need to specify the location of the exact service i.e. http://localhost:3994/service.svc.
Also: If you start a debug session and then change the URL to http://localhost:3994/ you should be able to check this is the case with the debugger.

ASP.NET: WebResource.axd call 404 error: how to know which assembly/resource is missing or responsible?

I get a 404 HTTP status error (not found) on a specific WebResource.axd call inside an ASP.NET 3.5 (AJAX) web application. I guess the error is thrown because a specific referenced assembly is missing in the bin folder/GAC. But I don't know which, since the page which requests the resource is very complex (I'm using third-party controls and ASP.NET Ajax.)
Is it possible to know from the encrypted "d" querystring parameter of the query, like:
.../WebResource.axd?d=...
which assembly should create the content and is possibly missing?
Note: There are other WebRequest.axd calls which execute with success.
One of the reasons for this issue is that the registered embedded resource path is incorrect or the resource is not there. Make sure the resource file is added as a Embedded Resource.
Asp.net uses the WebResourceAttribute which you must give the path to the resource.
The resource file needs to be added as a Embedded Resource to the project and the path to it would be the full namespace plus the file name.
So you have the following project resource "my.js" in the project "MyAssembly" the resource path would be "MyAssembly.my.js".
To check what file the web resource handler is not finding you can decrypt the hash code provided on the WebResource.axd url. Please see the example below an how to do that.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Web;
namespace WebApplication1
{
public partial class WebForm1 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
byte[] encryptedData = HttpServerUtility.UrlTokenDecode("encoded hash value");
Type machineKeySection = typeof(System.Web.Configuration.MachineKeySection);
Type[] paramTypes = new Type[] { typeof(bool), typeof(byte[]), typeof(byte[]), typeof(int), typeof(int) };
MethodInfo encryptOrDecryptData = machineKeySection.GetMethod("EncryptOrDecryptData", BindingFlags.Static | BindingFlags.NonPublic, null, paramTypes, null);
try
{
byte[] decryptedData = (byte[])encryptOrDecryptData.Invoke(null, new object[] { false, encryptedData, null, 0, encryptedData.Length });
string decrypted = System.Text.Encoding.UTF8.GetString(decryptedData);
decryptedLabel.Text = decrypted;
}
catch (TargetInvocationException)
{
decryptedLabel.Text = "Error decrypting data. Are you running your page on the same server and inside the same application as the web resource URL that was generated?";
}
}
}
}
Original code example by Telerik UI for ASP.NET AJAX Team Link: http://blogs.telerik.com/aspnet-ajax/posts/07-03-27/debugging-asp-net-2-0-web-resources-decrypting-the-url-and-getting-the-resource-name.aspx
This should return the URL path that aspt.net believes the embedded resource is at.
I just spent hours on a similar issue. Due to the great article pointed out by Diadistis I was able to decrypt the WebResource url and find out that my WebResource was translated into a wrong assembly pointer, recognizable by the garbage in front of your resource name. After many struggles I found out that this was because I was using the Page.ClientScript.GetWebResourceUrl in a class deriving from another class which resided outside of the assembly my resource was in. Confusing thing was that my class WAS in the same assembly, though the class deriving from was NOT. The this.GetType() parameter many articles state is a must, turned out not to be so much of a must at all in my situation. Actually, it needed to be replaced with a typeof() and it worked! Hope this may prevent others from getting the same headache as I got from this bugger.
In my case, the source of the 404 error was that the date and time of the machine running IIS were wrong (from the past).
Is your project missing any references?
Are there any references set to CopyLocal=False (common with Infragistics or GAC'ed refs) that dont make it to the destination?
A utility like reflector or dependency walker will tell you if your main assembly is missing any dependencies that are not immediately obvious.
Does the Application_Error handler in global.asax have a catch that is producing any error info (FileNotFoundExceptions)?
Did you set custom errors to 'remote only' and browse the site from the local machine?
This same issue occurs if there is a request filtering rule that detects the specified string in the query string. In my case, the query string for the AXD file was generated with a double dash that the rule detected and caused a 404 not found error for the AXD file request.

Resources