Recently I asked this :
Get Base URL of My Web Application
This worked to an extent in debug, as I use the VS Development server.
I then produced an Install, the install will then point to IIS 7.
I had :
void Application_Start(object sender, EventArgs e)
{
_baseUrl = HttpContext.Current.Request.Url.ToString();
....
}
But this threw the following error :
Request is not available in this context
I then did some reading up and here is why this happens :
http://mvolo.com/iis7-integrated-mode-request-is-not-available-in-this-context-exception-in-applicationstart
I then moved the code out from Application_Start to Application_BeginRequest, using the technique above as I found Application_BeginRequest was being executed several times.
But the problem is I need the Base URL of IIS 7, for use in Application_Start, and so I have a global string I tried to set in :
FirstRequestInitialization.Initialize(context);
But not surpriseingly when attempting this :
Application["BaseUrl"] = HttpContext.Current.Request.Url.ToString();
I get this error :
'Microsoft.Web.Administration.Application' is a 'type' but is used like a 'variable'
All I want is the Base URL of IIS 7.
I can't use Directory Entries as I can't support IIS 6.
How can I do this? Any workarounds? Can I execute AppCmd from VS?
Any help much appreciated. Thanks!
Short answer: you can't get it, because the websites do not have a single canonical base URI - a website (or rather, a web application) can be configured to answer to requests on any binding, any domain name, and any resource path - and a website can be reconfigured in the host webserver (IIS) without the application being made aware of this at all.
If you really want to store your "base URL" (even though such a thing doesn't really exist) then you can do it from within Application_BeginRequest like so:
private static readonly Object _arbitraryUrlLock = new Object();
private static volatile String _arbitraryUrl;
public void Application_BeginRequest() {
if( _arbitraryUrl == null )
lock( _arbitraryUrlLock )
if( _arbitraryUrl == null )
_arbitraryUrl = HttpContext.Current.Request.Url.ToString();
}
Related
I've created an Azure Function that retrieves new form inputs from a website, processes them and stores the result in another system by using an API call. I only want to retrieve the form inputs that have not been processed before. This is supported by the website.
I'm reading the timestamp of the most recent form input that has already been processed. This works fine.
I'm using the following function to read the setting from the Azure function environment:
private static string GetEnvironmentVariable(string name)
{
return System.Environment.GetEnvironmentVariable(name, EnvironmentVariableTarget.Process);
}
After I've processed a form input, I store the timestamp of the form with the following function:
private static void SetEnvironmentVariable(string name, string value)
{
System.Environment.SetEnvironmentVariable(name, value, EnvironmentVariableTarget.Process);
}
Everything seems to be working fine. I see in the logs that form inputs don't get processed more than once. However, when I take a look at the environment variables in the Azure dashboard, I can see that the initial value of the variable is still present. This initial value will be used when the environment 'shuts down' and is restarted (e.g. after changing the value of another environment variable).
I've tried to change the target from 'Process' to 'Machine', but this results in access control errors. There are some questions on SO that are related to my issue, but none of them provides me with an answer for my situation.
I would like to know whether:
Environment variables are the / a suited solution for my use case;
If so, how can I prevent that a variable will be reset to its initial value after resetting the Azure environment.
Thanks in advance!
Firstly, the Environment.SetEnvironmentVariable method already worked in your case.
Here is an answer from Hury Shen:
When you set the variable by Environment.SetEnvironmentVariable, it
will not show in application setting. But we can use it by
Environment.GetEnvironmentVariable as expected. Although the solution
you mentioned is not so good, but it can implement your requirement.
The adverse effect is when you restart the function app, the variables
will be lost.
About the target Machine: The environment variable is stored or retrieved from the HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\Environment key in the Windows operating system registry. This value should be used on .NET implementations running on Windows systems only.
One way to achieve but not set inside code:
In App Service, you can set app settings outside of your app code.
Then you can access them in any class using the standard ASP.NET Core
dependency injection pattern:
using Microsoft.Extensions.Configuration;
namespace SomeNamespace
{
public class SomeClass
{
private IConfiguration _configuration;
public SomeClass(IConfiguration configuration)
{
_configuration = configuration;
}
public SomeMethod()
{
// retrieve nested App Service app setting
var myHierarchicalConfig = _configuration["My:Hierarchical:Config:Data"];
// retrieve App Service connection string
var myConnString = _configuration.GetConnectionString("MyDbConnection");
}
}
}
We are currently seeing an issue with the use of HttpContext.Current.Items where the local environments of the developers show no issues (all works as expected) in the server environment we are suddenly loosing items placed inside (getting a NullReferenceException).
I sketched some code and use below:
Global.asax we initialise the factory at BeginRequest:
protected void Application_BeginRequest(object sender, EventArgs e)
{
HttpContext.Current.Items["Key"] = new Factory();
}
Inside the BaseControl we have a property to retrieve the factory easily:
public Factory Factory
{
get { return HttpContext.Current.Items["Key"] as Factory; }
}
In the UserControl we use it through the base property:
protected void Page_Load(object sender, EventArgs e)
{
Product p = Factory.CreateProduct();
}
Environment information:
Local DEVs are running on Windows 7 and 8 using IIS 7.5 and 8 and Sitecore 6.6
The server is running Windows Server 2008 R2 using IIS 7.5 and Sitecore 6.6
For all local DEVs (we're working on this project with 6 people) there's no issue with the code described above. However once we deploy the code to the test server the locations that use the Factory break (ea the HttpContext.Current.Items is empty)
I can imagine only 1 scenario when it behaves like you described: in the Global.asax file the Inherits property on the test server points to the Sitecore.Web.Application directly omitting your code execution.
Could you double check if the Global.asax file starts with
<%#Application Language='C#' Inherits="My.Assembly.Namespace.Global" %>
This could happen if the Global.asax was changed in your dev enironment but hasn't been deployed to the test environment.
If it's not an issue, try to check if the Application_BeginRequest is executed on the test server. It would give you an answer whether the Factory is never added to HttpContext.Current.Items or whether it's removed from the Items during the request handling.
I noticed you use the same name for your property as it's type:
public Factory Factory {}
Maybe this initiates some unexpected behavior?
i am trying to get the current user session from a static function, but the usObj is always null.
here is what i'm doing:
public static List<RequestTypeBL> GetRequestType(string itemNo, int requestTypeID, int claimID) {
UserSession usObj = (UserSession)HttpContext.Current.Session["UserSessionObj"];
RequestTypeBL reqTypeBL;
reqTypeBL= SERT_BO.RequestTypeBL.GetClaimRequestType(claimID, requestType, usObj.UserID, itemNo);
am i missing something? please help.
i am able to do the following from regular functions just fine:
return (UserSession)Session["UserSessionObj"];
i am seeing this behavior in our production environment only, when the same code is run in development,everything works as it should. could it have something to do with an IIS setting of some sorts?
Something similar happened to me once and it was because of the amount of worker threads that the application pool was using, the development webserver used only one. The request were not always answered by the same thread.
I don't remember where to configure that on IIS, not sure you are using IIS either.
Hope it helps.
I am trying to build (csharp) one webservice /WCF engine that make two actions:
Have one timer (thread), that will run in each 10-10 minutes, requesting some information (connecting with other server to grab some info - status) to update in one database. (This must be automatic and no human action will be available). The idea is the webservice automaticaly (10x10 minutes) update the database with the recent information status.
One service method that get some information from one database. (This is one simple method that gives the information when someone request it). This method will responsible to select the status info from database.
The problem is the step 1, because step 2 is very easy.
Can anyone help me, with ideas or some code, how to the step 1.
Any pattern should be used here?
Since it's a webapp (for instance, a "WCF Service Application" project type in VS2010), you can hook into the application events.
By default that project template type doesn't create a Global.asax, so you'll need to "add new item" and choose "Global Application Class" (it won't be available if you already have a Global.asax, FWIW).
Then you can just use the start and end events on the application to start and stop your timer, so something like:
public class Global : System.Web.HttpApplication
{
private static readonly TimeSpan UpdateEngineTimerFrequency = TimeSpan.FromMinutes(10);
private Timer UpdateEngineTimer { get; set; }
private void MyTimerAction(object state)
{
// do engine work here - call other servers, bake cookies, etc.
}
protected void Application_Start(object sender, EventArgs e)
{
this.UpdateEngineTimer = new Timer(MyTimerAction,
null, /* or whatever state object you need to pass */
UpdateEngineTimerFrequency,
UpdateEngineTimerFrequency);
}
protected void Application_End(object sender, EventArgs e)
{
this.UpdateEngineTimer.Dispose();
}
}
The Single Responsibility Principle suggests that you should split these two responsibilities into two services. One (a Windows Service) would handle the Timer. The second, the WCF Service, would have the single operation to query the database and return the data.
These are independent functions, and should be implemented independently.
Additionally, I would recommend against depending on IIS or Application_Start and similar methods. That will prevent your WCF service from being hosted in WAS or some other environment. Keep in mind that WCF is much more flexible than ASMX web services. It doesn't restrict where you host your service. You should think carefully before you place such restrictions on your own service.
I have written a Windows service, of which I want to have 1 instance running per customer. This is because the customers each have their own DB with identical schemas; the only difference between the Windows services is that they will each have a different parameter corresponding to the customer DB that they're designated to serve. (And I can't have one service with multiple worker threads, because the DB connection uses a static variable, which I can't fiddle with across threads.)
I found this neat little tutorial about how to make a Windows Service, but it only shows me how to set it up for a single service. I want to set up n instances of the service, each one with a display name that includes the customer name, running with the command line parameter that denotes the customer ID.
The tutorial linked above has a class called MyWindowsServiceInstaller, which installs the windows service on the local system, and I'm guessing this would be a logical place to set up a foreach loop through all my customers, setting up one service for each. But I can't see anywhere on the interfaces provided that would allow me to set up a command line parameter for the new service.
How do you do it?
All I wanted was to send one parameter to the service I have created.
As it turns out, all you have to do is (carefully!) edit the registry at HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\ and add the parameter in ImagePath, after the quotes.
Eg. ImagePath Value Data: "C:\Program Files\myservice\myservice.exe" param1
I found the solution in this link http://social.msdn.microsoft.com/Forums/is/csharpgeneral/thread/38242afa-7e40-4c06-975e-aa97d3cc782f
Wil Peck wrote a good article about how to install multiple instances of a windows service on a single box. The basic idea is that you have to trick the installer into thinking they are different services by giving them different names.
Having said that, it seems like it would be easier (and more maintainable) to redesign your database connection code so that it can support multiple worker threads.
You can pass parameters to your installer using installutil, for example ServiceName and DisplayName.
ProjectInstaller.cs
public partial class ProjectInstaller : Installer
{
protected override void OnBeforeInstall(IDictionary savedState)
{
SetServiceName();
base.OnBeforeInstall(savedState);
}
protected override void OnBeforeUninstall(IDictionary savedState)
{
SetServiceName();
base.OnBeforeUninstall(savedState);
}
private string AppendParameter(string path, char parameter, string value)
{
if (!path.StartsWith("\""))
path = $"\"{path}\"";
if (value.Contains(" "))
value = $"\"{value}\"";
return $"{path} -{parameter}{value}";
}
private void SetServiceName()
{
if (Context.Parameters.ContainsKey("ServiceName"))
serviceInstaller.ServiceName = Context.Parameters["ServiceName"];
if (Context.Parameters.ContainsKey("DisplayName"))
serviceInstaller.DisplayName = Context.Parameters["DisplayName"];
Context.Parameters["assemblypath"] = AppendParameter(Context.Parameters["assemblypath"], 's', serviceInstaller.ServiceName);
}
}
This will append a parameter to the path stored with the service, for example:
Before: "C:\Service.exe"
After: "C:\Service.exe" -s"Instance 1"
You can then read this parameter when you start the service and pass to your services constructor.
Program.cs
static void Main(string[] args)
{
string serviceName = args.Single(x => x.StartsWith("-s")).Substring("-s".Length);
ServiceBase service = new Service(serviceName);
ServiceBase.Run(service);
}
Service.cs
public partial class Service : ServiceBase
{
public Service(string serviceName)
{
InitializeComponent();
ServiceName = serviceName;
}
}
Usage
installutil /ServiceName="Instance 1" /DisplayName="Instance 1 Service" "C:\Service.exe"
installutil /ServiceName="Instance 2" /DisplayName="Instance 2 Service" "C:\Service.exe"
You basically need to install the service several times, and customise it with it's exe.config file.
Alternatively, you can have one service that runs different worker threads for each client.
Update
exe.Config is an Application Configuration File
I have no idea how to use that installer component to install several instances of the service, I wasn't aware you could.
Where we need several instances of one of our services to run on one machine, we actually only install it once, then literally copy the installed folder and change the exe name for the second instance. The second instance is then configured in it's own Application Configuration File.
As far as I known it is impossible to provide startup parameters using either ServiceInstaller, ServiceProcessInstaller or installutil. However, it is possible to provide startup parameters using some COM api's from advapi.dll (check the left menu). A complete collection of the required calls can be found here. It's a class (also) called ServiceInstaller that contains the required external methods and some utility methods.
You'd want to use the utility method InstallAndStart. It accepts a service name, a display name and a path to the executable that represents your Windows service. You can call it like this:
InstallAndStart("MyService", "My Service For User 1",
"c:\\pathtoexe\MyService.exe user1");
If you have the following service the parameter startupParam will receive the value user1.
class Program : ServiceBase
{
private string startupParam;
static void Main(string[] args)
{
string arg = args[0];
ServiceBase.Run(new Program(arg));
}
public Program(string startupParam)
{
this.ServiceName = "MyService";
this.startupParam = startupParam;
}
...
}