In a simple way, we need to do outproc communications through WCF named pipes. In the dev harness the applications both client and service components are instantiated through IOC in the same executable.
Service host:
/// <summary>
/// Default constructor
/// </summary>
public OpaRuntimeServiceHost(string serviceName, string hostAddress)
{
_serviceHost = new ServiceHost(typeof(OpaRuntimeService), new Uri[] {
new Uri(string.Format("net.pipe://{0}/opa/{1}", hostAddress, serviceName))
});
_serviceHost.AddServiceEndpoint(typeof(IOpaRuntimeService), new NetNamedPipeBinding(NetNamedPipeSecurityMode.None), serviceName);
_serviceHost.Open();
}
Client:
/// <summary>
/// Default constructor
/// </summary>
/// <param name="hostAddress"></param>
/// <param name="serviceName"></param>
public OpaRuntimeServiceClient(string serviceName, string hostAddress)
: base(new ServiceEndpoint(ContractDescription.GetContract(typeof(IOpaRuntimeService)),
new NetNamedPipeBinding(NetNamedPipeSecurityMode.None), new EndpointAddress(string.Format("net.pipe://{0}/opa/{1}", hostAddress, serviceName))))
{
}
Both of which are constructed successfully but when the client calls the service it generates this error:
There was no endpoint listening at net.pipe://localhost/opa/runtime that could accept the message. This is often caused by an incorrect address or SOAP action. See InnerException, if present, for more details.
Unfortunately there's no inner exception. As per other questions I made sure the Net.Pipe Listener service is running. Visual Studio is running with elevated priviledges.
Environments are VS2015 on Windows 10 or VS2012 on Windows 7.
Am I missing anything?
I believe the call to AddServiceEndpoint needs the address of the endpoint (according to the MSDN documentation). In your example code, it looks like you are only passing the serviceName.
I dug up some example code that is doing something similar. However, in my example, I am deriving from ServiceHost:
public class CustomServiceHost : ServiceHost
{
public CustomServiceHost() : base(
new CustomService(),
new[] { new Uri(string.Format("net.pipe://localhost/{0}", typeof(ICustomService).FullName)) })
{
}
protected override void ApplyConfiguration()
{
base.ApplyConfiguration();
foreach (var baseAddress in BaseAddresses)
{
AddServiceEndpoint(typeof(ICustomService), new NetNamedPipeBinding(), baseAddress);
}
}
}
Got it figured out. Service name was used twice in the service host setup therefore make something like net.pipe://localhost/opa/runtime/runtime when it should have contained this: net.pipe://localhost/opa/runtime. Thanks for the rubber duckie.
Related
I'm currently adding a WCF Service to my project, but I'm having trouble connecting to the service. I have two other services with similar configurations that work fine, and I can't find the reason why my new one is not connecting. When I try to connect to the endpoint, I get the following issue:
The type 'MyProject.MyServices.MyService, MyProject, Version=1.5.8.20715, Culture=neutral, PublicKeyToken=PUBLICKEY', provided as the Service attribute value in the ServiceHost directive, or provided in the configuration element system.serviceModel/serviceHostingEnvironment/serviceActivations could not be found.
I've tried setting the Service attribute in my .svc to the other two implementations and those work fine.
myService.svc
<%#ServiceHost language="C#" Debug="true"
Factory="Microsoft.SharePoint.Client.Services.MultipleBaseAddressDataServiceHostFactory, Microsoft.SharePoint.Client.ServerRuntime, Version=14.0.0.0, Culture=neutral, PublicKeyToken=PUBLICKEYTOKEN"
Service="MyProject.MyServices.MyService, $SharePoint.Project.AssemblyFullName$" %>
MyService.cs
/// <inheritdoc />
//[SuppressMessage("Microsoft.Security", "CA2135", Justification = "Using Level 1 Security Rules as per Chuck's SP2016 Upgrade.")]
//[SharePointPermission(SecurityAction.LinkDemand, ObjectModel = true)]
//[SharePointPermission(SecurityAction.InheritanceDemand, ObjectModel = true)]
[BasicHttpBindingServiceMetadataExchangeEndpoint]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, IncludeExceptionDetailInFaults = true)]
[ServiceContract]
public class MyService : DataService<MyDataContext>
{
private SPWeb _web;
#region Constructor
/// <summary>
/// Default constructor
/// </summary>
public MyService() : base()
{
}
#endregion
#region Methods
/// <summary>
/// Method for handling the incoming request
/// </summary>
/// <param name="args">The arguments for the request</param>
override protected void OnStartProcessingRequest(ProcessRequestArgs args)
{
}
[OperationContract]
public string AddTwoNumbers()
{
return "I love WCF Services";
}
#endregion
}
I'm expecting an "Endpoint not found", but I am getting the serviceActivations error described above instead.
In my opinion, there is something wrong with the incorrect file/directory structure of your WCF web application, which caused this issue, could not find the right file.
For details, please refer to the below link.
https://social.msdn.microsoft.com/Forums/vstudio/en-US/4aab8a20-f39f-4c8d-aad2-8762ef87a66c/systemservicemodelservicehostingenvironmentserviceactivations-could-not-be-found?forum=wcf
Feel free to let me know if the problem still exists.
I am writing a .Net Core windows service and here is a snippet of code:
internal static class Program
{
public static async Task Main(string[] args)
{
var isService = !(Debugger.IsAttached || args.Contains("--console"));
var builder = new HostBuilder()
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<IntegrationService>();
});
if (isService)
{
await builder.RunAsServiceAsync();
}
else
{
await builder.RunConsoleAsync();
}
}
}
I want to pass some parameters to my service i.e. IntegrationService - how I can send parameters to my service?
Small update on Joelius answer for .Net Core 3
Given an HostedService with this constructor mixing parameters (TimeSpan) and services (ILogger<StatusService>, IHttpClientFactory)
public StatusService(
TimeSpan cachePeriod,
ILogger<StatusService> logger,
IHttpClientFactory clientFactory)
You can in your Startup.cs add it to your HostedService like this :
services.AddHostedService
(serviceProvider =>
new StatusService(
TimeSpan.FromDays(1),
serviceProvider.GetService<ILogger<StatusService>>(),
serviceProvider.GetService<IHttpClientFactory>()));
While the answers above are correct, they do have the downside that you can't use DI in the Services Constructor anymore.
What I did instead was:
class Settings {
public string Url { get; set; }
}
class SomeService : IHostedService {
public SomeService (string instanceId, IOptionsMonitor<Settings> optionsMonitor) {
var settings = optionsMonitor.Get(instanceId);
}
}
services.Configure<Settings>("Instance1", (s) => s.Url = "http://google.com");
services.Configure<Settings>("Instance2", (s) => s.Url = "http://facebook.com");
services.AddSingleton<IHostedService>(x =>
ActivatorUtilities.CreateInstance<SomeService>(x, "Instance1")
);
services.AddSingleton<IHostedService>(x =>
ActivatorUtilities.CreateInstance<SomeService>(x, "Instance2")
);
This creates named settings for each instance and passes the named settings name to the HostedService.
If you want multiple Services with the same Class and different parameters make sure to use AddSingleton instead of AddHostedService as AddHostedService will add only one instance of the same Type which will result in only one instance being started!
What Joelius answered is correct although there is another way of doing this
services.AddSingleton<IHostedService>(provider => new IntegrationService("Test"));
Before .net core 3 you can use a config class which you can inject into the service via DI.
Your config class could look like this:
class IntegrationConfig
{
public int Timeout { get; set; }
public string Name { get; set; }
}
Then you need to add this config to the DI-system:
services.AddSingleton(new IntegrationConfig
{
Timeout = 1234,
Name = "Integration name"
});
In the class IntegrationService you need to add a constructor which takes an object of the config:
public IntegrationService(IntegrationConfig config)
{
// setup with config or simply store config
}
That's basically all you need. It's not the prettiest solution in my opinion and in .net core 3
you can simply use a factory func to add the HostedService but I think something like this is the best choice
if you're on .net core 2.2 or below.
EDIT:
In the comments Kirk Larkin mentions this:
You can emulate the overload. It's just a wrapper around AddTransient(), which of course does support the factory func approach.
For this you might want to look at the current overload which is accessable here:
/// <summary>
/// Add an <see cref="IHostedService"/> registration for the given type.
/// </summary>
/// <typeparam name="THostedService">An <see cref="IHostedService"/> to register.</typeparam>
/// <param name="services">The <see cref="IServiceCollection"/> to register with.</param>
/// <param name="implementationFactory">A factory to create new instances of the service implementation.</param>
/// <returns>The original <see cref="IServiceCollection"/>.</returns>
public static IServiceCollection AddHostedService<THostedService>(this IServiceCollection services, Func<IServiceProvider, THostedService> implementationFactory)
where THostedService : class, IHostedService
{
services.TryAddEnumerable(ServiceDescriptor.Singleton<IHostedService>(implementationFactory));
return services;
}
Note that the last commit that changed this file was on June 3rd and is tagged for preview6 and preview7 of .net core 3. Because I've never heard of TryAddEnumerable and am no microsoft employee, I don't know if you can directly translate that.
Just from looking at the current implementation of AddTransient and going down the rabbit hole a few files more, I sadly can't draw the lines well enough to be able to give you the exact functionality you're currently able to get with .net core 3.
The workaround I gave still works and seems acceptable depending on the situation.
I know that with the Flurl HTTP .NET library I can set a global proxy by using a custom HttpClientFactory, but is there a way to choose a custom proxy for each request?
With many other programming languages, setting a proxy is as easy as setting an option. For example, with Node.js I can do:
const request = require('request');
let opts = { url: 'http://random.org', proxy: 'http://myproxy' };
request(opts, callback);
The ideal way to do that with Flurl would be something like this, which is currently not possible:
await "http://random.org".WithProxy("http://myproxy").GetAsync();
I also know that creating a FlurlClient/HttpClient for every request is not an option, because of the socket exhaustion issue, which I've experienced myself in the past as well.
The scenario for this is when you need to have a pool of proxies that are rotated in some way, so that each HTTP request potentially uses a different proxy URL.
So after some discussion with the Flurl creator (#228 and #374), the solution we'come up with is to use a custom FlurlClient manager class, which would be in charge of creating the required FlurlClients and the linked HttpClient instances. This is needed because each FlurlClient can only use one proxy at a time, for limitations of how the .NET HttpClient is designed.
If you're looking for the actual solution (and code), you can skip to the end of this answer. The following section still helps if you want to understand better.
[UPDATE: I've also built an HTTP client library that takes care of all the stuff below, allowing to set a per-request proxy out of the box. It's called PlainHttp.]
So, the first explored idea was to create a custom FlurlClientFactory that implements the IFlurlClientFactory interface.
The factory keeps a pool of FlurlClients, and when a new request needs to be sent, the factory is invoked with the Url as the input parameter. Some logic is then performed to decide whether the request should go through a proxy or not. The URL could potentially be used as the discriminator for choosing the proxy to use for the particular request. In my case, a random proxy would be chosen for each request, and then a cached FlurlClient would be returned.
In the end, the factory would create:
at most one FlurlClient per proxy URL (which will be then used for all the requests that have to go through that proxy);
a set of clients for "normal" requests.
Some code for this solution can be found here. After registering the custom factory, there would be not much else to do. Standard requests like await "http://random.org".GetAsync(); would be automagically proxied, if the factory decided to do so.
Unfortunately, this solution has a drawback. It turns out that the custom factory is invoked multiple times during the process of building a request with Flurl. According to my experience, it is called at least 3 times. This could lead to issues, because the factory might not return the same FlurlClient for the same input URL.
The solution
The solution is to build a custom FlurlClientManager class, to completely bypass the FlurlClient factory mechanism and keep a custom pool of clients that are provided on demand.
While this solution is specifically built to work with the awesome Flurl library, a very similar thing can be done using the HttpClient class directly.
/// <summary>
/// Static class that manages cached IFlurlClient instances
/// </summary>
public static class FlurlClientManager
{
/// <summary>
/// Cache for the clients
/// </summary>
private static readonly ConcurrentDictionary<string, IFlurlClient> Clients =
new ConcurrentDictionary<string, IFlurlClient>();
/// <summary>
/// Gets a cached client for the host associated to the input URL
/// </summary>
/// <param name="url"><see cref="Url"/> or <see cref="string"/></param>
/// <returns>A cached <see cref="FlurlClient"/> instance for the host</returns>
public static IFlurlClient GetClient(Url url)
{
if (url == null)
{
throw new ArgumentNullException(nameof(url));
}
return PerHostClientFromCache(url);
}
/// <summary>
/// Gets a cached client with a proxy attached to it
/// </summary>
/// <returns>A cached <see cref="FlurlClient"/> instance with a proxy</returns>
public static IFlurlClient GetProxiedClient()
{
string proxyUrl = ChooseProxy();
return ProxiedClientFromCache(proxyUrl);
}
private static string ChooseProxy()
{
// Do something and return a proxy URL
return "http://myproxy";
}
private static IFlurlClient PerHostClientFromCache(Url url)
{
return Clients.AddOrUpdate(
key: url.ToUri().Host,
addValueFactory: u => {
return CreateClient();
},
updateValueFactory: (u, client) => {
return client.IsDisposed ? CreateClient() : client;
}
);
}
private static IFlurlClient ProxiedClientFromCache(string proxyUrl)
{
return Clients.AddOrUpdate(
key: proxyUrl,
addValueFactory: u => {
return CreateProxiedClient(proxyUrl);
},
updateValueFactory: (u, client) => {
return client.IsDisposed ? CreateProxiedClient(proxyUrl) : client;
}
);
}
private static IFlurlClient CreateProxiedClient(string proxyUrl)
{
HttpMessageHandler handler = new SocketsHttpHandler()
{
Proxy = new WebProxy(proxyUrl),
UseProxy = true,
PooledConnectionLifetime = TimeSpan.FromMinutes(10)
};
HttpClient client = new HttpClient(handler);
return new FlurlClient(client);
}
private static IFlurlClient CreateClient()
{
HttpMessageHandler handler = new SocketsHttpHandler()
{
PooledConnectionLifetime = TimeSpan.FromMinutes(10)
};
HttpClient client = new HttpClient(handler);
return new FlurlClient(client);
}
}
This static class keeps a global pool of FlurlClients. As with the previous solution, the pool consists of:
one client per proxy;
one client per host for all the requests that mustn't go through the proxy (this is actually the default factory strategy of Flurl).
In this implementation of the class, the proxy is chosen by the class itself (using whatever policy you want, e.g. round robin or random), but it can be adapted to take a proxy URL as the input. In that case, remember that with this implementation clients are never disposed after they're created, so you might want to think about that.
This implementation also used the new SocketsHttpHandler.PooledConnectionLifetime option, available since .NET Core 2.1, to solve the DNS issues that arise when your HttpClient instances have a long lifetime. On .NET Framework, the ServicePoint.ConnectionLeaseTimeout property should be used instead.
Using the manager class is easy. For normal requests, use:
await FlurlClientManager.GetClient(url).Request(url).GetAsync();
For proxied requests, use:
await FlurlClientManager.GetProxiedClient().Request(url).GetAsync();
I am following a tutorial from [this book]:
I am getting the following exception:
There's no doubt that my nodes are running fine:
Here's how my stateless service is setup:
internal sealed class MyStatelessService : StatelessService
{
public MyStatelessService(StatelessServiceContext context)
: base(context)
{ }
/// <summary>
/// Optional override to create listeners (e.g., TCP, HTTP) for this service replica to handle client or user requests.
/// </summary>
/// <returns>A collection of listeners.</returns>
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
return new ServiceInstanceListener[0];
}
/// <summary>
/// This is the main entry point for your service instance.
/// </summary>
/// <param name="cancellationToken">Canceled when Service Fabric needs to shut down this service instance.</param>
protected override async Task RunAsync(CancellationToken cancellationToken)
{
// TODO: Replace the following sample code with your own logic
// or remove this RunAsync override if it's not needed in your service.
long iterations = 0;
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
ServiceEventSource.Current.ServiceMessage(this.Context, "Working-{0}", ++iterations);
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
}
}
}
The way I deploy and get this exception:
What am I doing wrong? How can I get my client connected to my cluster?
The entire solution can be viewed here.
Two things:
Your ICalculatorService have to be defined in another library project, in order to be shared between your stateless service library and your client project. So:
Create a new library project MyStatelessService.Interfaces
Add to it NuGet package: Microsoft.ServiceFabric.Services.Remoting
Define in this library your ICalculatorService
Remove ICalculatorService from your other two projects
Add a reference to MyStatelessService.Interfaces to your client application and your stateless service library.
Fix references to ICalculatorService
All your ICalculatorService should be referenced from the same library
Your client will be:
using Microsoft.ServiceFabric.Services.Remoting.Client;
using MyStatelessService.Interfaces;
using System;
static void Main(string[] args)
{
var calculatorClient = ServiceProxy.Create<ICalculatorService>
(new Uri("fabric:/CalculatorService/MyStatelessService"));
var result = calculatorClient.Add(1, 2).Result;
Console.WriteLine(result);
Console.ReadKey();
}
Change your MyStatelessService's Program.cs part after the try statement with this:
ServiceRuntime.RegisterServiceAsync("MyStatelessServiceType",
context => new CalculatorService(context)).GetAwaiter().GetResult();
ServiceEventSource.Current.ServiceTypeRegistered(Process.GetCurrentProcess().Id, typeof(CalculatorService).Name);
Instead of CalculatorService, you were referencing MyStatelessService.
You're attempting to access your service through remoting, but your service is not enabled for remoting.
You need to return a communication listener from CreateServiceInstanceListeners and implement IService.
When creating your stateless service you called your service MyStatelessService instead of CalculatorService and you called your application CalculatorService instead of CalculatorApplication. As the book states at step 1 under 'The first version' "Create a new Service Fabric application named CalculatorApplication with a service named CalculatorService". You created an application named CalculatorService and a service called MyStatelessService. You then made a new service file within the stateless service project. You should never create a new service within a service. Use the generated service.cs (MyStatelessService.cs in your case) file instead. One way to solve your problem is to copy the implementation of CalculatorService into your stateless service and delete CalculatorService.cs. Your stateless service will be:
internal sealed class MyStatelessService: StatelessService, ICalculatorService
{
public MyStatelessService(StatelessServiceContext serviceContext) : base(serviceContext)
{
}
public Task<int> Add(int a, int b)
{
return Task.FromResult<int>(a + b);
}
public Task<int> Subtract(int a, int b)
{
return Task.FromResult<int>(a - b);
}
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
return new[] { new ServiceInstanceListener(context => this.CreateServiceRemotingListener(context)) };
}
}
However, it's unconventional to call an application 'service', so I'd recommend to create a new project and paste your current implementation into it.
When creating a new project, name your application CalculatorApplication.
And then create the service with the name CalculatorService.
Your CalculatorService.cs file is made automatically, so just paste your current implementation into it. (The same as MyStatelessService above, but with the name CalculatorService.)
I'm creating a custom channel in WCF in order to implement a custom security protocol. No, don't run away! It's not that scary!
Verifying the protocol on the service is relatively simple. The hard part is adding the security information to the request based on the client credentials.
What I want to do is access the ClientCredentials object (the one attached to the ClientProxy in use) from within my channel implementation. Normally, I'd get access to this through the Behaviors property on the ServiceEndpoint instance for the endpoint I'm trying to reach:
var credentials = channel.Endpoint.Behaviors.Find<ClientCredentials>();
However, I can't seem to find a way to access the service endpoint the channel is associated with from within the channel itself - almost zero metadata is available from the ChannelBase class.
Is there a way to get the endpoint my channel is associated with? Is there any alternative way to access the client credentials on the client-side?
Standard security channels don't use ClientCredentials internally. They instead talk with SecurityTokenManager which is constructed from ClientCredentials. I recommend using some disassembler to browse whole implementation.
Generally your BindingElement should build both ChannelLister and ChannelFactory and pass them all information they need.
Implement you own client service.
For example;
using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
public class UserClient : ClientBase<IAsyncESPUserService> , IESPUserService
{
/// <summary>
/// Constructor - No Parameters, this will use a default target endpoint.
/// </summary>
public UserClient() : base() { }
/// <summary>
/// Constructor - Binding and Address Parameters
/// </summary>
/// <param name="binding">How we are communicating.</param>
/// <param name="address">The address we are communicating to.</param>
public UserClient(Binding binding, EndpointAddress address) : base(binding, address) { }
/// <summary>
/// Constructor - Configuration Name Parameter
/// </summary>
/// <param name="endpointConfigurationName">The name of the configuration in ServiceReferences.ClientConfig. </param>
public UserClient(string endpointConfigurationName) : base(endpointConfigurationName) { }
//Implement your async service calls here
}
Now call it...
//Create using the default endpoint
UserClient client = new UserClient();
//Set user name and password with call
System.ServiceModel.Description.ClientCredentials loginCredentials = new System.ServiceModel.Description.ClientCredentials();
loginCredentials.UserName.UserName = "test";
loginCredentials.UserName.Password = "test";
//Attach Credentials, Can't do this in config file
var defaultCredentials = client.ChannelFactory.Endpoint.Behaviors.Find<System.ServiceModel.Description.ClientCredentials>();
client.ChannelFactory.Endpoint.Behaviors.Remove(defaultCredentials);
client.ChannelFactory.Endpoint.Behaviors.Add(loginCredentials);
//Now make a call to a service method...