Adding WCF service to existing application? - c#

I have an existing application which now has the requirement to be interacted with from a mobile device. The mobile device has a wifi connection, and would be connecting to the PC hosting the main application on a LAN. The mobile device simply needs to add/edit/find/delete objects the main application is maintaining. The main application already encapsulates his functionality in some simple repository classes.
I believe the approach would be to add a WCF service to the main application which exposes a set of methods the mobile device can call against. However I have looked up WCF today and tried to setup an example application, but when called the WCF methods it is unable to access any data, as such I feel the WCF service is running in its own application domain and as such has no access to the same static classes in the main application.
If I setup a WCF service project in VS 2008/2010, how can I run it under the same application domain as the main WinForms application, so that a remote application on the LAN can communicate with it to get data from the application.
Below is my sample WinForm
using System;
using System.ServiceModel;
using System.Windows.Forms;
using DataProject;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public TestDataProject.DataStore Datastore = TestDataProject.DataStore.GetInstance();
public Form1()
{
InitializeComponent();
Datastore.Add(new MyObj { ID = 1, Data = "hello" });
Datastore.Add(new MyObj { ID = 2, Data = "world" });
Datastore.Add(new MyObj { ID = 3, Data = "item3" });
Datastore.Add(new MyObj { ID = 4, Data = "item4" });
Datastore.Add(new MyObj { ID = 5, Data = "fiver" });
}
}
}
What I need from a WCF service, is access to TestDataProject.DataStore.GetInstance();
Edit
I achieved this by
using System;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Windows.Forms;
using DataProject;
using TestDataProject;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public TestDataProject.DataStore Datastore = TestDataProject.DataStore.GetInstance();
public Form1()
{
InitializeComponent();
Datastore.Add(new MyObj { ID = 1, Data = "hello" });
Datastore.Add(new MyObj { ID = 2, Data = "world" });
Datastore.Add(new MyObj { ID = 3, Data = "item3" });
Datastore.Add(new MyObj { ID = 4, Data = "item4" });
Datastore.Add(new MyObj { ID = 5, Data = "fiver" });
ServiceHost host = new ServiceHost(typeof(SimpleService),
new Uri("http://localhost:8001/MetadataSample"));
try
{
// Check to see if the service host already has a ServiceMetadataBehavior
ServiceMetadataBehavior smb = host.Description.Behaviors.Find<ServiceMetadataBehavior>();
// If not, add one
if (smb == null)
smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
host.Description.Behaviors.Add(smb);
// Add MEX endpoint
host.AddServiceEndpoint(
ServiceMetadataBehavior.MexContractName,
MetadataExchangeBindings.CreateMexHttpBinding(),
"mex"
);
// Add application endpoint
host.AddServiceEndpoint(typeof(ISimpleService), new WSHttpBinding(), "");
// Open the service host to accept incoming calls
host.Open();
// The service can now be accessed.
Console.WriteLine("The service is ready.");
Console.WriteLine("Press <ENTER> to terminate service.");
Console.WriteLine();
Console.ReadLine();
// Close the ServiceHostBase to shutdown the service.
//host.Close();
}
catch (CommunicationException commProblem)
{
Console.WriteLine("There was a communication problem. " + commProblem.Message);
Console.Read();
}
}
public void Display(string msg)
{
MessageBox.Show(msg);
}
}
[ServiceContract]
public interface ISimpleService
{
[OperationContract]
string Test();
[OperationContract]
string GetOBJDesc(int id);
[OperationContract]
MyObj GetObject(int id);
}
public class SimpleService : ISimpleService
{
#region Implementation of ISimpleService
public string Test()
{
return "Hello world";
}
public string GetOBJDesc(int value)
{
MyObj obj = DataStore.GetInstance().Get(value);
if (obj != null)
{
return obj.Data;
}
return "";
}
public MyObj GetObject(int id)
{
return DataStore.GetInstance().Get(id);
}
#endregion
}
}
With app.config containing
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<services>
<service name="WindowsFormsApplication1.SimpleService">
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="SimpleServiceBehavior">
<serviceMetadata httpGetEnabled="True" policyVersion="Policy15" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
I could then use WCF Test Client on the url http://localhost:8001/MetadataSample
The main issue I suffered from was my Service starting automatically, this can be disabled in VS2010 by a project setting. And the other issue was UAC, given Visual studio was not set to be a administrator the debugger failed to host a service, this was fixed by adding a WindowsFormApplication1.MANIFEST file containing
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">”
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">”
<security>
<requestedPrivileges>
<requestedExecutionLevel level="requireAdministrator"/>
</requestedPrivileges>
</security>
</trustInfo>
</assembly>

