Keep more of the url for requests SwaggerWcf - c#

My WCF service is locally hosted like this:
localhost/service.svc
On the server it's hosted like this:
servername/extra/service.svc
When making a request SwaggerWcf uses the base path and adds the configered elements:
localhost/user/parameter
But it should be:
localhost/extra/user/parameter
So I need WCF to not just use the hostname, but also a little more of the url.
I could add it to the SwaggerWcf configuration, which is user and could be extra/user. But then it won't work locally anymore.
I've tried adding a Config class that could read configuration attributes statically:
using System.Configuration;
namespace Project
{
public static class Config
{
public static readonly string SwaggerWcfRequestPath = ConfigurationManager.AppSettings["SwaggerWcf.RequestPath"] ?? "/user";
}
}
But when I try to use this:
[SwaggerWcf(Config.SwaggerWcfRequestPath)]
public class Service: IService
It still produces the same error:
Error 46 An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type.
How can this be done dynamically? Then it can be different for different environments.

Currently there is no way to do it
But I'll take a look into this later today
Probably the best way is allowing to override it using Web.config
<configSections>
<section name="swaggerwcf" type="SwaggerWcf.Configuration.SwaggerWcfSection, SwaggerWcf"/>
</configSections>
<swaggerwcf>
<settings>
<setting name="Host" value="www.msampleservice.com"/>
<setting name="BasePath" value="/myserviceapi"/>
</settings>
</swaggerwcf>

Related

Specifying a custom enricher using Serilog XML configuration

There are a lot of examples of configuring sinks and their properties using app settings configuration. However, I can't really wrap my head around configuring a custom enricher via app settings. Can this be done? I've tried to specify the configuration using the full path to my class and assembly's name, but it doesn't seem to work. Here's an example of the configuration I've tried to use:
<add key="serilog:enrich:with" value="MyApp.Logging.Serilog.MyEnricher, MyApp" />
The key-value pair syntax currently needs an extension method defined for this case to work, e.g.:
static class MyLoggerEnrichmentConfigurationExtensions
{
public static LoggerConfiguration WithMyEnricher(this LoggerEnrichmentConfiguration enrich)
{
return enrich.With(new MyEnricher());
}
}
It's then referenced and called like so:
<add key="serilog:using:MyApp" value="MyApp" />
<add key="serilog:enrich:WithMyEnricher" />

How to get all sections of a specific type

Let's say I have the following in my config:
<configSections>
<section name="interestingThings" type="Test.InterestingThingsSection, Test" />
<section name="moreInterestingThings" type="Test.InterestingThingsSection, Test" />
</configSections>
<interestingThings>
<add name="Thing1" value="Seuss" />
</interestingThings>
<moreInterestingThings>
<add name="Thing2" value="Seuss" />
</moreInterestingThings>
If I want to get either section, I can get them by name pretty easily:
InterestingThingsSection interesting = (InterestingThingsSection)ConfigurationManager.GetSection("interestingThings");
InterestingThingsSection more = (InterestingThingsSection)ConfigurationManager.GetSection("moreInterestingThings");
However, this relies on my code knowing how the section is named in the config - and it could be named anything. What I'd prefer is the ability to pull all sections of type InterestingThingsSection from the config, regardless of name. How can I go about this in a flexible way (so, supports both app configs and web configs)?
EDIT: If you have the Configuration already, getting the actual sections isn't too difficult:
public static IEnumerable<T> SectionsOfType<T>(this Configuration configuration)
where T : ConfigurationSection
{
return configuration.Sections.OfType<T>().Union(
configuration.SectionGroups.SectionsOfType<T>());
}
public static IEnumerable<T> SectionsOfType<T>(this ConfigurationSectionGroupCollection collection)
where T : ConfigurationSection
{
var sections = new List<T>();
foreach (ConfigurationSectionGroup group in collection)
{
sections.AddRange(group.Sections.OfType<T>());
sections.AddRange(group.SectionGroups.SectionsOfType<T>());
}
return sections;
}
However, how do I get the Configuration instance in a generally-applicable way? Or, how do I know if I should use ConfigurationManager or WebConfigurationManager?
So far, this appears to be the best way:
var config = HostingEnvironment.IsHosted
? WebConfigurationManager.OpenWebConfiguration(null) // Web app.
: ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None); // Desktop app.
Try to use ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None) method. It opens the configuration file for the current application as a Configuration object.
MSDN documentation: https://msdn.microsoft.com/en-us/library/ms134265%28v=vs.110%29.aspx
Maybe not the best way to do it but you can read your configuration file as a normal xml and then parse the sections you want. For example if it were a web application:
XmlDocument myConfig= new XmlDocument();
myConfig.Load(Server.MapPath("~/Web.config"));
XmlNode xnodes = myConfig.SelectSingleNode("/configSections");
Now you can see the nodes that you care about discovering the names at runtime and then access the specific node of your configuration file.
Another solution is:
Path.GetFileName(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile)
If this returns "web.config", it is probably a web application.
However, HostingEnvironment.IsHosted is intended to indicate whether an appdomain is configured to run under ASP.NET so it is not sure that yours is a web application.

