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.)
Related
I’ve created an object that I would like to pass in a WCF call… but inside ServiceReference1… this object is redefined… is there a way to just use the original object everywhere… it seems like people have done this but I can’t figure out what I am doing wrong.
The object is used as a parameter to a function in the service contract.
[OperationContract(IsOneWay = true)]
void UpdateInformation(MyObject myObject);
The error that I get when I try to call the function from my client is “Argument 1: cannot convert from ‘MyNameSpaceDTO.MyObject' to ‘MyNameSpace.ServiceReference1.MyObject’”
The object is in it’s own class library dll and it is marked with [DataObject] and [DataMember] attributes.
namespace MyNameSpaceDTO
{
[DataContract]
public class MyObject
{
[DataMember]
public string Name { get; set; }
….
But, also ends up in Reference.cs after adding the Service Reference as:
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Runtime.Serialization", "4.0.0.0")]
[System.Runtime.Serialization.DataContractAttribute(Name="MyObject", Namespace="http://schemas.datacontract.org/2004/07/MyNameSpaceDTO")]
[System.SerializableAttribute()]
public partial class MyObject : object, System.Runtime.Serialization.IExtensibleDataObject, System.ComponentModel.INotifyPropertyChanged {
[System.NonSerializedAttribute()]
private System.Runtime.Serialization.ExtensionDataObject extensionDataField;
[System.Runtime.Serialization.OptionalFieldAttribute()]
private string NameField;
...
Also, I do have the following set in the Advanced section of the Add Service Reference:
[x] Reuse types in referenced assemblies
(o) Reuse types in all referenced assemblies
For consuming a WCF service you often see samples (and they're undoubtedly advisable!) where you're instructed to add that service via the Add Service Reference dialog. By referencing a service that way your client application creates proxy classes form the WSDL exposed by the service.
As a result you end up having e.g. a class MyNameSpaceDTO.MyObject in your contract-assembly and a MyNameSpace.ServiceReference1.MyObject in your client application which was generated form the WSDL. This may seem somewhat redundant.
One situation in which you may need this behaviour could be the following: Imagine you'd want to consume an arbitrary public web service which you don't control. You have no access to the contract-assembly which defines the types etc. In that situation creating your own local proxy classes from the exposed WSDL is optimal since it's your only way to get the needed types and so on.
But your concrete situation seems to be a little bit different. I think what you're looking for is a shared contract. Since you're in control of the client and server code (and both live happily side by side in the same solution), you're in the comfortable situation to just share the contract:
So instead of adding a service reference within your client-app (via Add Service Reference), you'd just reference the contract-assembly (via the usual Add Reference dialogue). By doing this there'll by only one MyNameSpaceDTO.MyObject since the second one is never created and not needed. This approach is called contract sharing.
Please take a look at that example:
EDIT:
Please note some changes: The most important one is that you usually wouldn't want to share the assembly which holds your implementation logic of your service. So I extracted that part from the Contract-assembly and put it in a separate Implementation-assembly. By doing so, you simply share the interfaces and types and not the implementation logic. This change is reflected in the screenshot above, too.
You could set up that small solution with the following classes:
Contract - IService1.cs:
[ServiceContract]
public interface IService1
{
[OperationContract]
string GetData(int value);
}
Implementation - Service1.cs:
public class Service1 : IService1
{
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}
}
Host - Program.cs:
class Program
{
static void Main(string[] args)
{
var baseAddress = new Uri("http://localhost:8732/Design_Time_Addresses/Service1/");
using (var host = new ServiceHost(typeof(Service1), baseAddress))
{
// Enable metadata publishing.
var smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
host.Description.Behaviors.Add(smb);
// Open the ServiceHost to start listening for messages. Since no endpoints are
// explicitly configured, the runtime will create one endpoint per base address
// for each service contract implemented by the service.
host.Open();
Console.WriteLine("The service is ready at {0}", baseAddress);
Console.WriteLine("Press <Enter> to stop the service.");
Console.ReadLine();
host.Close();
}
}
}
Client - Program.cs:
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Press <Enter> to proceed.");
Console.ReadLine();
var binding = new BasicHttpBinding();
var endpoint = new EndpointAddress("http://localhost:8732/Design_Time_Addresses/Service1/");
var channelFactory = new ChannelFactory<IService1>(binding, endpoint);
// Create a channel.
IService1 wcfClient1 = channelFactory.CreateChannel();
string s = wcfClient1.GetData(42);
Console.WriteLine(s);
((IClientChannel)wcfClient1).Close();
Console.WriteLine("Press <Enter> to quit the client.");
Console.ReadLine();
}
}
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.
I know that I can point to some SOAP web service by adding web reference using visual studio.
But I need to do it from code.
How can I manually create web reference object in code and access all methods from that object?
Basically I want to avoid generating proxy classes.
If you can get a copy of the service contract (interface)(svcUtil can help with this) then you can include it in your project and use the ChannelFactory class to dynamically create a channel for the client to communicate with the service.
I tend to encapsulate it all up in a SAL (Service Application Layer) to re-use as required.
This is a simple (and in no way complete!) example demonstrating how to connect to a fictious time service and call the GetTime() operation without using a VS generated proxy:
public class TimeSAL : IDisposable
{
private ChannelFactory<ITimeService> timeServiceProxyFactory;
private ITimeService timeServiceProxy;
private ITimeService TimeService
{
get
{
//create channel factory if not there
if (timeServiceProxyFactory == null)
timeServiceProxyFactory = new ChannelFactory<ITimeService>(new BasicHttpBinding(), new EndpointAddress("http://url_to_my_timeservice_endpoint")); //
if (timeServiceProxy == null)
timeServiceProxy = amlProxyFactory.CreateChannel();
return timeServiceProxy;
}
}
public string GetTime()
{
return TimeService.GetTime();
}
public void Dispose()
{
//dispose of ChannelFactory and proxy.
//ensure you check for comm faults to abort before closing
}
}
Now I can use this SAL throughout my code as necessary:
....
using(TimeSAL timeSAL = new TimeSAL())
{
myBusinessObject.CurrentTime = timeSAL.GetTime();
}
....
If you are unable to get your hands on a copy of the service contract, a long-winded way is to handcraft the soap request. Fiddler or soapUI can help with what the message should look like.
Hope some of this helps.
I have two VSPackages. The first one provides a global service. Both VSPackages consume the service.
The service is defined as explained in MSDN "How To: Register a Service". I left out the ComVisibleAttribute, because the how-to says it's only required if the service needs to be available in unmanaged code, which it doesn't. The interfaces are like
[Guid("5A72348D-617B-4960-B07A-DC6CC5AA7675")]
public interface SMessageBus {}
[Guid("04A499BA-CE09-48AF-96D5-F32DEAF0754C")]
public interface IMessageBus { ... }
The service-providing package follows MSDN "How To: Provide a Service". It looks like:
[<package atttributes>]
[ProvideService(typeof(SMessageBus))]
public sealed class MessageBusProviderPackage : Package
{
public MessageBusProviderPackage()
{
var serviceContainer = this as IServiceContainer;
var serviceCreatorCallback = new ServiceCreatorCallback(CreateMessageBusService);
serviceContainer.AddService(typeof(SMessageBus), serviceCreatorCallback, true);
}
private object CreateMessageBusService(IServiceContainer container, Type serviceType)
{
// this gets called and returns a new bus instance
return (serviceType == typeof(SMessageBus)) ? new MyMessageBus() : null;
}
protected override void Initialize()
{
// this is called after the package was constructed
// the call leads to the service being created by CreateMessageService()
var messageBus = GetService(typeof(SMessageBus)) as IMessageBus;
// the bus is retrieved correctly
...
}
}
This other package is declared like
[<package attributes>]
[ProvideAutoLoad(VSConstants.UICONTEXT.NoSolution_string)]
public sealed class MessageGeneratorPackage : Package
{
protected override void Initialize()
{
// the call below is reached first, in its course the provider is loaded
var service = GetService(type(SMessageBus));
// this point is reached last, but service is null
...
}
}
I debugged through the startup phase and found that the MessageGeneratorPackage gets created and initialized first. This means that the package was sited. When the GetService() call in Initialize() is reached, VS loads my service provider, i.e., the ProvideServiceAttribute correctly marks the MessageBusProviderPackage as provider of the SMessageBus service. The provider package gets instantiated and its Initialize() method gets called, wherein the service is retrieved successfully. Afterwards the initialization of the consumer package continues, but the service request returns null. It seems to me that all requirements stated in MSDN "How To: Troubleshoot Services" are fulfilled. Can anyone tell me what I am missing?
Found the answer myself... the overrides of Initialize() need to call base.Initialize() since that is were registered services actually get promoted to the parent service containers.
I am trying to make a simple windows service that maintains a queue of integers and accepts new integers from other applications via a WCF call. My current implementation seems to maintain separate queues for each application which communicates with it, which is not what I want.
I started by following the instructions at from Microsoft on How to: Host a WCF Service in a Managed Windows Service.
My WindowsService class looks like this:
public class MyWindowsService : ServiceBase{
public ServiceHost serviceHost = null;
public MyWindowsService(){
ServiceName = "AdHocReportService";
}
public static void Main(){
ServiceBase.Run(new MyWindowsService());
}
protected override void OnStart(string[] args){
if (serviceHost != null)
serviceHost.Close();
serviceHost = new ServiceHost(typeof(MyService));
serviceHost.Open();
}
protected override void OnStop(){
if (serviceHost != null){
serviceHost.Close();
serviceHost = null;
}
}
}
In my Service class I have a queue and an Add method. The add method returns the count of items in the queue after the add. The code looks like this:
public class MyService : IMyService
{
private Queue<int> myQueue= new Queue<int>();
public int Add(int reportId)
{
myQueue.Enqueue(reportId);
return myQueue.Count;
}
}
Lastly, I test my service using the following code in a ConsoleApp:
MyServiceClient client = new MyServiceClient();
int count = client.Add(10);
Console.WriteLine(count); //prints 1
count = client.Add(25);
Console.WriteLine(count); //prints 2
Console.ReadLine();
I would expect this to print 1 and 2 the first time my test is run, then 3 and 4 the second time, and then 5 and 6 the third and so on. However, it simply returns 1 and 2 each time, as if the Console App is instantiating the object itself and not operating on the object inside the Windows Service. What am I not understanding?
I think you want a singleton WCF service. See here.
By default, the instance mode for a WCF service is per-call. So an instance of your service is being created by the host for each call you make.
Note: When using a singleton service, your operations need to be thread safe. So I suggest switching from a Queue to a ConcurrentQueue, so you can handle multiple concurrent clients.
Alternative: Use a MSMQ binding. This will ensure you that all of your incoming messages are queued out of process, therefore persisted between restarts too.