You have created a WCF Web Service project, which will be running inside the web service process (normally IIS), not inside your windows forms process and so it won't have any access to any data in static classes and properties in the Windows Forms process.
It sounds like your simplest option is to host the WCF service inside your windows forms application instead. I don't want to go into too much detail on how to do this as there are a number of resources already available on the web (also I can hardly claim to be an expert!), but you might want to try the following article as a starting point:
Hosting WCF services in a Windows Forms Application

WCF services are deployed in the same assembly as the rest of the application and should be able to access any classes in it. Perhaps you have used a different namespace. If this is the case, use a fully qualified name or a using statement.

Related

What replaces WCF in .Net Core?

I am used to creating a .Net Framework console application and exposing a Add(int x, int y) function via a WCF service from scratch with Class Library (.Net Framework). I then use the console application to proxy call this function within the server.
However if I use Console App (.Net Core) and a Class Library (.Net Core) the System.ServiceModel is not available. I have done some Googling but I haven't figured out what "replaces" WCF in this instance.
How do I expose a Add(int x, int y) function within a class library to a console application all within .Net Core? I see System.ServiceModel.Web, and since this is trying to be cross platform do I have to create a RESTful service?
You can use gRPC for hosting web services inside .NET core application.
Introduction
gRPC is a high performance, open source RPC framework initially developed by Google.
The framework is based on a client-server model of remote procedure calls. A client application can directly call methods on a server application as if it was a local object.
Example
Server Code
class Program
{
static void Main(string[] args)
{
RunAsync().Wait();
}
private static async Task RunAsync()
{
var server = new Grpc.Core.Server
{
Ports = { { "127.0.0.1", 5000, ServerCredentials.Insecure } },
Services =
{
ServerServiceDefinition.CreateBuilder()
.AddMethod(Descriptors.Method, async (requestStream, responseStream, context) =>
{
await requestStream.ForEachAsync(async additionRequest =>
{
Console.WriteLine($"Recieved addition request, number1 = {additionRequest.X} --- number2 = {additionRequest.Y}");
await responseStream.WriteAsync(new AdditionResponse {Output = additionRequest.X + additionRequest.Y});
});
})
.Build()
}
};
server.Start();
Console.WriteLine($"Server started under [127.0.0.1:5000]. Press Enter to stop it...");
Console.ReadLine();
await server.ShutdownAsync();
}
}
Client Code
class Program
{
static void Main(string[] args)
{
RunAsync().Wait();
}
private static async Task RunAsync()
{
var channel = new Channel("127.0.0.1", 5000, ChannelCredentials.Insecure);
var invoker = new DefaultCallInvoker(channel);
using (var call = invoker.AsyncDuplexStreamingCall(Descriptors.Method, null, new CallOptions{}))
{
var responseCompleted = call.ResponseStream
.ForEachAsync(async response =>
{
Console.WriteLine($"Output: {response.Output}");
});
await call.RequestStream.WriteAsync(new AdditionRequest { X = 1, Y = 2});
Console.ReadLine();
await call.RequestStream.CompleteAsync();
await responseCompleted;
}
Console.WriteLine("Press enter to stop...");
Console.ReadLine();
await channel.ShutdownAsync();
}
}
Shared Classes between Client and Server
[Schema]
public class AdditionRequest
{
[Id(0)]
public int X { get; set; }
[Id(1)]
public int Y { get; set; }
}
[Schema]
public class AdditionResponse
{
[Id(0)]
public int Output { get; set; }
}
Service descriptors
using Grpc.Core;
public class Descriptors
{
public static Method<AdditionRequest, AdditionResponse> Method =
new Method<AdditionRequest, AdditionResponse>(
type: MethodType.DuplexStreaming,
serviceName: "AdditonService",
name: "AdditionMethod",
requestMarshaller: Marshallers.Create(
serializer: Serializer<AdditionRequest>.ToBytes,
deserializer: Serializer<AdditionRequest>.FromBytes),
responseMarshaller: Marshallers.Create(
serializer: Serializer<AdditionResponse>.ToBytes,
deserializer: Serializer<AdditionResponse>.FromBytes));
}
Serializer/Deserializer
public static class Serializer<T>
{
public static byte[] ToBytes(T obj)
{
var buffer = new OutputBuffer();
var writer = new FastBinaryWriter<OutputBuffer>(buffer);
Serialize.To(writer, obj);
var output = new byte[buffer.Data.Count];
Array.Copy(buffer.Data.Array, 0, output, 0, (int)buffer.Position);
return output;
}
public static T FromBytes(byte[] bytes)
{
var buffer = new InputBuffer(bytes);
var data = Deserialize<T>.From(new FastBinaryReader<InputBuffer>(buffer));
return data;
}
}
Output
Sample client output
Sample Server output
References
https://blogs.msdn.microsoft.com/dotnet/2018/12/04/announcing-net-core-3-preview-1-and-open-sourcing-windows-desktop-frameworks/
https://grpc.io/docs/
https://grpc.io/docs/quickstart/csharp.html
https://github.com/grpc/grpc/tree/master/src/csharp
Benchmarks
http://csharptest.net/787/benchmarking-wcf-compared-to-rpclibrary/index.html
WCF is not supported in .NET Core since it's a Windows specific technology and .NET Core is supposed to be cross-platform.
If you are implementing inter-process communication consider trying the IpcServiceFramework project.
It allows creating services in WCF style like this:
Create service contract
public interface IComputingService
{
float AddFloat(float x, float y);
}
Implement the service
class ComputingService : IComputingService
{
public float AddFloat(float x, float y)
{
return x + y;
}
}
Host the service in Console application
class Program
{
static void Main(string[] args)
{
// configure DI
IServiceCollection services = ConfigureServices(new ServiceCollection());
// build and run service host
new IpcServiceHostBuilder(services.BuildServiceProvider())
.AddNamedPipeEndpoint<IComputingService>(name: "endpoint1", pipeName: "pipeName")
.AddTcpEndpoint<IComputingService>(name: "endpoint2", ipEndpoint: IPAddress.Loopback, port: 45684)
.Build()
.Run();
}
private static IServiceCollection ConfigureServices(IServiceCollection services)
{
return services
.AddIpc()
.AddNamedPipe(options =>
{
options.ThreadCount = 2;
})
.AddService<IComputingService, ComputingService>();
}
}
Invoke the service from client process
IpcServiceClient<IComputingService> client = new IpcServiceClientBuilder<IComputingService>()
.UseNamedPipe("pipeName") // or .UseTcp(IPAddress.Loopback, 45684) to invoke using TCP
.Build();
float result = await client.InvokeAsync(x => x.AddFloat(1.23f, 4.56f));
It seems, that there will be a CoreWCF project maintained by .NET Foundation with Microsoft support.
More details at Welcoming Core WCF to the .NET Foundation
Initially only netTcp and http transport will be implemented.
WCF does many things; it is an easy way to remote procedure calls between two applications (processes) on one machine, using named pipes; it can be a high volume internal client-server communication channel between .NET components, using binary serialization over TCPIP; or it can provide a standardised cross-technology API, e.g. via SOAP. It even has support for things like asynchronous messaging, via MSMQ.
For .NET Core, there are different replacements based on the purpose.
For cross-platform API, you would replace this with a REST service using ASP.NET.
For inter-process connections, or client-server connection, gRPC would be good, with an excellent answer given by #Gopi.
So the answer to "What replaces WCF" depends on what you are using it for.
There is a community repo https://github.com/CoreWCF/CoreWCF that implements some parts of WCF. You can use it to support some simple WCF services. However not all features are supported.
What's new in .NET 5 / Windows Communication Foundation
The original implementation of Windows Communication Foundation (WCF) was only supported on Windows. However, there is a client port available from the .NET Foundation. It is entirely open source, cross platform, and supported by Microsoft.
The community maintains the server components that complement the aforementioned client libraries. The GitHub repository can be found at CoreWCF. The server components are not officially supported by Microsoft. For an alternative to WCF, consider gRPC.
So from my research the best solution does not have the auto-generated proxy classes. This best solution is to create a RESTful service and to serialise the response body into model objects. Where the models are the usual model objects found in the MVC design pattern.
Thank you for your responses
You can also self-host ASP.NET Core Web API.
<!-- SelfHosted.csproj -->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<!-- see: https://learn.microsoft.com/en-us/aspnet/core/migration/22-to-30?view=aspnetcore-3.1&tabs=visual-studio#framework-reference -->
<FrameworkReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="3.1.0" />
</ItemGroup>
</Project>
// Program.cs
using System.IO;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
namespace SelfHosted
{
class Program
{
static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args)
{
// see: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/generic-host?view=aspnetcore-3.1
return Host.CreateDefaultBuilder(args)
.ConfigureHostConfiguration(configHost =>
{
configHost.SetBasePath(Directory.GetCurrentDirectory());
configHost.AddJsonFile("appsettings.json", optional: true);
configHost.AddEnvironmentVariables(prefix: "SelfHosted_");
configHost.AddCommandLine(args);
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.CaptureStartupErrors(true);
webBuilder.UseStartup<Startup>();
});
}
}
}
// Startup.cs
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace SelfHosted
{
public class Startup
{
public Startup(IConfiguration configuration, IWebHostEnvironment env)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
// see: https://github.com/aspnet/AspNetCore.Docs/tree/master/aspnetcore/web-api/index/samples/3.x
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
// Controllers\TestController.cs
using System.Net.Mime;
using Microsoft.AspNetCore.Mvc;
namespace SelfHosted.Controllers
{
[ApiController]
[Produces(MediaTypeNames.Application.Json)]
[Route("[controller]")]
public class HelloController : SelfHostedControllerBase
{
[HttpGet]
public ActionResult<string> HelloWorld() => "Hello World!";
[HttpGet("{name}")]
public ActionResult<string> HelloName(string name) => $"Hello {name}!";
}
}
There is a .NET Core port available: https://github.com/dotnet/wcf
It's still in preview, but they are actively developing it.
As today all the WCFCore selfhost Available are not that easy to install and use.
The best for HostedService it will be the alternatives as gRPC showed in the previous answer and notice that in 1 year can change many things sure WCF is supported in Core only as a client that works fine.
// I found a way to implement WCF client proxy in .Net 6.0 (Core):
//--------------------------------------WCF duplex fix------------------------------
// I found a way to fix my problem.. it took me a week of research
// So here it is:
// How to generate WCF Service (.Net Framework 4.8) proxy in client (.Net 6.0):
// If using a callback I need duplex communication
[ServiceContract(CallbackContract = typeof(IEventsService))]
// Just do as explain here but dont expect it to work for Client .Net 6.0 it will
// only work for client .net Framework 4.8 as Wcf service is .Net Framework 4.8
https://www.codeproject.com/articles/663333/understanding-events-in-wcf#:~:text=Background%20While%20events%20in%20WCF%20are%20nothing%20more,typical%20relationship%20between%20a%20client%20and%20a%20service.
// so here is what I need to do to make it work in .Net 6.0 client:
// Use netHttpBinding for duplex
// Put this on web.config of your Wcf service
<service name="HomeManagerWcfService.HomeManagerService" behaviorConfiguration="HomeManagerServiceBehavior">
<host>
<baseAddresses>
<add baseAddress="http://localhost:53318/HomeManagerService"/>
</baseAddresses>
</host>
<endpoint address="" binding="netHttpBinding" contract="HomeManagerWcfService.IHomeManagerService"/>
<!--<endpoint address="" binding="wsDualHttpBinding" contract="HomeManagerWcfService.IHomeManagerService"/>-->
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<!-- HomeManagerService Behavior -->
<behavior name="HomeManagerServiceBehavior">
<serviceMetadata httpGetEnabled="true"/>
<serviceDebug includeExceptionDetailInFaults="true "/>
</behavior>
</serviceBehaviors>
</behaviors>
// Generate files for your client proxy on VisualStudio.Tools.Command line.Developer command prompt
// The WCF service must be running
svcutil http://localhost:53318/HomeManagerService.svc
//copy both files generated in your client project.
// if using the VS UI generator (Managed connected service) it won't work, there is a bug in it I guess.
// I also need System.ServiceModel.Http
// & I need System.ServiceModel.Duplex
// in the client
// use NetHttpBinding for duplex communication
// Use IHomeManagerServiceCallback to implement the callback function
// where you want it to run the callback
InstanceContext iCntxt = new InstanceContext(this);// "this" is where i implement the callback for my case
var endpointAddress = new EndpointAddress("http://localhost:53318/HomeManagerService.svc");
var binding = new NetHttpBinding();
var factory = new DuplexChannelFactory<IHomeManagerService>(iCntxt, binding, endpointAddress);
var clientWCF = factory.CreateChannel();
EmailMessage emailMessage = new EmailMessage();
emailMessage.Name = "ww23";
emailMessage.Body = "SSWDDW223";
emailMessage.EmailAddress = "EMAIL AD dsf2223";
clientWCF.SubscribeCalculatedEvent(); // where we register to the event on the server
clientWCF.AddEmail(emailMessage); // the callback event call is in this function on the server
//----------------------------------------------------------------------------------
// for IIS
// In order to make sure this mapping appears you need to go to control panel
-> turn windows features on or off
-> .NET Framework 4.8 Advanced Services
-> WCF Services -> HTTP Activation
https://devblogs.microsoft.com/dotnet/corewcf-v1-released/
April 2022, WCF is available on the following .NET core versions
.NET Core 3.1
.NET 5 & 6
I have found the ServiceWire package to be an excellent replacement for NamedPipes and WCF, especially if you do not need to queue requests.

Configure endpoint programmatically

First of, let me start by saying I have no experience of doing endpoint/networking type of stuff, so my question might seem a bit stupid, so please bear with me :)
I am working on porting an App written for Windows Phone 7 onto Windows 8 (Metro app). In the original App, there was a ServicesReference.ClientConfig file which defined the URL, bindings and other bits for the App to connect to the server (addresses changed):
<client>
<endpoint address="https://someurl.com/userservice.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_UserServices"
contract="UserApi.UserServices" name="User_Services" />
<endpoint address="https://someurel.com/dataservice.svc"
binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_DataServices"
contract="DataApi.DataServices" name="Data_Services" />
Also, in the WP7 project, there were already two "Service References" added (one for User Services and one for Data Services), with supporting Reference.cs generated files. When I tried adding the service references into the Win8 project (VS2012), it generated a blank reference.cs file, so I simply added the Reference.cs file from the WP7 project into the W8 project, and also copied the ServiceReferences.ClientConfig file into the W8 project (so in terms of directory structure, it looked identical to the WP7 project).
I think these Reference.cs files are the ones which provide the interface for the contracts
Now, when I run my W8 app, I get an error during the part where it needs access to the service:
InvalidOperationException was unhandled by user code
Could not find endpoint element with name
'User_Services' and contract
'UserApi.UserServices' in the ServiceModel client
configuration section. This might be because no configuration file was
found for your application, or because no endpoint element matching
this name could be found in the client element.
So I figured the App isn't using the ServicesReferces.ClientConfig file to pickup the endpoints and network adresses, or it wasn't finding the Reference.cs files which I have importes into the project. So, assuming first it is not finding the endpoints correctly through the ServicesReferences.ClientConfig file, is it possible to do the same in code?
All I got so far is this:
BasicHttpBinding binding = new BasicHttpBinding();
EndpointAddress endpoint = new EndpointAddress(new Uri("https://someurl.com/someservice.svc"));
but I don't how to take this further (I added this into App.xaml.cs)
Hope the question makes sense. If there is any further information you need, please let me know and I will try to find out about it while I go and educate myself more on this endpoint business
Thanks in advance
I had the same problem and I tried to wrap everything in some classes.. This is what I did:
First of all, I created a class called ClientService where it creates and wraps the EndpointAdress:
EDIT for Win8:
public class ClientService
{
public Type ContractType {get;set;}
public EndpointAdress EndpointAdress {get;set;}
public Binding Binding { get; private set; }
public ClientService(Type contractType)
{
ContractType = contractType;
CreateEndpoint();
CreateBinding();
}
private void CreateEndpoint()
{
EndpointAdress = new EndpointAddress(....) //You can get some parameters about the service adress in the Constructor of this class
}
private void CreateBinding()
{
Binding = new BasicHttpBinding(); //Or your specific Binding
}
}
Once I have this, I create a static class with all my client registrations. I add all of them once I start my app. Something like this:
ClientServices.AddClientService(new ClientService(typeof(IYourService));
public static class ClientServices
{
private static readonly Dictionary<Type, ClientService> _clientServices;
static ClientServices()
{
_clientServices = new Dictionary<Type, ClientService>();
}
public static void AddClientService(ClientService clientService)
{
if (!_clientServices.ContainsKey(clientService.ContractType))
{
_clientServices.Add(clientService.ContractType, clientService);
}
}
public static ClientService GetClientServiceBy(Type contract)
{
if (_clientServices.ContainsKey(contract))
{
return _clientServices[contract];
}
throw new ArgumentException(string.Format("The contract's Type {0} is not registered. Please register the client's endpoint.", contract));
}
}
So, when my application starts I have all my client endpoints registered in a static class. Now when I want to call a service I have a wrapper called ServiceInvoker. I use it like this whenever I want to call a Service:
var invoker = new ServiceInvoker();
var result = invoker.InvokeService<IMyService, MyObjectReturnType>(
proxy => proxy.DoSomething(myParameters));
return result;
Where InvokeService looks like this:
public TResult InvokeService<TServiceContract, TResult>(Func<TServiceContract, TResult> invokeHandler) where TServiceContract : class
{
ICommunicationObject communicationObject;
var arg = CreateCommunicationObject<TServiceContract>(out communicationObject);
var result = default(TResult);
try
{
result = invokeHandler(arg);
}
catch (Exception ex)
{
throw;
}
finally
{
try
{
if (communicationObject.State != CommunicationState.Faulted)
communicationObject.Close();
}
catch
{
communicationObject.Abort();
}
}
return result;
}
private TServiceContract CreateCommunicationObject<TServiceContract>(out ICommunicationObject communicationObject)
where TServiceContract : class
{
var clientService = GetClientService(typeof(TServiceContract));
var arg = new ChannelFactory<TServiceContract>(clientService.Binding, clientService.EndpointAdress).CreateChannel();
communicationObject = (ICommunicationObject)arg;
return arg;
}
private ClientService GetClientService(Type type)
{
var clientService = ClientServices.GetClientServiceBy(type);
return clientService;
}
The main problem here is that since DLL's cannot be referenced in a Windows Store App, the only way to make this example work is to copy all Service Interfaces and possible ojects that we transfer to a Class Library (Windows Store apps). This way we will be able to create a channel and connect to the WCF service.
Copying the interfaces can be a workaround but is NOT a good approach.
Service Reference or other code generation tools are the way to go.
In addition, async and await are not possible in this scenario.
**ServiceInvoker apprach is used from creating WCF ChannelFactory

Meta data issue wcf rest

So trying to create a rest service but I keep getting an error:
If I try to run it in a browser I get : The type 'WcfService2.Service1', provided as the Service attribute value in the ServiceHost directive could not be found.
namespace WcfService2
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IService1" in both code and config file together.
[ServiceContract]
public class HelloWorldService
{
[OperationContract]
[WebGet(UriTemplate = "")]
public string HelloWorld()
{
return "Hello world!";
}
}
}
namespace WcfService2
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Service1" in code, svc and config file together.
class Program
{
static void Main(string[] args)
{
WebHttpBinding binding = new WebHttpBinding();
WebServiceHost host =
new WebServiceHost(typeof(HelloWorldService));
host.AddServiceEndpoint(typeof(HelloWorldService),
binding,
"http://localhost:8000/Hello");
host.Open();
Console.WriteLine("Hello world service");
Console.WriteLine("Press <RETURN> to end service");
Console.ReadLine();
}
}
}
You're defining a REST-style WCF service using the WebHttpBinding.
The WCF Test Client is only usable for SOAP services - not for REST services. REST services can be tested using your regular browser, or tools like Fiddler.
The error message you're getting would almost indicate that you have a *.svc lying around somewhere, too, that gets in your way. Is that the case?