How to get rid of app.config and move it all into code?

I tried this question in a generic way on this post: https://stackoverflow.com/q/18968846/147637
But that did not get us to the result.
Soooo, here it is concretely!
I have the code below. It works. In VS, you add a web reference, code up the below, and then.... start fiddling the app.config.
And it works.
But I need to get rid of the app config. It is a problem that crucial pieces of the code are not in the.... code. It is hard to document, and easy for folks looking at this example to forget to look in the app config (this is an example for other devs).
So the question is: How do I move the contents of app.config into code?
(I am a part part part time coder. Pointing me at generic documentation won't get me there, sorry to say!)
**// .cs file:**
using myNameSpace.joesWebService.WebAPI.SOAP;
namespace myNameSpace
{
class Program
{
static void Main(string[] args)
{
// create the SOAP client
joesWebServerClient server = new joesWebServerClient();
string payloadXML = Loadpayload(filename);
// Run the SOAP transaction
string response = server.WebProcessShipment(string.Format("{0}#{1}", Username, Password), payloadXML);
=================================================
**app.config**
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<!-- Some non default stuff has been added by hand here -->
<binding name="IjoesWebServerbinding" maxBufferSize="256000000" maxReceivedMessageSize="256000000" />
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://joesWebServer/soap/IEntryPoint"
binding="basicHttpBinding" bindingConfiguration="IjoesWebServerbinding"
contract="myNameSpace.joesWebService.WebAPI.SOAP.IjoesWebServer"
name="IjoesWebServerSOAP" />
</client>
</system.serviceModel>
</configuration>
Generally speaking, a config file is preferred over hard-coding the settings because all you need to do with a config file is change the values you want to change and then restart the application. If they're hardcoded, you have to modify the source, recompile and redeploy.
Having said that, you can pretty much do everything in code that you do in the config file for WCF (I seem to recall a few exceptions, but don't remember them off hand).
One way to achieve what you're looking for is to define the binding in your code and create the client via ChannelFactory<T>, where T is the interface for your service (more accurately the service contract, which is usually in an interface and then implemented by a class).
For example:
using System.ServiceModel;
using myNameSpace.joesWebService.WebAPI.SOAP;
namespace myNameSpace
{
class Program
{
static void Main(string[] args)
{
// Create the binding
BasicHttpBinding myBinding = new BasicHttpBinding();
myBinding.MaxBufferSize = 256000000;
myBinding.MaxReceivedMessageSize = 256000000;
// Create the Channel Factory
ChannelFactory<IjoesWebServer> factory =
new ChannelFactory<IjoesWebServer>(myBinding, "http://joesWebServer/soap/IEntryPoint");
// Create, use and close the client
IjoesWebService client = null;
string payloadXML = Loadpayload(filename);
string response;
try
{
client = factory.CreateChannel();
((IClientChannel)client).Open();
response = client.WebProcessShipment(string.Format("{0}#{1}", Username, Password), payloadXML);
((IClientChannel)client).Close();
}
catch (Exception ex)
{
((ICientChannel)client).Abort();
// Do something with the error (ex.Message) here
}
}
}
Now you don't need a config file. The additional settings you had in the example are now in the code.
The advantage of ChannelFactory<T> is that once you create an instance of the factory, you can generate new channels (think of them as clients) at will by calling CreateChannel(). This will speed things up as most of your overhead will be in the creation of the factory.
An additional note - you're using I<name> in a lot of places in your config file. I usually denotes an interface, and if a full time developer were to look at your project it might be a little confusing for them at first glance.
With WCF 4.5, if you add a static config method to your WCF service class, then it will load automatically and ignore what's in app.config file.
<ServiceContract()>
Public Interface IWCFService
<OperationContract()>
Function GetData(ByVal value As Integer) As String
<OperationContract()>
Function GetDataUsingDataContract(ByVal composite As CompositeType) As CompositeType
End Interface
Public Class WCFService
Implements IWCFService
Public Shared Function CreateClient() As Object
End Function
Public Shared Sub Configure(config As ServiceConfiguration)
'Define service endpoint
config.AddServiceEndpoint(GetType(IWCFService), _
New NetNamedPipeBinding, _
New Uri("net.pipe://localhost/WCFService"))
'Define service behaviors
Dim myServiceBehaviors As New Description.ServiceDebugBehavior With {.IncludeExceptionDetailInFaults = True}
config.Description.Behaviors.Add(myServiceBehaviors)
End Sub
Public Function GetData(ByVal value As Integer) As String Implements IWCFService.GetData
Return String.Format("You entered: {0}", value)
End Function
Public Function GetDataUsingDataContract(ByVal composite As CompositeType) As CompositeType Implements IWCFService.GetDataUsingDataContract
End Function
End Class
I'm still looking into how to do the same for the client. I'll try to update when I figure it out if there's any interest.

Custom type application settings in ASP.NET

Just now I came across ApplicationSettings in .NET WinForms that could handle complex types.
Currently I am using AppSettings in my ASP.NET WebForms which can handle only string.
Can I use ApplicationSettings in Webforms? If so how?
The basic idea:
In a different project, create classes that will hold your custom settings. For example:
public class EndPoint
{
public string HostName { get; set; }
public int Port { get; set; }
}
public class EndPointCollection : Collection<EndPoint>
{
}
Build the project containing the classes.
Go to the Settings tab in Project Properties. It will say that there is no settings file yet and ask if you want to create it.
Add a new settings file. In the type field select Browse and type the full class name. For example: ClassLibrary.EndPointCollection. Save and rebuild the project.
Hit the edit button for the setting value. (Note that this will not be available if the classes made in the earlier step are in the same project.) Use the UI to edit the settings.
If you open the web.config / app.config file, you will see something like this:
...
<applicationSettings>
<WebApplication1.Properties.Settings>
<setting name="MyEndPoints"
serializeAs="Xml">
<value>
<ArrayOfEndPoint xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<EndPoint>
<HostName>MyHostName</HostName>
<Port>12345</Port>
</EndPoint>
<EndPoint>
<HostName>MyHost1</HostName>
<Port>1212</Port>
</EndPoint>
</ArrayOfEndPoint>
</value>
</setting>
</WebApplication1.Properties.Settings>
</applicationSettings>
...
Finally, to read these settings from your code, simply use
var endPointCollection = Settings.Default.MyEndPoints;
The designer will have created, behind the scenes, the strongly-typed objects to allow the above to work. You can see the full details in the Settings.Designer.cs file.
Bottom line: you can make all kinds of custom type settings, as long as those settings have XmlSerializable or have type converter. This technique works on Web Applications, WinForms, WPF, Console Applications etc.
Here's a variation on the accepted answer, using the following user-defined class to represent a setting:
namespace MyApplication
{
public class EndPoint
{
public string HostName { get; set; }
public int Port { get; set; }
}
}
The accepted answer proposes the use of a specialised collection class, EndPointCollection to hold settings. However, I don't think this is necessary; an array type (EndPoint[]) also seems to work.
However, typing the array type in the type browser doesn't work; you can instead specify the type directly in the .settings file (using a text editor):
<Setting Name="MyEndPoints" Type="MyApplication.EndPoint[]" Scope="User">
<Value Profile="(Default)" />
</Setting>
Also, if the value editor shown in the accepted answer isn't available, you can instead type the values directly into the value field using XML:
<ArrayOfEndPoint>
<EndPoint>
<HostName>MyHostName</HostName>
<Port>12345</Port>
</EndPoint>
<EndPoint>
<HostName>MyHost1</HostName>
<Port>1212</Port>
</EndPoint>
</ArrayOfEndPoint>
Note that the XML namespace declarations that Visual Studio generates aren't necessary in the XML, as shown above.

How to define endpoint without web.config or httpModule?

I would like to make a RESTful app of HTTPhandlers without having to define every endpoint by making an entry in the web.config, i'd like the style of attaching attributes to a class constructor eg:
public class obj : IHttpHandler
{
[WebGet(UriTemplate = "/accounts/{id}")]
public obj(string id)
{
// this is just an eg, it worild normally include caching and
// a template system
String html = File.ReadAllText("/accounts/accounts.htm");
html.replace("id", id);
httpcontext.current.response.write(html)
}
}
instead of
<httpHandlers>
<clear />
<add verb="GET" path="/accounts/*" type="MyApp.obj" />
</httphandlers>
The way i'm doing it now i have 100's of endpoints in the web.config :( i'd rather define them in the class. And i don't want to make extra files (.asmx) either. I'd like an app of just .htm files with tokens and .cs files
Thanks!
You could automate the registration of the endpoints and so on, with a custom ServiceHost, which overrides the ApplyConfiguration() method, which then virtualizes the configuration so that it does not have to be in the web.config file.
Here's a starting point. It doesn't do exactly what you want, but it illustrates the concept of virtualizing the configuration.

Categories