I created a WebService using the .NET 2.0 framework, a class based on an interface that has the WebServiceAttribute and hosting it using IIS and a ASMX file. The WebService currently loads its configuration from one XML file.
I'd like to create multiple instance of this service where each loads it own configuration.
By coping the ASMX file I can create a clone of the webservice under a different name which will be based on exact the same implementation. But it also loads the exact same configuration file which makes it rather useless.
So my question is: What is the best way to create an arbitrary number of WebServices that are based on one class, living in one IIS virtual directory where each is loading a different configuration file?
Solution
With the help of Pavel Chuchuva's answer I created the following code to handle the loading of the configuration:
public class WebConfigManager
{
public static T Load<T>() where T: new()
{
string location =
HttpContext.Current.Request.PhysicalPath + ".config";
if (HttpContext.Current.Cache[location] is T)
{
return (T)HttpContext.Current.Cache[location];
}
using (Stream s =
new FileStream(location, FileMode.Open, FileAccess.Read))
{
return (T)(HttpContext.Current.Cache[location] =
new XmlSerializer(typeof(T)).Deserialize(s));
}
}
}
// example of the usage of WebConfigManager
public class MyWebService : IMyWebService
{
Config config = WebConfigManager.Load<Config>();
...
Copy and paste .asmx file to create multiple instances of your web service (e.g. Service1.asmx, Service2.asmx and so on).
Load configuration file based on Context.Request.FilePath value:
public string LoadConfig()
{
string configPath = Server.MapPath(this.Context.Request.FilePath + ".xml");
using (XmlReader reader = XmlReader.Create(configPath))
{
// Will read Service1.asmx.xml, Service2.asmx.xml and so on
}
}
I suggest placing the asmx in different folders and placing a web.config in each of those folders with the setting for that specific instance of the web service. This is the easy and fast way
OR
you could use Web Service Enhancements 3.0 and create a WSE router, redirect a calls to a ASMX to that router and let the router forward the call to the right web service instance and pass additional config. This a more complex way of doing it but it enables u to use a single instance of the web service that's picks the right configuration based on the parameters the router passes it.
For more info on WSE3.0 I point you to the MSDN.
Hope this helps!
Related
I'm writing an asp.net core application and wanna send a message to an NServiceBus endpoint.
As we have different environments I need to configure an endpoint address for each environment. I do that in the app settings.Env.json.
I like to do the same with the instance mappings. The only ways I know is to have a different instance-mapping.xml file for every environment or add it to the app.config that I don't have. Is there a way to set the instance machine in code? I don't wanna have different XML files.
I use NServiceBus 6.3.4
I added a feature to the endpoint configuration:
endpointConfiguration.EnableFeature<MyFeature>();
public class MyFeature : Feature
{
protected override void Setup(FeatureConfigurationContext context)
{
var endpointInstances = context.Settings.Get<EndpointInstances>();
endpointInstances.AddOrReplaceInstances("InstanceMapping",
new List<EndpointInstance>
{
new EndpointInstance("MyEndpoint").AtMachine("VM-1")
});
}
}
Check docs here and here
I've just started using Apache Ignite for .NET. In particular I am trying to write an output cache for some web APIs using the following library:
Apache.Ignite.AspNet.IgniteOutputCacheProvider
Can anyone provide any example on how to Initialize this class?
This is the Initialize() function:
public override void Initialize(string name, NameValueCollection config)
{
base.Initialize(name, config);
var cache = ConfigUtil.InitializeCache<string, object>(config, GetType(), null);
_expiryCacheHolder = new ExpiryCacheHolder<string, object>(cache);
}
I would like to see an example on how to use this WITHOUT using any xml file.
I already have a running instance of Ignite, how can I pass it to this class?
Thank you.
If Ignite instance is running within the same process, then just use Ignition.GetIgnite() method to acquire it.
If you mean that there are standalone server(s) running, then you still have to start an embedded client to connect to cluster using Ignition.Start(..). XML is not required, you can create configuration programmatically: https://apacheignite-net.readme.io/docs/configuration#c-code
In my web application, I have a service reference (not web service reference).
[DebuggerStepThrough]
[GeneratedCode("System.ServiceModel", "4.0.0.0")]
public partial class LoginClient : ClientBase<Login.LoginClient>, Login.LoginSvc
{
public LoginClient() {
EndpointAddress address = new EndpointAddress(ConfigurationManager.AppSettings["login_svc"]);
this.Endpoint.Binding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
this.Endpoint.Address = address;
}
}
Basically, I have the "login_svc" in the AppSettings. However, it throws the flowing exception:
I don't want to add the service configuration to the web.config system.servicemodel....instead I just want to use the appsettings for the url. How do I do this?
Rather than trying to do this in the constructor, you should just use one of the overloaded constructors already available to you when you instantiate the proxy in your client application. For example,
MyService.Service1Client proxy = new MyService.Service1Client(
new BasicHttpBinding(),
new EndpointAddress("<YOUR ENDPOINT ADDRESS>"));
Also, it's not recommended to edit the auto-generated code like this because if you update your service reference, then those changes will be lost because the Reference.cs file is regenerated at that time. Of course, you could copy the Reference.cs and make it a file in your project you manage as one of your own. It's not clear if you were doing that or not but just wanted to mention this just in case.
I am trying to do the following
I am building asp.net website with c# language
I want to read a text file from my project(the file is inside the project)
I tried to get the file Path by this way :
string path=Request.PhysicalApplicationPath+"filename.txt";
but I can't use The "Request" object from separated C# file ??
note: separated C3 file,I mean it's not related with aspx file
can you help me with my way or do you have another way ??
thx
I would recommend you passing the path to your library from the web application. So for example in your web app:
var path = Server.MapPath("~/filename.txt");
var result = BusinessLayer.SomeMethod(path);
You could also use HostingEnvironment in your class library but I would really advice you against it as it creates a dependency with System.Web which makes your class library tied to a web context and not unit testable friendly:
var path = Path.Combine(
HostingEnvironment.ApplicationPhysicalPath,
"filename.txt"
);
Using HttpContext.Current you have access to Request, Server, Response and other objects of the HTTP request.
but I can't use The "Request" object from separated C# file ??
I'm guessing you mean this is in a dll?
If so, then you can get to it by referencing system.web in the separate dll, and getting at the httpcontext.current object
I would use some sort of injection mechanism either to give the application root path to the class or a copy of the current context/request to the class that it can use. Essentially, you want to give the class the means to find the path (or even give it the path) rather than use a fixed dependency that is hard to recreate in testing. To simplify my example, I'll use the Request as you are doing, though, you could easily provide just the base path of the application as a string as well.
public class Foo
{
// HttpRequestBase may be more appropriate
private HttpRequest Request { get; set; }
public Foo( HttpRequest request )
{
this.Request = request;
}
public void Bar()
{
string path = Path.Combine( this.Request.PhysicalApplicationPath,
"filename.txt" );
...
}
}
Note that you could combine this with #Darin's ideas on how to calculate the server path as well.
I have a .wsdl file that my web service (old asmx style) must implement. That is taken care of. When I publish the web service you can call it with ?wsdl parameter to get a generated wsdl.
How do I include my .wsdl file so that is the one that is returned instead of the generated one?
Is it possible to do with an attribute in my web service class?
Is it a given to stay with "old-style" ASMX? Or could you move up to WCF? That's really the most current webservice offering by Microsoft, and if you're doing something new and you're on .NET 3.0 or higher - why spend time on "old" technology?
In WCF, you could definitely define a static physical WSDL file to be used by clients connecting to your metadata endpoint (your "...?wsdl" URL). Not sure if you can do it in ASMX, too.
OK, on ASMX / .NET 2.0, you could of course always put the actual WSDL file under the root of your web site, and then just reference it like this:
http://yourwebserver/YourVirtDir/MyService.wsdl
I don't know if there's a way to "redirect" the
http://yourwebserver/YourVirtDir/MyService.asmx?wsdl
call to go to that fixed URL instead. I'm sure someone else will know, though!
Marc
To avoid the confusion of having two different WSDLs available on two different URLs (i.e., the *.asmx?wsdl URL and a custom URL) in your web service application, you could write an HttpModule that intercepts the request to the *.asmx?wsdl URL and returns your custom WSDL instead.
EDIT: Here's an example, adapted and simplified from some code I previously wrote that makes a custom WSDL available at the standard *.asmx?wsdl URL.
using System;
using System.IO;
using System.Web;
using System.Web.Services.Configuration;
namespace DemoWebService
{
public class CustomWsdlModule :
IHttpModule
{
public void
Init(HttpApplication application)
{
// hook up to BeginRequest event on application object
application.BeginRequest += new EventHandler(this.onApplicationBeginRequest);
}
public void
Dispose()
{
}
private void
onApplicationBeginRequest(object source, EventArgs ea)
{
HttpApplication application = (HttpApplication)source;
HttpRequest request = application.Request;
HttpResponse response = application.Response;
// check if request is for WSDL file
if ( request.Url.PathAndQuery.EndsWith(".asmx?wsdl", StringComparison.InvariantCultureIgnoreCase) )
{
// if Documentation protocol is not allowed, throw exception
if ( (WebServicesSection.Current.EnabledProtocols & WebServiceProtocols.Documentation) == 0 )
{
throw new System.InvalidOperationException("Request format is unrecognized.");
}
// get path to physical .asmx file
String asmxPath = request.MapPath(request.Url.AbsolutePath);
// build path to .wsdl file; should be same as .asmx file, but with .wsdl extension
String wsdlPath = Path.ChangeExtension(asmxPath, ".wsdl");
// check if WSDL file exists
if ( File.Exists(wsdlPath) )
{
// read WSDL file
using ( StreamReader reader = new StreamReader(wsdlPath) )
{
string wsdlFileContents = reader.ReadToEnd();
// write WSDL to response and end response without normal processing
response.ContentType = "text/xml";
response.Write(wsdlFileContents);
response.End();
}
}
}
}
}
}
This simplified code assumes that your custom WSDL is in the same folder as your .asmx file with a .wsdl extension. The HttpModule needs to be hooked into your web service application via the web.config file:
<?xml version="1.0"?>
<configuration>
<!-- ... -->
<system.web>
<!-- ... -->
<httpModules>
<add
type="DemoWebService.CustomWsdlModule"
name="CustomWsdlModule"/>
<!-- ... -->
</httpModules>
<!-- ... -->
</system.web>
<!-- ... -->
</configuration>
You can generate a WSDL and DISCO file by pointing the disco.exe tool that ships with the .NET Framework at your web service.
disco.exe http://webserver/MyWebService.asmx
The following files are created:
results.discomap
MyWebService.disco
MyWebService.wsdl