How to create a winform app in visual studio 2010 to host a wcf service

I have a working skeleton WCF service. I want to host it in a winform app with a simple start and stop button.
This is how I host in a console app, easy to change to win app
public Program()
{
Console.WriteLine("This is the SERVER console");
var myUri = new Uri[1];
myUri[0] = new Uri(ConfigurationManager.AppSettings["baseAddress"]);
var timeEntryService = new WCFTimeEntryService();
var host = new ServiceHost(timeEntryService, myUri);
host.Open();
Console.WriteLine("Service Started!");
Console.WriteLine("Click any key to close...");
Console.ReadKey();
host.Close();
}
EDIT
First you need an interface that both client and server will use to communicate.
using System;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Data;
namespace TimeEntryService
{
[ServiceContract]
public interface ITimeEntry
{
[OperationContract]
string Ping();
}
}
Then you create the class that will do the work when a client calls.
using System.ServiceModel;
using System.Data;
namespace TimeEntryService
{
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class WCFTimeEntryService : ITimeEntry
{
public string Ping()
{
return "Pong";
}
}
}
Then make sure you update your App.config (Use WCF Service Configuration Editor)
In my VS2010 its under Tools -> Service Configuration Editor
(Not sure if you need to do something to get it to show there).
When it runs up, you can use the WCF Test Client to confirm its working.
C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE\WcfTestClient.exe

