WCF service in Windows 10 app - c#

I created a WCF service that retrieves data from a database and displays the information.
First question: When I submit the app to the store, does the WCF service get bundled with app, how does that work?
Second question: I noticed when the service is running, IIS is also running on my system, so what happens if a user doesn't have IIS or what happens when it runs on a windows phone.
Lastly, I noticed when IIS isn't running and I open the app, the app crashes, why does it do so, shouldn't it be able to start the service?
Please I am not an expert on this, and this is my first time using a WCF service, so be patient with me and give as much detail as possible.
Thanks.
WCF service:
namespace CustomerService
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Service1" in code, svc and config file together.
// NOTE: In order to launch WCF Test Client for testing this service, please select Service1.svc or Service1.svc.cs at the Solution Explorer and start debugging.
public class Service1 : IService1
{
SqlConnection sqlCon = new SqlConnection("Data Source=MOD;Initial Catalog=DB2;User ID=sa;Password=*********");
public Customer getCustomer()
{
try
{
sqlCon.Open();
string strSql = "SELECT * FROM Table_1";
DataSet ds = new DataSet();
SqlDataAdapter sqlDa = new SqlDataAdapter(strSql, sqlCon);
sqlDa.Fill(ds);
Customer objCus = new Customer();
objCus.Age = (int)ds.Tables[0].Rows[0][0];
objCus.Name = ds.Tables[0].Rows[0][1].ToString();
return objCus;
}
catch
{
return null;
}
finally
{
sqlCon.Close();
}
}
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}
public CompositeType GetDataUsingDataContract(CompositeType composite)
{
if (composite == null)
{
throw new ArgumentNullException("composite");
}
if (composite.BoolValue)
{
composite.StringValue += "Suffix";
}
return composite;
}
}
}
namespace CustomerService
{
// 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 interface IService1
{
[OperationContract]
string GetData(int value);
[OperationContract]
CompositeType GetDataUsingDataContract(CompositeType composite);
// TODO: Add your service operations here
[OperationContract]
Customer getCustomer();
}
// Use a data contract as illustrated in the sample below to add composite types to service operations.
[DataContract]
public class CompositeType
{
bool boolValue = true;
string stringValue = "Hello ";
[DataMember]
public bool BoolValue
{
get { return boolValue; }
set { boolValue = value; }
}
[DataMember]
public string StringValue
{
get { return stringValue; }
set { stringValue = value; }
}
}
[DataContract]
public class Customer
{
int age;
string name;
[DataMember]
public int Age
{
get { return age; }
set { age = value; }
}
[DataMember]
public string Name
{
get { return name; }
set { name = value; }
}
}
}
MainPage.xaml.cs
public sealed partial class MainPage : Page
{
ServiceReference1.Service1Client obj = new ServiceReference1.Service1Client();
public MainPage()
{
this.InitializeComponent();
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
ServiceReference1.Customer g = await obj.getCustomerAsync();
ageTB.Text = g.Age.ToString();
nameTB.Text = g.Name;
}
}

WCF Services are meant to be backend services. This means that it is not shipped with the app, but it is hosted somewhere else.
So:
Question1: No, it is not bundled to the app, you have to take care about the hosting
Question2: Visual Studio starts an IIS for you, but in production it should be hosted on a server. A Windows Phone app typically connects to it via the internet. One hosting option would be Azure…
Question3: If the service is not available the app should deal with it… (For example a try catch in the Button_Click event handler would do the work..)

Related

Wcf ChannelFactory

