I am kind of new in implementing and using WCF services and extremely new (and apparently clueless) in DI.
I have WCF Services which are having constructors. The parameters of the constructors could only come in runtime from the Client application (Web server).
Something like this:
In Application server:
public class MyService : IMyService {
private IUserContext userContext;
public MyService(IUserContext uContext) {
this.userContext = uContext;
}
public DoWork() {
... // uses uContext
}
}
In Web server can only see IMyService and not the implementation of the MyService. The code would be something like this (oversimplified console app):
class Program {
static void Main(string[] args) {
var factory = new ChannelFactory<IMyService>("MyServiceEndpoint"); // MyServiceEndpoint correctly defined in config file
var client = factory.CreateChannel();
client.DoWork();
((IClientChannel)client).Close();
factory.Close();
}
}
First WCF "forced" me to use parameter-less constructor in the implementation of MyService in order to test it I added that by initializing the UserContext object. Of course I don't have the necessary info to create the object in compile time so this won't help me.
I proceeded with using this solution creating my own ServiceHostFactory, ServiceHost and IInstanceProvider where IDependency is an interface IUserContext which is implemeted by my UserContext class.
This works as expected, I registered in my svc file the custom factory, I don't need parameter-less constructor anymore. However since I don't know how to pass my UserContext to the InstanceProvider I only get a default UserContext object.
Now my noviceness comes in. I don't know how to invoke MyService by passing in the UserContext which lives in the web server. Do I also need own ChannelFactory?
Can someone direct me in the right way by updating the web server dummy code?
Thanks!
Remark: I don't want UserContext to be a parameter of the DoWork() method, because that would mean changing the parameter list of all my services and all calls...
The notion of constructors does not exist on the wire (no matter what transport you are using). For that reason you will never be able to make the client invoke a particular constructor. This is simply not part of the design of WCF (also not part of SOAP).
Don't use constructor parameters that are provided by the client. Or, make the service class have a parameterless ctor and make all service methods accepts the former constructor parameters as normal parameters.
You can also transmit common parameters as SOAP headers. That saves you changing the signature of all service methods.
Related
I use AutoFac. I have to resolve a type with an explicit instance which I get from another service.
For example: I have an instance of type Client which I get from somewhere (not from the container).
I want to configure the Autofac container so that always when an object of type Client should be resolved, it should return my instance.
Problem is that I don't have this instance at the time, when I configure the container with the Containerbuilder - so I cannot use for example LambdaRegistration.
Is there another solution for solving my problem?
You can do the following:
MyService service = null;
builder.Register(c => service).As<IMyService>();
// Later on
service = new MyService();
Depending on your needs there are quite some variations of this approach possible, such as:
Send a 'setter' delegate to some initialization code that will call the delegate after the service gets created, e.g. MyServiceInitializer.AfterInitialization(s => service = s);
Promote the service variable to a class property and provide that new wrapper to the initialization
Hide access to the service behind specific read and write abstractions, e.g. interface IMyServiceContext { IMyService Current { get; } } and interface IMyServiceSetter { void SetCurrent(IMyService service); }.
Prevent Autofac from accidentally resolving the service before it is initialized by throwing an exception instead of throwing null, e.g. Register(c => service ?? throw new InvalidOperationException("..."))
It's important to note, however, that in general, the creation of components should be fast and reliable. The fact that your component isn't available at startup is likely because it requires I/O to setup. This is a situation should should try to prevent, for instance by hiding it behind an abstraction completely. This allows you to implement a Proxy that allows the real service to be lazy loaded.
Hopefully this gives you some clues on how to solve this.
I have some integration tests that run by using ChannelFactory to create an instance of the service.
My normal WCF Service is setup using Simple Injector, but I can't seem to figure out how to get that to work with my ChannelFactory code.
Here is what my test setup code looks like:
private readonly IMyService myChannel;
public TestingClient(Binding binding, string serviceUrl)
{
var endpointAddress = new EndpointAddress(serviceUrl);
var myChannelFactory = new ChannelFactory<IMySErvice>(binding, endpointAddress);
myChannel = myChannelFactory.CreateChannel();
}
When I run my tests it looks like myChannel.MyServiceOperation(); But I get an error of:
The service type provided could not be loaded as a service because it does not have a default (parameter-less) constructor
This is because normally WCF does not support constructors with parameters. But SimpleInjector fixes that and allows for parameters that it knows how to construct.
Because of this MyService has a constructor that looks like this:
public MyService(Validations validations)
{
this.validations = validations;
}
And Validations is injected by Simple Injector (see here for how that is setup without a ChannelFactory)
But since I am setting it up manually with the ChannelFactory, I don't know how to get SimpleInjector in the mix (so it can inject the Validations object).
Can anyone tell me how to get Simple Injector to work with a ChannelFactory?
I'm in a situation where the classic functionality of vnext's DI container is not enough to provide me with the correct functionality. Let's say I have a DataService that gets data from a database like this:
public class DataService : IDataService, IDisposable {
public List<MyObject> GetMyObjects()
{
// do something to fetch the data...
return myObjects;
}
}
I can then register this service in the DI container during the configuration phase in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped(typeof(IDataService), typeof(DataService));
}
This ensures the correct lifecylce of the service (one per request scope), however, I need the service to access a different database when a different request is made. For simplicity reasons, let's say the following scenario applies:
when a request to my Web API is made, the DataService will access the currently logged in user, which contains a claim called Database which contains the information which database to use.
the DataService is then instantiated with the correct database connection.
In order to get the second step to work, I have created a constructor for the DataService like this:
public DataService(IHttpContextAccessor accessor)
{
// get the information from HttpContext
var currentUser = accessor.HttpContext.User;
var databaseClaim = currentUser.Claims.SingleOrDefault(c => c.Type.Equals("Database"));
if (databaseClaim != null)
{
var databaseId = databaseClaim.Value;
// and use this information to create the correct database connection
this.database = new Database(databaseId);
}
}
By using the currently logged in user and his claims, I can ensure that my own authentication middleware takes care of providing the necessary information to prevent attackers from trying to access the wrong database.
Of course adding the IDisposable implementation is required to cleanup any database connections (and gets called correctly using the scope lifecycle).
I can then inject the DataService into a controller like this
public MyController : Controller
{
private IDataService dataService;
public MyController(IDataService dataService)
{
this.dataService = dataService;
}
}
This all works fine so far.
My questions now are:
Is there another way to create the instance other than using the constructor of the DataService? Maybe accessing the object the IServiceCollection provides in a different place other than during the configration phase which runs only once? Maybe using my own OWIN middleware?
Is this method really safe? Could two requests made at the same time accidentally end up with the DataServiceintended for the other request and therefore end up giving out the wrong data?
What you have is fine.
Is there another way to create the instance other than using the constructor of the DataService? Maybe accessing the object the IServiceCollection provides in a different place other than during the configration phase which runs only once? Maybe using my own OWIN middleware?
Not really. You can use delegate registration but it's the same problem.
Is this method really safe?
Yes
Could two requests made at the same time accidentally end up with the DataServiceintended for the other request and therefore end up giving out the wrong data?
Nope. The IHttpContextAcessor uses AsyncLocal (http://blog.stephencleary.com/2013/04/implicit-async-context-asynclocal.html) to provide access to the "current" http context.
I'm subcsribing to the SQL Server 2008 SSRS web service ( .../reportserver/ReportService2005.asmx?wsdl) using WCF, with default WCF config options as far as I can tell.
It does something weird when it generates the local proxy classes though.
I'll use the ListChildren method as an example:
On the client side, WCF generates an interface like this, as you would expect:
public interface ReportingService2005Soap {
ListChildrenResponse ListChildren(ListChildrenRequest request);
}
It also generates a 'client' proxy that implements that interface:
public partial class ReportingService2005SoapClient :
System.ServiceModel.ClientBase<ReportingService2005Soap>, ReportingService2005Soap
{
[EditorBrowsableAttribute(EditorBrowsableState.Advanced)]
ListChildrenResponse ReportingService2005Soap.ListChildren(ListChildrenRequest request)
{
return base.Channel.ListChildren(request);
}
public ServerInfoHeader ListChildren(string Item, bool Recursive, out CatalogItem[] CatalogItems) {
ListChildrenRequest inValue = new ListChildrenRequest();
inValue.Item = Item;
inValue.Recursive = Recursive;
ListChildrenResponse retVal = ((ReportingService2005Soap)(this)).ListChildren(inValue);
CatalogItems = retVal.CatalogItems;
return retVal.ServerInfoHeader;
}
}
As you can see, the client proxy implements the interface and then 'hides' it from being used by explicitly implementing the interface (so you have to cast to get to the interface method) and additionally with a EditorBrowsableState.Advanced attribute.
It then adds an extra wrapper method that uses 'out' parameters.
Is there a way to stop if from doing that, and just have it implement the interface directly?
What its doing here leads you down the path of using the wrapper methods with 'out' parameters, and then you find you can't mock the service very easily because the wrapper methods aren't virtual, and aren't defined in any interface.
NB: I'm using the SSRS web service as an example here but I've seen WCF do this on other services as well.
This probably happens if your service is using MessageContracts. Proxy creation by default tries to unwrap these message contracts so that exposed operations accept their content directly. If you want to use message contracts on the client as well you need to configure it in advanced settings of Add service reference by checking Always generate message contracts.
I have some doubts regarding service hosting in WCF.
I want to host a service using the self-hosting approach.
ServiceHost class comes to the rescue.
By using it, it is possible to host a service having a direct access to the Windows Hosting Framework.
Well, consider the following approaches:
0) COMMON ASSUMPTIONS: All cases assume that configuration file App.config is used in order to set endpoints' ABC. So in the following codes no mention about endpoint, just do not bother about it.
We will consider this services too:
[ServiceContract]
public interface IMyService {
[OperationContract]
string MyOp1(int myint);
[OperationContract]
string MyOp2(int myint);
}
public class MyService : IMyService {
// This service needs to be constructed providing at least a string or an integer, if an attempt to construct it wothout passing any of these is done, the service should raise an error.
MyService(string strparam) { ... }
MyService(int intparam) { ... }
MyService(string strparam, int intparam) { ... }
public string MyOp1(int myint) { ... }
public string MyOp2(int myint) { ... }
}
public class MyStandaloneService : IMyService {
// This service does not need to be constructed.
MyStandaloneService() { ... }
public string MyOp1(int myint) { ... }
public string MyOp2(int myint) { ... }
}
1) CASE 1: It is possible to host a service using this overload of the ServiceHost class:
public ServiceHost(
Type serviceType,
params Uri[] baseAddresses
)
By using it, it is possible to let the framework manage the service instantiation because simply the service type is required. Of course the construction is the base contruction if the service. The no parameter constructor will be called. This overload is good when handling services that do not need special construction... some kind of standalone services:
using (ServiceHost host = new ServiceHost(typeof(MyStandaloneService))) {
host.Open();
...
host.Close();
}
2) CASE 2: It is possible to host a service using this overload of the ServiceHost class:
public ServiceHost(
Object singletonInstance,
params Uri[] baseAddresses
)
By using it, it is possible to instantiate a service and then host it without letting the framework handle this... this approach is good when dealing with services that need special treatment and are not completely standalone:
MyService MS = new MyService("the string");
using (ServiceHost host = new ServiceHost(MS)) {
host.Open();
...
host.Close();
}
Well I would like to understand the following:
A) In CASE 1 it is possible to host a service automatically by providing the type. If I attempt to create another service of the same type (MyStandaloneService), does it result in an error because trying to create two same services? Probably I should hardcode, in this case, endpoint configurations because using the config file will result in two same services hosted n the same address.
B) In CASE 2 the MSDN documentation says this creates a singleton instance of the service. So If I attempt to host another service in this way:
MyService MS = new MyService("the string");
MyService MS2 = new MyService(23);
ServiceHost host = new ServiceHost(MS));
ServiceHost host2 = new ServiceHost(MS2));
host.Open();
host2.Open();
...
host.Close();
host2.Close();
Would I get an error?
C) If I wanted to avoid singleton instantiation what should I do?
Thanks
First of all, you probably need to check out the ServiceBehaviorAttribute MSDN article.
I don't go into details here, but you can either create one instance of the service object to handle ALL requests (sequentially, that is, one after the other), or let the ServiceHost object create one service object per request and handle them simultaneously in different threads.
Once you decide what approach suits you best in your application, you will understand which one of the ServiceHost constructor overloads to use. Your CASE 1 corresponds to multiple-instances simultaneous handling approach, and CASE 2 corresponds to 'one instance to handle them all' approach.
The ServiceHost overloads must go hand-to-hand with [ServiceBehavior] attribute on your MyService class. So, please check out the link I gave above.
EDIT: now responding to your questions:
A) If I attempt to create another service of the same type (MyStandaloneService), does it result in an error because trying to create two same services?
No, it is what will be done by ServiceHost: it will create one instance of the service per request (in fact, per session, but again read MSDN)
B) In CASE 2 the MSDN documentation says this creates a singleton instance of the service. So If I attempt to host another service in this way (...) Would I get an error?
You can't host two services with the same ABC at once, so yes. If you host them on different endpoints, it's OK. The 'singleton' here means that one single service instance will handle all requests.
C) If I wanted to avoid singleton instantiation what should I do?
Use CASE 1 approach :)
To get a non-default constructor called for each instance of a non-singleton service, you should look at IInstanceProvider. You can use ServiceBehaviorAttribute with InstanceContextMode = PerCall to get a new service object for each call, and your IInstanceProvider will be used to get that object, so you can do whatever setup you need to do.