WCF ServiceHost already has 5 behaviors - c#

I´m creating a ServiceFactory to gain control over inicialization of my services exposed through IIS 7.
However I´m surprised by the behavior of ServiceHost. Although I have 0 configuration files for the service, wherever I Initialize a new ServiceHost, like this:
var host = new ServiceHost(typeof(MyService), baseAddresses);
Next I want to add some behaviors only if the build is in Debug mode:
#if DEBUG
host.Description.Behaviors.Add(new ServiceDebugBehavior());
#endif
However this code fails cause the ServiceDebugBehavior is already applied! Despite I have no configuration files, and no attributes applied to the service class, the host already has this behavior and 5 more applied!
Is this the expected behavior? What if I want to disable the ServiceDebugBehavior on release builds?
Thanks in advance,

Not easily - no setting I'm aware of to just turn this off. Question really is: what benefit do you get from that??
From what I see, most of those behaviors are quite essential - authentication and service credentials and so forth. And if they're there by default, even without config, I would believe they're there for a reason.
But if you really really want to, you can always create your own CustomServiceHost and do whatever you like inside that class - including tossing out all pre-defined behaviors, if you want to.
If you want to e.g. enable the IncludeExceptionDetailsInFaults setting on the service debug behavior of your service, try this type of code:
ServiceDebugBehavior behavior =
host.Description.Behaviors.Find<ServiceDebugBehavior>();
if(behavior != null)
{
behavior.IncludeExceptionDetailInFaults = true;
}
else
{
host.Description.Behaviors.Add(
new ServiceDebugBehavior() { IncludeExceptionDetailInFaults = true });
}
In this case, if the ServiceDebugBehavior is present already, you find it and just set the property to true - otherwise you create and add a new ServiceDebugBehavior. Pretty easy, I think.

You shouldn't create the service debug behaviour inside the #if DEBUG, instead just set the values for the properties you want to change from the default.

Related

Turn `ReloadOnChange` off in config source for WebApplicationFactory

This is both a question and an answer. I've fixed my problem, but it seems a bit wrong.
My original problem is running my asp.net core integration tests in a bitbucket pipeline causes System.IO.IOException: The configured user limit (128) on the number of inotify instances has been reached. Some solutions call for changing some setting through sysctl, but that is restricted by bitbucket, so that isn't an option for me.
The second way of fixing this, as noted in these stackoverflow answers, is to turn reloadOnChange off.
My new problem is now, how do we best do this for the test WebApplicationFactory?
One solution that has worked for me, which is the least amount of code, seems like a total hack. I iterate through all the JsonConfigurationSource and set ReloadOnChange to false.
Full solution:
public class TestApplicationFactory : WebApplicationFactory<Startup>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureAppConfiguration(config =>
{
foreach (var source in config.Sources)
{
if (source is JsonConfigurationSource)
{
var jsonConfigSource = (JsonConfigurationSource) source;
jsonConfigSource.ReloadOnChange = false;
}
}
});
}
}
Another solution, that I haven't tried, may be to override CreateWebHostBuilder(). However, it seems like more code and a lot of copy and paste from the default one.
Am I missing something? Is there a better way to do this?
Just experienced this issue myself running integration tests within a Linux container and followed the previous suggestions to switch off the ReloadOnChange within the WebApplicationFactory. Unfortunately that did not resolve the problem and integration tests were still failing with the same error:
System.IO.IOException: The configured user limit (128) on the number of inotify instances has been reached.
I also tried to configure xUnit to run the integration tests sequentially rather than in parallel, but that did not work either.
The solution that did work for me was to set the appropriate environment variable within the container that runs the integration tests:
export ASPNETCORE_hostBuilder__reloadConfigOnChange=false
builder.ConfigureAppConfiguration is there to configure your (main) application.
You can use builder.ConfigureHostConfiguration (see docs) to explicitly configure files to be read for the host.
builder.ConfigureHostConfiguration((hostingContext, config) =>
{
config.AddJsonFile("appsettings.json", optional: false, reloadOnChange: false);
});
The host configuration is loaded. ASP.NET Core from 3.0 is built based on Generic host (rather than the Web Host of the former versions).
You can do this without inheriting from WebApplicationFactory by using the WithWebHostBuilder and ConfigureAppConfiguration extension methods:
var webAppFactory = new WebApplicationFactory<Startup>().WithWebHostBuilder(webHostBuilder =>
{
webHostBuilder.ConfigureAppConfiguration((hostingContext, configBuilder) =>
configBuilder.Sources.Where(s => s is FileConfigurationSource).ToList()
.ForEach(s => ((FileConfigurationSource)s).ReloadOnChange = false));
});
This accomplishes the same thing as your original idea (which helped me a lot!), but more compact and without the need for a separate class definition.
I just encountered the same issue.
Setting the env variable DOTNET_hostBuilder:reloadConfigOnChange to false fixed it.
This solution works for net6 when you use the Generic Host. For other hosts, maybe try replacing DOTNET_ prefix with ASPNETCORE_
To make it simple, I set it in my code before creating the WebApplicationFactory
Environment.SetEnvironmentVariable("DOTNET_hostBuilder:reloadConfigOnChange", "false");