I have a question about WCF and Channel Factory usage.
On Host :
[ServiceContract]
public interface IGetMessage
{
[OperationContract]
string ShowMessage(Sample p, string Username, string Password);
}
[DataContract]
public class Sample
{
[DataMember]
public string Name { get; set; }
}
public string ShowMessage(Sample p, string Username, string Password)
{
return p.Name.ToString() + " - " + "Correct"; //Error line
}
On Client :
[ServiceContract()]
public interface IGetMessage
{
[OperationContract()]
string ShowMessage(Sample p, string Username, string Password);
}
[DataContract()]
public class Sample
{
[DataMember]
public string Name { get; set; }
}
private void button1_Click(object sender, EventArgs e)
{
Sample p1 = new Sample();
p1.Name = "ALEX";
BasicHttpBinding myBinding = new BasicHttpBinding();
EndpointAddress myEndpoint = new EndpointAddress("http://MYURL/GetMessage.svc");
using (var myChannelFactory = new ChannelFactory<IGetMessage>(myBinding, myEndpoint))
{
IGetMessage client = null;
try
{
client = myChannelFactory.CreateChannel();
MessageBox.Show(client.ShowMessage(p1, "abc","123"));
((ICommunicationObject)client).Close();
myChannelFactory.Close();
}
catch
{
(client as ICommunicationObject)?.Abort();
}
}
}
If I add on client as Service Reference, it works perfectly. I can get "ALEX - Correct" message.
When I test on WcfTestClient.exe, it works perfectly.
But, I have a problem when using on Winform with above codes.
When I check on WcfServer Trace Log and Message Log;
System.NullReferenceException - Object reference not set to an instance of an object. Line number:22
Line number 22:
p.Name.ToString() on the Host's GetMessage.cvs.cs file.
I think, there is no problem on host. Problem is Client side.
I'd like to ask you how I made a mistake on the client side?
Regards.
As mentioned in the comments, to solve the problem you only to do is adding the namespace property to ServiceContract and DataContract. be sure that the namespace is identical between the client and the server. the reason why we should do is an error occurred during serialization and deserialization, we must ensure that the service contract and data contract has a consistent namespace between the server and client.
Here is my example(Add this feature on both the server and client.)
[ServiceContract(Namespace ="http://mydomain")]
public interface IGetMessage
{
[OperationContract]
string ShowMessage(Sample p, string Username, string Password);
}
[DataContract(Namespace = "http://mydomain")]
public class Sample
{
[DataMember]
public string Name { get; set; }
}
Result.
Feel free to let me know if there is anything I can help with.

WCF Singleton-Service error: The service type provided could not be loaded as a service because it does not have a default constructor

