I have a WCF service in c#, that I would like to pass in some parameters upon initialization. Now the error I get is service must be parameter less.
I have read some articles online regarding dependency injection etc. But i'm not sure if that's what i want and have tried a few things and can't seem to get it to work.
I'm also calling it from x++ ax 2012. using ref=AifUtil::createServiceClient(clientType);
to create my service reference, but would like to pass in some parameters upon initial construction of the object. Any simple ideas how to do this ?
You can't use parameterised constructors directly because of WCF default behaviours. However it is possible to do that with using implementaton of ServiceHostFactory, ServiceHost and IInstanceProvider.
Look at this: How do I pass values to the constructor on my wcf service?
EDIT: Added example codes from the link:
Given a service with this constructor signature:
public MyService(IDependency dep)
Here's an example that can spin up MyService:
public class MyServiceHostFactory : ServiceHostFactory
{
private readonly IDependency dep;
public MyServiceHostFactory()
{
this.dep = new MyClass();
}
protected override ServiceHost CreateServiceHost(Type serviceType,
Uri[] baseAddresses)
{
return new MyServiceHost(this.dep, serviceType, baseAddresses);
}
}
public class MyServiceHost : ServiceHost
{
public MyServiceHost(IDependency dep, Type serviceType, params Uri[] baseAddresses)
: base(serviceType, baseAddresses)
{
if (dep == null)
{
throw new ArgumentNullException("dep");
}
foreach (var cd in this.ImplementedContracts.Values)
{
cd.Behaviors.Add(new MyInstanceProvider(dep));
}
}
}
public class MyInstanceProvider : IInstanceProvider, IContractBehavior
{
private readonly IDependency dep;
public MyInstanceProvider(IDependency dep)
{
if (dep == null)
{
throw new ArgumentNullException("dep");
}
this.dep = dep;
}
#region IInstanceProvider Members
public object GetInstance(InstanceContext instanceContext, Message message)
{
return this.GetInstance(instanceContext);
}
public object GetInstance(InstanceContext instanceContext)
{
return new MyService(this.dep);
}
public void ReleaseInstance(InstanceContext instanceContext, object instance)
{
var disposable = instance as IDisposable;
if (disposable != null)
{
disposable.Dispose();
}
}
#endregion
#region IContractBehavior Members
public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
}
public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
{
dispatchRuntime.InstanceProvider = this;
}
public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
{
}
#endregion
}
Register MyServiceHostFactory in your MyService.svc file, or use MyServiceHost directly in code for self-hosting scenarios.
For self-hosted WCF services such as the console, we can initialize the passed parameters directly in the service host life cycle event, or perform specific actions before the service starts.
For the WCF service is hosted in WCF, this feature could be completed by the service host factory property.
https://learn.microsoft.com/en-us/dotnet/framework/wcf/extending/extending-hosting-using-servicehostfactory
https://blogs.msdn.microsoft.com/carlosfigueira/2011/06/13/wcf-extensibility-servicehostfactory/
Here is a detailed example related to authenticating the client with Asp.net membership provider, before the service running, we seed some data in the database.
Svc markup.
<%# ServiceHost Language="C#" Debug="true" Factory="WcfService2.CalculatorServiceHostFactory" Service="WcfService2.Service1" CodeBehind="Service1.svc.cs" %>
Factory.
public class CalculatorServiceHostFactory : ServiceHostFactoryBase
{
public override ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses)
{
return new CalculatorServiceHost(baseAddresses);
}
}
class CalculatorServiceHost : ServiceHost
{
#region CalculatorServiceHost Constructor
/// <summary>
/// Constructs a CalculatorServiceHost. Calls into SetupUsersAndroles to
/// set up the user and roles that the CalculatorService allows
/// </summary>
public CalculatorServiceHost(params Uri[] addresses)
: base(typeof(Service1), addresses)
{
SetupUsersAndRoles();
}
#endregion
/// <summary>
/// Sets up the user and roles that the CalculatorService allows
/// </summary>
internal static void SetupUsersAndRoles()
{
// Create some arrays for membership and role data
string[] users = { "Alice", "Bob", "Charlie" };
string[] emails = { "alice#example.org", "bob#example.org", "charlie#example.org" };
string[] passwords = { "ecilA-123", "treboR-456", "eilrahC-789" };
string[] roles = { "Super Users", "Registered Users", "Users" };
// Clear out existing user information and add fresh user information
for (int i = 0; i < emails.Length; i++)
{
if (Membership.GetUserNameByEmail(emails[i]) != null)
Membership.DeleteUser(users[i], true);
Membership.CreateUser(users[i], passwords[i], emails[i]);
}
// Clear out existing role information and add fresh role information
// This puts Alice, Bob and Charlie in the Users Role, Alice and Bob
// in the Registered Users Role and just Alice in the Super Users Role.
for (int i = 0; i < roles.Length; i++)
{
if (Roles.RoleExists(roles[i]))
{
foreach (string u in Roles.GetUsersInRole(roles[i]))
Roles.RemoveUserFromRole(u, roles[i]);
Roles.DeleteRole(roles[i]);
}
Roles.CreateRole(roles[i]);
string[] userstoadd = new string[i + 1];
for (int j = 0; j < userstoadd.Length; j++)
userstoadd[j] = users[j];
Roles.AddUsersToRole(userstoadd, roles[i]);
}
}
}
Official sample.
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-use-the-aspnet-membership-provider
Feel free to let me know if there is anything I can help with.
Related
A few days ago I opened a question if I succeed with the answers. I had not focused the question well, and now with something more knowledge I ask again.
I need to capture the errors of all my endpoints to have them included in the same site. The idea is to add a behavior to these endpoints.
namespace SIPE.Search.Helpers
{
/// <summary>
/// Implements methods that can be used to extend run-time behavior for an endpoint in either a client application.
/// </summary>
public class ExternalClientBehavior : BehaviorExtensionElement
{
protected override object CreateBehavior()
{
return new ExternalClientBehaviorClass();
}
public override Type BehaviorType
{
get
{
return typeof(ExternalClientBehaviorClass);
}
}
/// <summary>
/// JSON REST[GET] Converter Behavior
/// </summary>
private class ExternalClientBehaviorClass : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
ExternalClientMessageInspector clientInspector = new ExternalClientMessageInspector(endpoint);
clientRuntime.MessageInspectors.Add(clientInspector);
foreach (ClientOperation op in clientRuntime.Operations)
{
op.ParameterInspectors.Add(clientInspector);
}
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
//("Behavior not supported on the consumer side!");
}
public void Validate(ServiceEndpoint endpoint)
{
}
}
}
}
Now I know that it will never enter my ApplyDispatchBehaviour if the client does not implement my behaviour, and this will NEVER happen, since they are external providers and I do not have access to the code.
Also, my first error does not even leave my code, since I'm causing a NOT FOUND error.
I have found many similar sources with my problem without solution. I have found by several sites to add the following in ApplyClientBehaviour:
IErrorHandler errorHandler = new CustomErrorHandler();
clientRuntime.CallbackDispatchRuntime.ChannelDispatcher.ErrorHandlers.Add(errorHandler);
But this does not work.
Other sources that happened to me: https://riptutorial.com/csharp/example/5460/implementing-ierrorhandler-for-wcf-services
It is NOT a solution, since it is for Services Behavior. I need to do it in EndPoint Behavior.
Thank you
Please refer to the following example.
Server side.
class Program
{
static void Main(string[] args)
{
Uri uri = new Uri("http://localhost:1100");
BasicHttpBinding binding = new BasicHttpBinding();
using (ServiceHost sh = new ServiceHost(typeof(MyService), uri))
{
ServiceEndpoint se = sh.AddServiceEndpoint(typeof(IService), binding, "");
ServiceMetadataBehavior smb;
smb = sh.Description.Behaviors.Find<ServiceMetadataBehavior>();
if (smb == null)
{
smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.HttpGetUrl = new Uri("http://localhost:1100/mex");
sh.Description.Behaviors.Add(smb);
}
MyEndpointBehavior bhv = new MyEndpointBehavior();
se.EndpointBehaviors.Add(bhv);
sh.Open();
Console.WriteLine("service is ready");
Console.ReadKey();
sh.Close();
}
}
}
[ServiceContract(ConfigurationName = "isv")]
public interface IService
{
[OperationContract]
string Delete(int value);
[OperationContract]
void UpdateAll();
}
[ServiceBehavior(ConfigurationName = "sv")]
public class MyService : IService
{
public string Delete(int value)
{
if (value <= 0)
{
throw new ArgumentException("Parameter should be greater than 0");
}
return "Hello";
}
public void UpdateAll()
{
throw new InvalidOperationException("Operation exception");
}
}
public class MyCustomErrorHandler : IErrorHandler
{
public bool HandleError(Exception error)
{
return true;
}
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
FaultException faultException = new FaultException(error.Message);
MessageFault messageFault = faultException.CreateMessageFault();
fault = Message.CreateMessage(version, messageFault, error.Message);
}
}
public class MyEndpointBehavior : IEndpointBehavior
{
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
return;
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
return;
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
MyCustomErrorHandler myCustomErrorHandler = new MyCustomErrorHandler();
endpointDispatcher.ChannelDispatcher.ErrorHandlers.Add(myCustomErrorHandler);
}
public void Validate(ServiceEndpoint endpoint)
{
return;
}
}
Client.
static void Main(string[] args)
{
ServiceReference1.ServiceClient client = new ServiceReference1.ServiceClient();
try
{
client.Delete(-3);
}
catch (FaultException fault)
{
Console.WriteLine(fault.Reason.GetMatchingTranslation().Text);
}
}
Result.
Feel free to let me know if there is anything I can help with.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 years ago.
Improve this question
I've setup a WCF Server and Client and all works nicely, i can call the method at the server and receive the object initialised from that call...great.
The problem i have is the Server receives some arguments when it's started, what i can't see is how i can obtain these values from the server as it appears i can't obtain a message or pre-initialised class from the server itself.
Is there a way to provide this information other than saving it to file when the server first receives it then reading that file from the server method?
Thanks
The easiest way is to have a singleton service object. This can be done by marking the service class with ServiceBehavior attribute and passing this object to ServiceHost constructor:
[ServiceContract]
public interface IExampleService
{
[OperationContract]
int GetInitValue();
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class ExampleService : IExampleService
{
private readonly int initValue;
public ExampleService(int initValue)
{
this.initValue = initValue;
}
public int GetInitValue() => initValue;
}
// ...
// public ServiceHost(object singletonInstance, params Uri[] baseAddresses)
var host = new ServiceHost(new ExampleService(someValue)));
host.AddServiceEndpoint(typeof(IExampleService),
new WSHttpBinding(), "http://localhost:8080");
host.Open();
When singleton mode is not appropriate, one can use dependency injection, e.g. with Unity:
var container = new UnityContainer();
container
.RegisterType<IExampleService, ExampleService>()
// When service object is created, someValue is passed to it's constructor
new InjectionConstructor(someValue));
var host = new ServiceHost(typeof(ExampleService));
host.AddServiceEndpoint(typeof(IExampleService),
new WSHttpBinding(), "http://localhost:8080")
.EndpointBehaviors.Add(new UnityEndpointBehavior(container));
host.Open();
// ...
class UnityEndpointBehavior : IEndpointBehavior
{
private readonly IUnityContainer container;
public UnityEndpointBehavior(IUnityContainer container)
{
this.container = container;
}
public void AddBindingParameters(ServiceEndpoint endpoint,
BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint,
ClientRuntime clientRuntime)
{
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint,
EndpointDispatcher endpointDispatcher)
{
endpointDispatcher.DispatchRuntime.InstanceProvider =
new UnityInstanceProvider(container, endpoint.Contract.ContractType);
}
public void Validate(ServiceEndpoint endpoint)
{
}
}
class UnityInstanceProvider : IInstanceProvider
{
private readonly IUnityContainer container;
private readonly Type contractType;
public UnityInstanceProvider(IUnityContainer container, Type contractType)
{
this.container = container;
this.contractType = contractType;
}
public object GetInstance(InstanceContext instanceContext)
{
return GetInstance(instanceContext, null);
}
public object GetInstance(InstanceContext instanceContext, Message message)
{
return container.Resolve(contractType);
}
public void ReleaseInstance(InstanceContext instanceContext, object instance)
{
container.Teardown(instance);
}
}
I'm having a problem where I am able to retrieve two cookies from a login call from one web service but both cookies are stripped from any requests made to a different web service.
A little background: I have a Windows service (.NET 4.5) that needs to send data to another companies web service interface. Their interface is made up of a couple different independent web services. To connect I have to call a log in web method from one of their services; I'll call it service A. The log in method returns a Long value made up of DateTime ticks as well as two cookies: a session cookie and an authorization cookie. Both cookies must be included in every request.
If I call any of the methods in service A both cookies are included in the request. Using Fiddler I have been able to verify this. However, if I attempt to call a method in service B, both cookies get stripped from the request call before it is made.
Here are the classes and code I am currently using:
CookieBehavior Class
public sealed class CookieBehavior : IContractBehavior, IEndpointBehavior, IClientMessageInspector
{
#region Private fields
public CookieContainer cookieContainer = new CookieContainer();
#endregion
#region Public constructors
public CookieBehavior()
: this(true)
{
}
public CookieBehavior(Boolean shared)
{
this.Shared = shared;
}
#endregion
#region Public properties
public Boolean Shared
{
get;
private set;
}
#endregion
#region Private methods
private void getCookies(HttpResponseMessageProperty prop, CookieContainer cookieContainer)
{
if (prop != null)
{
String header = prop.Headers[HttpResponseHeader.SetCookie];
if (header != null)
{
String[] cookies = header.Split(',');
var cc = new CookieCollection();
foreach (string cookie in cookies)
{
if (cookie.Contains(";"))
{
cc.Add(new Cookie(cookie.Substring(0, cookie.IndexOf("=")),
cookie.Substring(cookie.IndexOf("=") + 1, cookie.IndexOf(";") - (cookie.IndexOf("=") + 1))));
}
else
{
cc.Add(new Cookie(cookie.Substring(0, cookie.IndexOf("=")), cookie.Substring(cookie.IndexOf("=") + 1)));
}
}
cookieContainer.Add(new Uri(#"http://tempuri.org"), cc);
}
}
}
private void setCookies(HttpRequestMessageProperty prop, CookieContainer cookieContainer)
{
if (prop != null)
{
prop.Headers.Add(HttpRequestHeader.Cookie, cookieContainer.GetCookieHeader(new Uri(#"http://tempuri.org")));
}
}
#endregion
#region IClientMessageInspector Members
void IClientMessageInspector.AfterReceiveReply(ref Message reply, Object correlationState)
{
if (((this.Shared == true) ? CookieContext.Current.cookieContainer : this.cookieContainer).Count == 0)
{
HttpResponseMessageProperty prop = reply.Properties[HttpResponseMessageProperty.Name.ToString()] as HttpResponseMessageProperty;
this.getCookies(prop, (this.Shared == true) ? CookieContext.Current.cookieContainer : this.cookieContainer);
}
}
Object IClientMessageInspector.BeforeSendRequest(ref Message request, IClientChannel channel)
{
HttpRequestMessageProperty prop = null;
if (request.Properties.ContainsKey(HttpRequestMessageProperty.Name.ToString()))
prop = request.Properties[HttpRequestMessageProperty.Name.ToString()] as HttpRequestMessageProperty;
else
prop = new HttpRequestMessageProperty();
this.setCookies(prop, (this.Shared == true) ? CookieContext.Current.cookieContainer : this.cookieContainer);
return (null);
}
#endregion
#region IEndpointBehavior Members
void IEndpointBehavior.AddBindingParameters(ServiceEndpoint serviceEndpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
}
void IEndpointBehavior.ApplyClientBehavior(ServiceEndpoint serviceEndpoint, ClientRuntime behavior)
{
behavior.MessageInspectors.Add(this);
}
void IEndpointBehavior.ApplyDispatchBehavior(ServiceEndpoint serviceEndpoint, EndpointDispatcher endpointDispatcher)
{
}
void IEndpointBehavior.Validate(ServiceEndpoint serviceEndpoint)
{
}
#endregion
#region IContractBehavior Members
void IContractBehavior.AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
void IContractBehavior.ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
endpoint.Behaviors.Add(this);
}
void IContractBehavior.ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
{
}
void IContractBehavior.Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
{
}
#endregion
}
CookieContext Class
public class CookieContext : IDisposable
{
#region Internal fields
internal CookieContainer cookieContainer = new CookieContainer();
#endregion
#region Private static fields
[ThreadStatic]
private static CookieContext current = null;
#endregion
#region Public static constructor
public CookieContext()
{
current = this;
}
#endregion
#region Public static properties
public static CookieContext Current
{
get
{
return (current);
}
}
#endregion
#region IDisposable Members
void IDisposable.Dispose()
{
this.cookieContainer.SetCookies(new Uri(#"http://tempuri.org"), String.Empty);
this.cookieContainer = null;
current = null;
}
#endregion
}
My Code
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
ServicePointManager.ServerCertificateValidationCallback = ((sender, certificate, chain, sslPolicyErrors) => true);
using (var cookieCtx = new CookieContext())
{
ServiceA.ServiceASoapClient serviceA_req = new ServiceA.ServiceASoapClient();
ServiceB.ServiceBSoapClient serviceB_req = new ServiceB.ServiceBSoapClient();
var cookieBhr = new CookieBehavior(true);
serviceA_req.Endpoint.EndpointBehaviors.Add(cookieBhr);
serviceB_req.Endpoint.EndpointBehaviors.Add(cookieBhr);
long? loginTicks = serviceA_req.InterfaceLogin();
if (loginTicks.HasValue)
{
// Get/Set data for request call
serviceB_resp = serviceB_req.SendData(fooBar);
}
}
Debugging my application has shown that both cookie headers are correctly set from the request yet they are stripped prior to the service B request.
Why would the both cookies get stripped from the second request?
WCF services. My 1st attempt at IoC with WCF. Calling service from Console app to test. Funny thing is I thought these services were working so I was using them to provide data to test a POC I was working on...little did I know I'd first end up fixing the services!
(Debugging using Cassini)
Versions I'm using:
FluentNHibernate 1.1.0.685
NHibernate 2.1.2.4000
Ninject 2.2.0.0
Ninject.Extensions.Wcf 2.2.0.0
.NET Framework 3.5
This is one of the examples I used as a reference: Pieter De Rycke's Blog
Almost every post I found on SO deals with MVC...I assume my issue is slightly different since this is WCF not MVC.
The Exception:
Error activating IAuditRepository
No matching bindings are available, and the type is not self-bindable.
Activation path:
2) Injection of dependency IAuditRepository into parameter auditRepository of constructor of type ShotService
1) Request for ShotService
Suggestions:
1) Ensure that you have defined a binding for IAuditRepository.
2) If the binding was defined in a module, ensure that the module has been loaded into the kernel.
3) Ensure you have not accidentally created more than one kernel.
4) If you are using constructor arguments, ensure that the parameter name matches the constructors parameter name.
5) If you are using automatic module loading, ensure the search path and filters are correct.
Console app failing code: (fails on last line)
ShotServiceClient shotSvc = new ShotServiceClient();
LookupShotAdministeredRequest request = new LookupShotAdministeredRequest();
request.ClientId = "128";
request.ClinicId = "289";
request.RequestingUserId = "1";
List<ShotsAdministeredContract> shots = shotSvc.LookupShotAdministered(request).ShotsAdministered;
The Code:
Global.asax.cs
public class Global : NinjectWcfApplication
{
protected override IKernel CreateKernel()
{
var modules = new INinjectModule[]
{
new NHibernateModule(),
new ServiceModule(),
new RepositoryModule()
};
return new StandardKernel(modules);
}
}
NHibernateSessionFactoryProvider.cs
public class NhibernateSessionFactoryProvider : Provider<ISessionFactory>
{
protected override ISessionFactory CreateInstance(IContext context)
{
var sessionFactory = new NhibernateSessionFactory();
return sessionFactory.GetSessionFactory();
}
}
NHibernateSessionFactory.cs
public class NhibernateSessionFactory
{
public ISessionFactory GetSessionFactory()
{
ISessionFactory fluentConfiguration = Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2008
.ConnectionString(ConfigurationManager.ConnectionStrings["DefaultConnectionString"].ConnectionString)
.Cache(c => c
.UseQueryCache()
.ProviderClass<HashtableCacheProvider>())
.ShowSql())
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<AppointmentMap>()
.Conventions.AddFromAssemblyOf<PrimaryKeyConvention>())
.BuildSessionFactory();
return fluentConfiguration;
}
}
NHibernateModule.cs
public class NHibernateModule : NinjectModule
{
public override void Load()
{
Bind<ISessionFactory>().ToProvider<NhibernateSessionFactoryProvider>().InSingletonScope();
Bind<ISession>().ToMethod(context => context.Kernel.Get<ISessionFactory>().OpenSession()).InRequestScope();
}
}
RepositoryModule.cs
public class RepositoryModule : NinjectModule
{
public override void Load()
{
Bind<IAuditRepository>().To<AuditRepository>();
.
.
.
Bind<IShotAdministeredRepository>().To<ShotAdministeredRepository>();
}
}
ServiceModule.cs
public class ServiceModule : NinjectModule
{
public override void Load()
{
Bind<IAuditService>().To<AuditService>();
.
.
.
Bind<IShotService>().To<ShotService>();
}
}
NinjectInstanceProvider.cs
public class NinjectInstanceProvider : IInstanceProvider
{
private Type serviceType;
private IKernel kernel;
public NinjectInstanceProvider(IKernel kernel, Type serviceType)
{
this.kernel = kernel;
this.serviceType = serviceType;
}
public object GetInstance(InstanceContext instanceContext)
{
return this.GetInstance(instanceContext, null);
}
public object GetInstance(InstanceContext instanceContext, Message message)
{
//Create the instance with your IoC container of choice...here we're using Ninject
return kernel.Get(this.serviceType);
}
public void ReleaseInstance(InstanceContext instanceContext, object instance)
{
}
}
NinjectBehaviorAttribute.cs
public class NinjectBehaviorAttribute : Attribute, IServiceBehavior
{
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
Type serviceType = serviceDescription.ServiceType;
IInstanceProvider instanceProvider = new NinjectInstanceProvider(new StandardKernel(), serviceType);
foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
{
foreach (EndpointDispatcher endpointDispatcher in dispatcher.Endpoints)
{
DispatchRuntime dispatchRuntime = endpointDispatcher.DispatchRuntime;
dispatchRuntime.InstanceProvider = instanceProvider;
}
}
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
}
}
IAuditRepository.cs
public interface IAuditRepository : IRepository<Audit>
{
}
AuditRepository.cs
public class AuditRepository : Repository<Audit>, IAuditRepository
{
public AuditRepository(ISession session) : base(session) { }
}
ShotRepository.cs
public class ShotRepository : Repository<Shot>, IShotRepository
{
public ShotRepository(ISession session) : base(session) { }
}
ShotService.svc.cs
[NinjectBehaviorAttribute]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class ShotService : IShotService
{
#region Members
private IAuditRepository _auditRepository;
private IClientRepository _clientRepository;
private IClinicRepository _clinicRepository;
private IShotRepository _repository;
private IShotAdministeredRepository _administeredRepository;
private IShotCostRepository _costRepository;
private IUserRepository _userRepository;
#endregion
#region Constructors
public ShotService(IAuditRepository auditRepository, IClientRepository clientRepository, IClinicRepository clinicRepository, IShotRepository repository, IShotAdministeredRepository administeredRepository, IShotCostRepository costRepository, IUserRepository userRepository)
{
_auditRepository = auditRepository;
_clientRepository = clientRepository;
_clinicRepository = clinicRepository;
_repository = repository;
_administeredRepository = administeredRepository;
_costRepository = costRepository;
_userRepository = userRepository;
}
#endregion
#region IShotService Members
.
.
.
public ListAdministeredShotsResponse LookupShotAdministered(LookupShotAdministeredRequest request)
{
ListAdministeredShotsResponse response = new ListAdministeredShotsResponse();
try
{
UserService userService = new UserService(_userRepository, _auditRepository);
User requestingUser = userService.Read(Convert.ToInt32(request.RequestingUserId));
if (userService.HasPermission(requestingUser, Permissions.ScheduleAppointments))
{
ShotAdministeredService service = new ShotAdministeredService(_administeredRepository, _auditRepository);
//Guts of method go here...irrelevant to current issue
}
else
{
throw new InvalidPermissionException("Requesting user does not have sufficient permissions to complete the request.");
}
}
catch (Exception ex)
{
response.FailureReason = ex.Message;
}
return response;
}
.
.
.
#endregion
}
I put a break point in CreateKernel(), it has not been hit. I also put a break point in Load() in NHibernateModule.cs, that break point has also not been hit. <-- Correction...Cassini was "not responding" so I guess I wasn't really debugging ALL of my code. I just did an End Task on Cassini and re-ran the debugger on my services...My break point in CreateKernel() was hit as well as my break point in Load(). My main issue still exists, but at least I know this code is being executed.
You are using Ninject.Extensions.Wcf and you do an own integration into Wcf at the same time. There are two kernel instances involved one of which is configured and the other one not. You should decide which integration to use and configure that kernel properly.
I would like to get some advice. I am developing a system that will load up plugins at runtime and require them to be available through a WCF endpoint.
I will have a MVC 3 Web app that is only really used for configuration, and a class library (core) that will load up different plugins.
I would appreciate some guidance on how to go about this. I would like to load the plugin up and then be able to create a WCF endpoint that is registered with IIS 7 for access into that plugin.
Thanks in advance :)
Using a derivative of Darko's Dynamic IIS hosted WCF Service work, you can achieve something what you want. Let's start with an example service we might want to host, we'll call it an IMessageBroker, it's contract is simple:
[ServiceContract]
public interface IMessageBroker
{
[OperationContract]
string Send(string message);
}
We use this contract for both the Service, and the MEF Exports/Imports. We'll also define some additional metadata to go along with it:
public interface IMessageBrokerMetadata
{
public string Name { get; }
public string Channel { get; }
}
As it's a simple project, I'll cheat and use a simple static class for managing the MEF CompositionContainer used to compose parts:
public static class MEF
{
private static CompositionContainer container;
private static bool initialised;
public static void Initialise()
{
var catalog = new DirectoryCatalog("bin");
container = new CompositionContainer(catalog);
initialised = true;
}
public static CompositionContainer Container
{
get
{
if (!initialised) Initialise();
return container;
}
}
}
To be able to generate WCF Services dynamically, we need to create a ServiceHostFactory that can access our composition container to access our types, so you could do:
public class MEFServiceHostFactory : ServiceHostFactory
{
public override ServiceHostBase CreateServiceHost(string constructorString, System.Uri[] baseAddresses)
{
var serviceType = MEF.Container
.GetExports<IMessageBroker, IMessageBrokerMetadata>()
.Where(l => l.Metadata.Name == constructorString)
.Select(l => l.Value.GetType())
.Single();
var host = new ServiceHost(serviceType, baseAddresses);
foreach (var contract in serviceType.GetInterfaces())
{
var attr = contract.GetCustomAttributes(typeof(ServiceContractAttribute), true).FirstOrDefault();
if (attr != null)
host.AddServiceEndpoint(contract, new BasicHttpBinding(), "");
}
var metadata = host.Description.Behaviors
.OfType<ServiceMetadataBehavior>()
.FirstOrDefault();
if (metadata == null)
{
metadata = new ServiceMetadataBehavior();
metadata.HttpGetEnabled = true;
host.Description.Behaviors.Add(metadata);
}
else
{
metadata.HttpGetEnabled = true;
}
return host;
}
}
Essentially the constructorString argument is used to pass in the Metadata name we want for the specific service. Next up, we need to handle locating these services. What we now need is a VirtualPathProvider which we can use to dynamically create the instance, through a VirtualFile. The provider would look like:
public class ServiceVirtualPathProvider : VirtualPathProvider
{
private bool IsServiceCall(string virtualPath)
{
virtualPath = VirtualPathUtility.ToAppRelative(virtualPath);
return (virtualPath.ToLower().StartsWith("~/services/"));
}
public override VirtualFile GetFile(string virtualPath)
{
return IsServiceCall(virtualPath)
? new ServiceFile(virtualPath)
: Previous.GetFile(virtualPath);
}
public override bool FileExists(string virtualPath)
{
if (IsServiceCall(virtualPath))
return true;
return Previous.FileExists(virtualPath);
}
public override System.Web.Caching.CacheDependency GetCacheDependency(string virtualPath, System.Collections.IEnumerable virtualPathDependencies, DateTime utcStart)
{
return IsServiceCall(virtualPath)
? null
: Previous.GetCacheDependency(virtualPath, virtualPathDependencies, utcStart);
}
}
What we are doing, is mapping any calls to /Services/ to our MEF derived endpoints. The service needs a virtual file, and this is where we tie it all together:
public class ServiceFile : VirtualFile
{
public ServiceFile(string virtualPath) : base(virtualPath)
{
}
public string GetName(string virtualPath)
{
string filename = virtualPath.Substring(virtualPath.LastIndexOf("/") + 1);
filename = filename.Substring(0, filename.LastIndexOf("."));
return filename;
}
public override Stream Open()
{
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
writer.Write("<%# ServiceHost Language=\"C#\" Debug=\"true\" Service=\"" + GetName(VirtualPath) +
"\" Factory=\"Core.MEFServiceHostFactory, Core\" %>");
writer.Flush();
stream.Position = 0;
return stream;
}
}
The virtual file will break out the Metadata name from the virtual path, where /Services/SampleMessageBroker.svc -> SampleMessageBroker. We then generate some markup which represents the markup of an .svc file with Service="SampleMessageBroker". This argument will be passed to the MEFServiceHostFactory where we can select out endpoints. So, given a sample endpoint:
[Export(typeof(IMessageBroker)),
ExportMetadata("Name", "SampleMessageBroker"),
ExportMetadata("Channel", "Greetings")]
public class SampleMessageBroker : IMessagerBroker
{
public string Send(string message)
{
return "Hello! " + message;
}
}
We can now access that dynamically at /Services/SampleMessageBroker.svc. What you might want to do, is provide a static service which allows you to interegate what endpoints are available, and feed that back to your consuming clients.
Oh, don't forget to wire up your virtual path provider:
HostingEnvironment.RegisterVirtualPathProvider(new ServiceVirtualPathProvider());