How to reconfigure SQLTransport based NServicebus in Asp.net Web API?

I am using NServicebus(version 4.6.3) with SQLTransport in my ASP.net web api project. I have different connectionstrings for the queues for different environments (Dev,QA,etc). My configuration looks like below:
public class BusConfigurator
{
public static IStartableBus Bus { get; private set; }
public static void DisposeBus()
{
if (Bus == null)
return;
Bus.Shutdown();
Bus.Dispose();
Bus = null;
}
public static void InitializeServiceBus(string connectionString)
{
var configure = Configure.With()
.DefineEndpointName("MyEndPoint")
.Log4Net(new DebugAppender { Threshold = Level.Warn })
.UseTransport<SqlServer>(connectionString)
.PurgeOnStartup(false)
.SetDefaultTransactionLevel()
.UnicastBus(); // Error is thrown here on second call
configure.MyCustomSQLServerPersistence();
Bus = configure.CreateBus();
}
public static void StartBus()
{
Bus.Start(() => Configure.Instance.ForInstallationOn<NServiceBus.Installation.Environments.Windows>().Install());
}
}
I have a dropdown in the app so that the user can select the environment. Based on the selection, I want to reconfigure the bus. So, I call DisposeBus then pass the connection string to the IntializeServiceBus method followed by the startBus. It works first time but throws error below when it gets called again with different connectionstring:
Unable to set the value for key: NServiceBus.Transport.ConnectionString. The settings has been locked for modifications. Please move any configuration code earlier in the configuration pipeline
Source=NServiceBus.Core
Line=0
BareMessage=Unable to set the value for key: NServiceBus.Transport.ConnectionString. The settings has been locked for modifications. Please move any configuration code earlier in the configuration pipeline
Is NServicebus intended to be used/configured this way? (I am guessing probably not) If not then is there a workaround/different approach for this?
In V4 or below, there is no way to do it by normal human means. There is only one Bus per AppDomain. All of the configuration API is static, so if you try, you get exactly the problems you ran into.
By "human means", I mean that it might be possible to do something crazy with spinning up a new AppDomain within your process, setting up a Bus within that, and then tearing it down when you're finished. It might be possible. I haven't tried it. I wouldn't recommend it.
In V5, the configuration API is completely redesigned, is not static, and so this is possible:
var cfg = new BusConfiguration();
// Set up all the settings with the new V5 Configuration API
using (var justOneBus = NServiceBus.Bus.Create(cfg).Start())
{
// Use justOneBus, then it gets disposed when done.
}
That's right. It's disposable. Then you can do it again. In your case you wouldn't want to put it in a using block - you would want to set it up somewhere, and when the dropdown gets switched, call Dispose on the current instance and rebuild it with the new parameters.
Keep in mind, however, that the Bus is still pretty expensive to create. It's definitely still something you want to treat as an application-wide singleton (or singleton-like) instance. You definitely wouldn't want to spin up a separate one per web request.

Writing a generic WCF service client config/endpoint checker?