I have the following strange issue with WCF, for which I cannot figure out the cause:
I am working with WCF to figure out, whether to use it for a remote-controlling API that I need to implement for a printer-like device. The device is controlled by a Windows-PC that runs the controller-software implemented in .Net. It for this software that I need to implement the API.
The service is self-hosting from inside the controller-software and I am currently figuring out how I can create a singleton-instance of the WCF service, so that I can create this instance with corresponding objects/classes from withing controller-software. I have gotten this to work using a reduced version, but oddly enough I am getting this warning, if the service does not include a default (parameter-less) constructor. Even weirder, I am doing exactly, what the exception is telling me in the second sentence (or at least I like to think I am). This exception is thrown in separate window with title WCF Service Host and the program continues to execute normally afterwards:
System.InvalidOperationException: The service type provided could not be loaded as a service because it does not have a default (parameter-less) constructor. To fix the problem, add a default constructor to the type, or pass an instance of the type to the host.
at System.ServiceModel.Description.ServiceDescription.CreateImplementation(Type serviceType)
at System.ServiceModel.Description.ServiceDescription.SetupSingleton(ServiceDescription serviceDescription, Object implementation, Boolean isWellKnown)
at System.ServiceModel.Description.ServiceDescription.GetService(Type serviceType)
at System.ServiceModel.ServiceHost.CreateDescription(IDictionary`2& implementedContracts)
at System.ServiceModel.ServiceHostBase.InitializeDescription(UriSchemeKeyedCollection baseAddresses)
at System.ServiceModel.ServiceHost..ctor(Type serviceType, Uri[] baseAddresses)
at Microsoft.Tools.SvcHost.ServiceHostHelper.CreateServiceHost(Type type, ServiceKind kind)
at Microsoft.Tools.SvcHost.ServiceHostHelper.OpenService(ServiceInfo info)
Here is the code that I am using to create the service. I commented the commented line in Service.cs, that contains the default constructor. Interestingly, when I include the default constructor (and therefore the error is never thrown) it is never called (I confirmed that by setting a breakpoint). I you uncommented it, the exception is not thrown.
Server.cs:
public class Server
{
private ServiceHost svh;
private Service service;
public Server()
{
service = new Service("A fixed ctor test value that the service should return.");
svh = new ServiceHost(service);
}
public void Open(string ipAdress, string port)
{
svh.AddServiceEndpoint(
typeof(IService),
new NetTcpBinding(),
"net.tcp://"+ ipAdress + ":" + port);
svh.Open();
}
public void Close()
{
svh.Close();
}
}
Service.cs:
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant,
InstanceContextMode = InstanceContextMode.Single)]
public class Service : IService
{
private string defaultString;
public Service(string ctorTestValue)
{
this.defaultString = ctorTestValue;
}
//// when this constructor is uncommented, I do not get the error
//public Service()
//{
// defaultString = "Default value from the ctor without argument.";
//}
public string GetDefaultString()
{
return defaultString;
}
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}
public CompositeType GetDataUsingDataContract(CompositeType composite)
{
if (composite == null)
{
throw new ArgumentNullException("composite");
}
if (composite.BoolValue)
{
composite.StringValue += "Suffix";
}
return composite;
}
public string Ping(string name)
{
Console.WriteLine("SERVER - Processing Ping('{0}')", name);
return "Hello, " + name;
}
static Action m_Event1 = delegate { };
static Action m_Event2 = delegate { };
public void SubscribeEvent1()
{
IMyEvents subscriber = OperationContext.Current.GetCallbackChannel<IMyEvents>();
m_Event1 += subscriber.Event1;
}
public void UnsubscribeEvent1()
{
IMyEvents subscriber = OperationContext.Current.GetCallbackChannel<IMyEvents>();
m_Event1 -= subscriber.Event1;
}
public void SubscribeEvent2()
{
IMyEvents subscriber = OperationContext.Current.GetCallbackChannel<IMyEvents>();
m_Event2 += subscriber.Event2;
}
public void UnsubscribeEvent2()
{
IMyEvents subscriber = OperationContext.Current.GetCallbackChannel<IMyEvents>();
m_Event2 -= subscriber.Event2;
}
public static void FireEvent1()
{
m_Event1();
}
public static void FireEvent2()
{
m_Event2();
}
public static Timer Timer1;
public static Timer Timer2;
public void OpenSession()
{
Timer1 = new Timer(1000);
Timer1.AutoReset = true;
Timer1.Enabled = true;
Timer1.Elapsed += OnTimer1Elapsed;
Timer2 = new Timer(500);
Timer2.AutoReset = true;
Timer2.Enabled = true;
Timer2.Elapsed += OnTimer2Elapsed;
}
void OnTimer1Elapsed(object sender, ElapsedEventArgs e)
{
FireEvent1();
}
void OnTimer2Elapsed(object sender, ElapsedEventArgs e)
{
FireEvent2();
}
}
IServices.cs:
public interface IMyEvents
{
[OperationContract(IsOneWay = true)]
void Event1();
[OperationContract(IsOneWay = true)]
void Event2();
}
// 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(CallbackContract = typeof(IMyEvents))]
public interface IService
{
[OperationContract]
string GetData(int value);
[OperationContract]
string GetDefaultString();
[OperationContract]
CompositeType GetDataUsingDataContract(CompositeType composite);
// TODO: Add your service operations here
[OperationContract]
string Ping(string name);
[OperationContract]
void SubscribeEvent1();
[OperationContract]
void UnsubscribeEvent1();
[OperationContract]
void SubscribeEvent2();
[OperationContract]
void UnsubscribeEvent2();
[OperationContract]
void OpenSession();
}
// Use a data contract as illustrated in the sample below to add composite types to service operations.
// You can add XSD files into the project. After building the project, you can directly use the data types defined there, with the namespace "WcfService.ContractType".
[DataContract]
public class CompositeType
{
bool boolValue = true;
string stringValue = "Hello ";
[DataMember]
public bool BoolValue
{
get { return boolValue; }
set { boolValue = value; }
}
[DataMember]
public string StringValue
{
get { return stringValue; }
set { stringValue = value; }
}
}
Main for starting the server:
static void Main(string[] args)
{
// start server
var server = new Server();
server.Open("localhost", "6700");
Console.WriteLine("Server started.");
Console.ReadLine();
server.Close();
}
The problem is caused by the WcfSvcHost running while you're debugging in Visual Studio. According to this, "WCF Service Host enumerates the services in a WCF service project, loads the project’s configuration, and instantiates a host for each service that it finds. The tool is integrated into Visual Studio through the WCF Service template and is invoked when you start to debug your project."
You don't need to use the WCF Service Host since you're self-hosting, so you can disable it through the project properties page for the project containing the service. You should see a tab for "WCF Options" on the property page. On that, turn off the "Start WCF Service Host when debugging ..." option.

Pass to my form WCF client message

I have Winforms application that host WCF service.
this is my Button connect event:
private void btnConnect_Click(object sender, EventArgs e)
{
try
{
// Returns a list of ipaddress configuration
IPHostEntry ips = Dns.GetHostEntry(Dns.GetHostName());
// Get machine ipaddress
IPAddress _ipAddress = IPAddress.Parse(tbServerIp.Text);
// Create the url that is needed to specify where the service should be started
urlService = "net.tcp://" + _ipAddress.ToString() + ":8000/MyService";
// Instruct the ServiceHost that the type that is used is a ServiceLibrary.service1
host = new ServiceHost(typeof(ServiceLibrary.service1));
host.Opening += new EventHandler(host_Opening);
host.Opened += new EventHandler(host_Opened);
host.Closing += new EventHandler(host_Closing);
host.Closed += new EventHandler(host_Closed);
// The binding is where we can choose what transport layer we want to use. HTTP, TCP ect.
NetTcpBinding tcpBinding = new NetTcpBinding();
tcpBinding.TransactionFlow = false;
tcpBinding.Security.Transport.ProtectionLevel = System.Net.Security.ProtectionLevel.EncryptAndSign;
tcpBinding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Windows;
tcpBinding.Security.Mode = SecurityMode.None; // <- Very crucial
// Add a endpoint
host.AddServiceEndpoint(typeof(ServiceLibrary.IService1), tcpBinding, urlService);
// A channel to describe the service. Used with the proxy scvutil.exe tool
ServiceMetadataBehavior metadataBehavior;
metadataBehavior = host.Description.Behaviors.Find<ServiceMetadataBehavior>();
if (metadataBehavior == null)
{
// This is how I create the proxy object that is generated via the svcutil.exe tool
metadataBehavior = new ServiceMetadataBehavior();
metadataBehavior.HttpGetUrl = new Uri("http://" + _ipAddress.ToString() + ":8001/MyService");
metadataBehavior.HttpGetEnabled = true;
metadataBehavior.ToString();
host.Description.Behaviors.Add(metadataBehavior);
urlMeta = metadataBehavior.HttpGetUrl.ToString();
//pbIndicator.Image = Resources.indicator_green;
btnConnect.BackColor = Color.Red;
btnConnect.Text = "Stop";
}
host.Open();
}
catch (Exception ex)
{
MessageBox.Show(ex.StackTrace);
}
}
And this is my ServiceLibrary:
namespace ServiceLibrary
{
public delegate void StatusEventHandler(string srt);
[ServiceContract()]
public interface IService1
{
[OperationContract]
string MyOperation1(string myValue);
[OperationContract]
string MyOperation2(DataContract1 dataContractValue);
[OperationContract]
string HelloWorld(string str);
}
public class service1 : IService1
{
public event StatusEventHandler StartEvent;
public string MyOperation1(string myValue)
{
return "Hello: " + myValue;
}
public string MyOperation2(DataContract1 dataContractValue)
{
return "Hello: " + dataContractValue.FirstName;
}
public string HelloWorld(string str)
{
StartEvent(str);
//return "Helloworld from " + str;
}
}
[DataContract]
public class DataContract1
{
string firstName;
string lastName;
[DataMember]
public string FirstName
{
get { return firstName; }
set { firstName = value; }
}
[DataMember]
public string LastName
{
get { return lastName; }
set { lastName = value; }
}
}
}
What i want to do is to pass the client message from HelloWorld function to my main form, so i try to create event inside class service1:
public delegate void StatusEventHandler(string srt);
public event StatusEventHandler StartEvent;
but it remained null even after registered from my main form (inside my button click event)
What is the simplest way to achieve that ?
When you created a service host, you gave a type, which means that with every call to your service, a new instance of that type will be created.
host = new ServiceHost(typeof(ServiceLibrary.service1));
You need to pass an instance of your service to the servicehost constructor, so every call will use that instance.
var yourServiceInstance = new ServiceLibrary.service1();
// attach event handlers here
host = new ServiceHost(yourServiceInstance);
When you do this, your service class needs to be configured for single instance mode:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class service1
{
// methods here
}

Windows Communication Foundation Null Reference Error

I am trying to use Windows Communication Foundation(WCF) for testing purposes in my application and I am getting a null object. I am new to using WCF, so any help would be appreciated.
From what I understand, each host has one service type(class). I have several interfaces and classes I want to test. My goal is to make one master class that has instances of the others, so I don't have to have several hosts running. Below is some of my code.
Host-
public class AutomationWCFHost
{
public ServiceHost host;
public AutomationWCFHost() {
Uri httpUrl = new Uri("http://localhost:8090/MyFun");
host = new ServiceHost(typeof(MyFun), httpUrl);
ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
smb.HttpGetEnabled = true;
smb.MetadataExporter.PolicyVersion = PolicyVersion.Policy15;
host.Description.Behaviors.Add(smb);
}
public void StartWCF() {
this.host.Open();
}
public void StopWCF(){
this.host.Close();
Interfaces -
[ServiceContract]
public interface IMyFun {
[OperationContract]
string Echo(string msg);
IDataSources DataSources { [OperationContract]get; [OperationContract]set; }
}
[ServiceContract]
public interface IDataSources {
[OperationContract]
void Add(DataSource dataSource);
[OperationContract]
void Remove(DataSource dataSource);
}
Class - I am trying to make one base class so I don't have to have multiple hosts running.
public class MyFun : IMyFun {
public DataSources _dataSources;
public MyFun () {
_dataSources = new DataSources();
}
public string Echo(string msg) {
return msg;
}
public IDataSources DataSources { get { return _dataSources; } set {_dataSources = (DataSources)value;} }
}
public class DataSources : IDataSources{
....//Several methods and properties
}
Test -
public void MyTest() {
EndpointAddress address = new EndpointAddress("http://localhost:8090/MyFun");
BasicHttpBinding binding = new BasicHttpBinding();
ChannelFactory<IMyFun> factory = new ChannelFactory<IMyFun>(binding, address);
IMyFun channel = factory.CreateChannel();
Assert.AreEqual("Test", channel.Echo("Test"));
IDataSources dataSources = channel.DataSources;
dataSources.Add(newDataSource);
}
As I step through, it runs the echo method fine, but when it does
IDataSources dataSources = channel.DataSources;
dataSources is null. This makes it so
dataSources.Add(newDataSource);
fails due to null exception error.

my first WCF server - works with "string" but doesn't work with custom interface?

I've implemented my first WCF application.
I need one method IConsoleData GetData();
I receive CommunicationException "There was an error reading from the pipe: The channel was closed. (109, 0x6d)." in client.
When I replaced IConsoleData GetData(); to string GetData(); application become functionable.
How should I fix the code to use IConsoleData GetData()?
Server:
//public interface IConsoleData
//{
// double GetCurrentIndicator();
//}
//public class IConsoleDataImpl : IConsoleData
//{
// public double GetCurrentIndicator()
// {
// return 22;
// }
//}
[ServiceContract]
public interface IMBClientConsole
{
[OperationContract]
//IConsoleData GetData();
string GetData();
}
public class MBClientConsole : IMBClientConsole
{
//public IConsoleData GetData()
//{
// return new IConsoleDataImpl();
//}
public string GetData()
{
//return new IConsoleDataImpl();
return "hello";
}
}
class Log
{
private ServiceHost _host;
public void initialize()
{
_host = new ServiceHost(typeof (MBClientConsole),
new Uri[]
{
new Uri("net.pipe://localhost")
});
_host.AddServiceEndpoint(typeof(IMBClientConsole),
new NetNamedPipeBinding(),
"PipeReverse");
_host.Open();
System.Threading.Thread.Sleep(1000000);
// TODO: host.Close();
}
}
Client:
//public interface IConsoleData
//{
// double GetCurrentIndicator();
//}
[ServiceContract]
public interface IMBClientConsole
{
[OperationContract]
//IConsoleData GetData();
string GetData();
}
class Program
{
static void Main(string[] args)
{
ChannelFactory<IMBClientConsole> pipeFactory =
new ChannelFactory<IMBClientConsole>(
new NetNamedPipeBinding(),
new EndpointAddress(
"net.pipe://localhost/PipeReverse"));
IMBClientConsole pipeProxy =
pipeFactory.CreateChannel();
while (true)
{
string str = Console.ReadLine();
Console.WriteLine("pipe: " +
//pipeProxy.GetData().GetCurrentIndicator());
pipeProxy.GetData());
}
}
}
if you use an Interface you have to do two things:
the implementations needs to be serializable (use the DataContract - Attribute)
you have to tell the service the known types if you use the interface (here it's IConsoleDataImpl)
Make your live easier and decorate the implementation with DataContract and the members of it with DataMember Attributes and use the implementation instead of the interface.
You can find a lot about this here
(and don't begin the implementations name with "I")
here is a rework without the known-type stuff and I think this will help you for your
first steps - no need to dig in that deep (Interfaces + KnownTypes) yet.
[DataContract]
public class ConsoleData
{
[DataMember]
public double CurrentIndicator
{
get { return 22; }
set { /* whatever */ }
}
}
[ServiceContract]
public interface IMBClientConsole
{
[OperationContract]
ConsoleData GetData();
}
You have a couple problems there. First, your interface should be marked as a [DataContract]
[DataContract]
public interface IConsoleData
{
double GetCurrentIndicator();
}
Now, when WCF sends an IConsoleData over the wire, it is going to serialize the data in the class, send it, and deserialize it on the client. The problem with your implementation is that it contains nothing that can be serialized.
public class IConsoleDataImpl : IConsoleData
{
public double GetCurrentIndicator()
{
return 22;
}
}
If you were to build a client using svcutil.exe from the above, it would create the IConsoleDataImpl class, but the GetCurrentIndicator method wouldn't do anything. Important distinction here: WCF will transmit DATA, not IMPLEMENTATION.
What you probably want here is something more like:
[DataContract]
public interface IConsoleData
{
[DataMember]
double CurrentIndicator { get; set; }
}
public class ConsoleDataImpl : IConsoleData
{
public double CurrentIndicator { get; set; }
}
[ServiceContract]
[KnownType(typeof(ConsoleDataImpl))]
public interface IMBClientConsole
{
[OperationContract]
IConsoleData GetData();
}
public class MBClientConsole : IMBClientConsole
{
public IConsoleData GetData()
{
return new IConsoleDataImpl() { CurrentIndicator = 22 };
}
}
Although at this point the IConsoleData interface isn't really needed, and I would just remove it.
But basically the thing to remember is in general, you want your WCF services to contain methods, and your data contracts to contain properties (or fields). The implementation inside a method in a DataContract will not be in the client if you generate a client from he WSDL, that would only work if you were to copy over a shared dll with the DataContracts to the client.
Hope that makes sense...

Categories

Resources