WCF: Accessing the service instance from the server

Context:
I need to develop a monitoring server that monitors some of our applications (these applications are in c#). So I decided to develop the system with WCF which seems suitable for my needs.
These applications must register themselves to the monitoring server when they start. After that the monitoring server can call the methods Start or Stop of these applications.
Everything is completely executed on the same machine, nothing needs to be executed remotely.
So I developed a good prototype and everything works fine. Each application registers itself to the monitoring server.
Question:
ApplicationRegistrationService (see the code below) is the implementation of the monitoring service and it is a singleton instance due to the ServiceBehavior attribute.
Here my problem: I want to access the content of ApplicationRegistrationService per example, the number of connected applications from my server (ConsoleMonitoringServer in the example). But, I am not sure how to achieve this.
Do I need to create a channel in my server to the service like I did in my clients (ConsoleClient) or it exists a better way to achieve this?
Code:
The code is very simplified for the purpose of this question:
//The callback contract interface
public interface IApplicationAction
{
[OperationContract(IsOneWay = true)]
void Stop();
[OperationContract(IsOneWay = true)]
void Start();
}
[ServiceContract(SessionMode = SessionMode.Required,
CallbackContract = typeof(IApplicationAction))]
public interface IApplicationRegistration
{
[OperationContract]
void Register(Guid guid, string name);
[OperationContract]
void Unregister(Guid guid);
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single,
ConcurrencyMode = ConcurrencyMode.Multiple)]
public class ApplicationRegistrationService : IApplicationRegistration
{
//IApplicationRegistration Implementation
}
public class ApplicationAction : IApplicationAction
{
//IApplicationAction Implementation
}
Console application for this example
class ConsoleClient
{
static void Main(string[] args)
{
ApplicationAction actions = new ApplicationAction();
DuplexChannelFactory<IApplicationRegistration> appRegPipeFactory =
new DuplexChannelFactory<IApplicationRegistration>(actions,
new NetNamedPipeBinding(), new EndpointAddress("net.pipe://localhost/AppReg"));
IApplicationRegistration proxy = appRegPipeFactory.CreateChannel();
proxy.Register(Guid.Empty, "ThisClientName");
//Do stuffs
}
}
Console server for this example
class ConsoleMonitoringServer
{
static void Main(string[] args)
{
using (ServiceHost host = new ServiceHost(typeof(ApplicationRegistrationService),
new Uri[]{ new Uri("net.pipe://localhost")}))
{
host.AddServiceEndpoint(typeof(IApplicationRegistration),
new NetNamedPipeBinding(), "AppReg");
host.Open();
//Wait until some write something in the console
Console.ReadLine();
host.Close();
}
}
}
Finally, I find the answer and it was pretty easy. I just need to create the service instance and pass the reference to the constructor of ServiceHost.
So I need to replace the following code:
using (ServiceHost host = new ServiceHost(typeof(ApplicationRegistrationService),
new Uri[]{ new Uri("net.pipe://localhost")}))
by :
ApplicationRegistrationService myService = new ApplicationRegistrationService();
using (ServiceHost host = new ServiceHost(myService,
new Uri[]{ new Uri("net.pipe://localhost")}))
If you mean you'd like two way communication between your monitoring service and your registered services or nodes, then you probably should be using two way communication in WCF also known as duplex communication. Very cool stuff.
http://www.codeproject.com/KB/WCF/WCF_Duplex_UI_Threads.aspx

Categories