I have a client application that consumes a number of services. It's not always immediately obvious when a service is down or incorrectly configured. I own the service side code and hosting for most of the services, but not all of them. It's a real mixed bag of client proxies - different bindings (basichttp/wshttp/nettcp), some have been generated using svcutil.exe, while others are made programatically with ChannelFactory where the contract is in a common assembly. However, I always have access to the address, binding and contract.
I would like to have a single component in my client application that could perform a basic check of the binding/endpoint config and the service availability (to show in some diagnostic panel in the client). As a minimum I just want to know that there is an endpoint at the configured address, even better would be to find out if the endpoint is responsive and supports the binding the client is trying to use.
I tried googling and was surprised that I didn't find an example (already a bad sign perhaps) but I figured that it couldn't be that hard, all I had to do was to create a clientchannel and try to open() and close() catch any exceptions that occur and abort() if necessary.
I was wrong - in particular, with clients using BasicHttpBinding where I can specify any endpoint address and am able to open and close without any exceptions.
Here's a trimmed down version of my implementation, in reality I'm returning slightly more detailed info about the type of exception and the endpoint address but this is the basic structure.
public class GenericClientStatusChecker<TChannel> : ICanCheckServiceStatus where TChannel : class
{
public GenericClientStatusChecker(Binding binding, EndpointAddress endpoint)
{
_endpoint = endpoint;
_binding = binding;
}
public bool CheckServiceStatus()
{
bool isOk = false;
ChannelFactory<TChannel> clientChannelFactory = null;
IClientChannel clientChannel = null;
try
{
clientChannelFactory = new ChannelFactory<TChannel>(_binding, _endpoint);
}
catch
{
return isOk;
}
try
{
clientChannel = clientChannelFactory.CreateChannel() as IClientChannel;
clientChannel.Open();
clientChannel.Close();
isOk = true;
}
catch
{
if (clientChannel != null)
clientChannel.Abort();
}
return isOk;
}
}
[Test]
public void CheckServiceAtNonexistentEndpoint_ExpectFalse()
{
var checker = new GenericClientStatusChecker<IDateTimeService>(new BasicHttpBinding(), new Endpointaddress("http://nonexistenturl"));
// This assert fails, because according to my implementation, everything's ok
Assert.IsFalse(checker.CheckServiceStatus());
}
I also tried a similar technique with a dummy testclient class that implemented ClientBase with the same result. I suppose it might be possible if I knew that all my service contracts implemented a common CheckHealth() method, but because some of the services are outside my control, I can't even do that.
So, is it even possible to write such a simple general purpose generic service checker as this? And if so how? (And if not, why not?)
Thanks!
Have you looked at WCF Discovery?
WCF Discovery allows a client to search for a service based on
different criteria including contract types, binding elements,
namespace, scope, and keywords or version numbers. WCF Discovery
enables runtime and design time discovery. Adding discovery to your
application can be used to enable other scenarios such as fault
tolerance and auto configuration.
For a first attempt, you could query the endpoint to see if it supports the expected contract.
The big benefit is that you can have the client “discover” which service it wants to talk to at runtime. Which removes a lot of the client side configuration errors that you are likely used to seeing.
You need to check out SO-AWARE. It is a web service management tool that can manage SOAP or REST WCF-based service across your organization. Further it has a Test Workbench!
Here are a couple of videos that show it off too:
Part 1
Part 2
To put it in perspective, this is so complex that these people make a living doing it, I don't think it's something you want to realistically build on your own.

Checking for an unregistered/missing service

How can I use ServiceController to tell me if a service has been registered or not? In the code fragment below, the check for a null DisplayName results in a System.InvalidOperationException.
Is there a straightforward way of doing this that I'm completely missing?
ServiceController sc = new ServiceController("TestService");
if (sc.DisplayName == null)
{
// unregistered or missing service
}
Look at solution below... It doesn't appear that you can...
From the docs:
You can use the ServiceController
class to connect to and control the
behavior of existing services.
Which reads like services must already be registered in order for the ServiceController to work with them.
I guess you could simply catch the exception (indicating the service doesn't exist), but that doesn't seem to be truly exceptional does it?
Solution:
Use
var services = ServiceController.GetServices(machineName)
and search the array for your needed services. If you're just working on your local box you can omit the machineName argument.
http://msdn.microsoft.com/en-us/library/s21fd6th.aspx

Is there any way to programmatically set the application name in Elmah?

I need to change the app name based on what configuration I'm using in Visual Studio. For example, if I'm in Debug configuration, I want the app name to show as 'App_Debug' in the Application field in the Elmah_Error table. Does anyone have any experience with this? Or is there another way to do it?
This can now be done purely in markup. Just add an applicationName attribute to the errorLog element in the <elmah> section of the web.config file. Example:
<errorLog type="Elmah.SqlErrorLog, Elmah"
connectionStringName="connectionString" applicationName="myApp" />
I've tested this and it works both when logging an exception and when viewing the log via Elmah.axd.
In the case of the OP, one would imagine it can be set programatically too but I didn't test that. For me and I imagine for most scenarios the markup approach is sufficient.
By default, Elmah uses the AppPool's application GUID as the default application name. It uses this as the key to identify the errors in the Elmah_Error table when you look at the web interface that's created through it's HTTP Module.
I was tasked to explore this option for my company earlier this year. I couldn't find a way to manipulate this by default since Elmah pulls the application name from HttpRuntime.AppDomainAppId in the ErrorLog.cs file. You could manipulate it by whatever key you want; however, that is the AppPool's GUID.
With that said, I was able to manipulate the ErrorLog.cs file to turn Elmah into a callable framework instead of a handler based one and allow for me set the ApplicationName. What I ended up doing was modifying ErrorLog.cs to include a property that allowed me to set the name as below:
public virtual string ApplicationName
{
get
{
if (_applicationName == null) { _applicationName = HttpRuntime.AppDomainAppId; }
return _applicationName;
}
set { _applicationName = value; }
}
What you will probably need to do is adjust this differently and set the ApplicationName not to HttpRuntime.AppDomainAppId but, instead, a value pulled from the web.config. All in all, it's possible. The way I did it enhanced the ErrorLog.Log(ex) method so I could use Elmah has a callable framework beyond web applications. Looking back I wish I did the app/web.config approach instead.
One thing to keep in mind when changing the application name in Elmah. The http handler that generates the /elmah/default.aspx interface will no longer work. I'm still trying to find time to circle back around to such; however, you may need to look into creating a custom interface when implementing.

Categories