I am implementing an ipCamera/encoder management system in C#. The system will manage multiple ipCameras and/or encoders from a multiple vendors. Using Onvif instead off each ipcamera or encoders sdk will be a benefit.
One of the key concepts of the management system is to listen on events, such as motion detection events, from the cameras. Onvif supports this by use of the ws-basenotification or a pull type support. I don’t like pull, so I will use ws-basenotification support in Onvif (Onvif spec 9.1).
I have successfully added subscription to a Sony SNC-RH164, Bosh VIP X1 XF IVA and Acti TCD2100.
My problem is: I don’t get any notifications from any of the devices. Can anyone see what I'm doing wrong or give my som pointers on how to get notifications from devices.
My pc is on the same subnet as the devices. And my firewall is turned off for the test.
My test console app initiating the OnvifManager class.
using (var manager = new OnvifManager())
{
//manager.ScanForDevices();
var sonyDevice = new OnvifClassLib.OnvifDevice
{
OnvifDeviceServiceUri = new Uri(#"http://192.168.0.101/onvif/device_service"),
};
manager.AddDevice(sonyDevice);
manager.AddEventSubscription(sonyDevice, "PT1H");
var boshDevice = new OnvifClassLib.OnvifDevice
{
OnvifDeviceServiceUri = new Uri(#"http://192.168.0.102/onvif/device_service"),
};
manager.AddDevice(boshDevice);
manager.AddEventSubscription(boshDevice, string.Empty);
var actiDevice = new OnvifClassLib.OnvifDevice
{
OnvifDeviceServiceUri = new Uri(#"http://192.168.0.103/onvif/device_service"),
UserName = "uid",
Password = "pwd"
};
manager.AddDevice(actiDevice);
manager.AddEventSubscription(actiDevice);
Console.WriteLine("Waiting...");
Console.Read();
}
My managerClass will in the Constructor initialize my NotificationConsumer interface.
private void InitializeNotificationConsumerService()
{
_notificationConsumerService = new NotificationConsumerService();
_notificationConsumerService.NewNotification += NotificationConsumerService_OnNewNotification;
_notificationConsumerServiceHost = new ServiceHost(_notificationConsumerService);
_notificationConsumerServiceHost.Open();
}
My NotificationConsumer interface implementation.
/// <summary>
/// The client reciever service for WS-BaseNotification
/// </summary>
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class NotificationConsumerService : NotificationConsumer
{
public event EventHandler<EventArgs<Notify1>> NewNotification;
/// <summary>
/// Notifies the specified request.
/// </summary>
/// <param name="request">The request.</param>
/// <remarks>A </remarks>
public void Notify(Notify1 request)
{
var threadSafeEventHandler = NewNotification;
if (threadSafeEventHandler != null)
threadSafeEventHandler.Invoke(this, new EventArgs<Notify1>(request));
}
}
public class EventArgs<T> : EventArgs
{
public EventArgs(T data)
{
Data = data;
}
public T Data { get; set; }
}
The config for NotificationConsumerService
<services>
<service name="OnvifClassLib.NotificationConsumerService">
<endpoint address="" binding="customBinding" bindingConfiguration="CustomBasicHttpBinding"
name="CustomHttpBinding" contract="EventService.NotificationConsumer" />
<host>
<baseAddresses>
<add baseAddress="http://192.168.0.10:8080/NotificationConsumerService" />
</baseAddresses>
</host>
</service>
</services>
<bindings>
<customBinding>
<binding name="CustomBasicHttpBinding">
<textMessageEncoding messageVersion="Soap12">
<readerQuotas maxStringContentLength="80000" />
</textMessageEncoding>
<httpTransport maxReceivedMessageSize="800000" maxBufferSize="800000" />
</binding>
</customBinding>
</bindings>
The AddDevice method
public void AddDevice(OnvifDevice device)
{
LoadCapabilities(device);
OnvifDevices.Add(device);
}
internal void LoadCapabilities(OnvifDevice onvifDevice)
{
if (onvifDevice.OnvifDeviceServiceUri == null)
return;
if (onvifDevice.DeviceClient == null)
LoadDeviceClient(onvifDevice);
try
{
onvifDevice.Capabilities = onvifDevice.DeviceClient.GetCapabilities(new[] { OnvifClassLib.DeviceManagement.CapabilityCategory.All });
}
catch (Exception ex)
{
Console.Write(ex.ToString());
}
}
private void LoadDeviceClient(OnvifDevice onvifDevice)
{
if (onvifDevice.OnvifDeviceServiceUri == null)
return;
var serviceAddress = new EndpointAddress(onvifDevice.OnvifDeviceServiceUri.ToString());
var binding = GetBindingFactory(onvifDevice);
onvifDevice.DeviceClient = new OnvifClassLib.DeviceManagement.DeviceClient(binding, serviceAddress);
if (!string.IsNullOrWhiteSpace(onvifDevice.UserName))
{
onvifDevice.DeviceClient.ClientCredentials.UserName.UserName = onvifDevice.UserName;
onvifDevice.DeviceClient.ClientCredentials.UserName.Password = onvifDevice.Password;
}
}
The AddEventSubscription method
public void AddEventSubscription(OnvifDevice onvifDevice, string initialTerminationTime = "PT2H")
{
if (onvifDevice.Capabilities.Events == null)
throw new ApplicationException("The streamer info does not support event");
try
{
if (onvifDevice.NotificationProducerClient == null)
LoadNotificationProducerClient(onvifDevice);
XmlElement[] filterXml = null;
var subScribe = new Subscribe()
{
ConsumerReference = new EndpointReferenceType
{
Address = new AttributedURIType { Value = _notificationConsumerServiceHost.BaseAddresses.First().ToString() },
}
};
if (!string.IsNullOrWhiteSpace(initialTerminationTime))
subScribe.InitialTerminationTime = initialTerminationTime;
onvifDevice.SubscribeResponse = onvifDevice.NotificationProducerClient.Subscribe(subScribe);
Console.WriteLine("Listening on event from {0}", onvifDevice.NotificationProducerClient.Endpoint.Address.Uri.ToString());
}
catch (FaultException ex)
{
Console.Write(ex.ToString());
}
catch (Exception ex)
{
Console.Write(ex.ToString());
}
}
private void LoadNotificationProducerClient(OnvifDevice onvifDevice)
{
var serviceAddress = new EndpointAddress(onvifDevice.Capabilities.Events.XAddr.ToString());
var binding = GetBindingFactory(onvifDevice);
onvifDevice.NotificationProducerClient = new OnvifClassLib.EventService.NotificationProducerClient(binding, serviceAddress);
if (!string.IsNullOrWhiteSpace(onvifDevice.UserName))
{
onvifDevice.NotificationProducerClient.ClientCredentials.UserName.UserName = onvifDevice.UserName;
onvifDevice.NotificationProducerClient.ClientCredentials.UserName.Password = onvifDevice.Password;
}
}
Bindings for Soap12
private Binding GetBindingFactory(OnvifDevice onvifDevice)
{
return GetCustomBinding(onvifDevice);
}
private Binding GetCustomBinding(OnvifDevice onvifDevice)
{
HttpTransportBindingElement transportElement = new HttpTransportBindingElement();
if (!string.IsNullOrWhiteSpace(onvifDevice.UserName))
transportElement.AuthenticationScheme = AuthenticationSchemes.Basic;
var messegeElement = new TextMessageEncodingBindingElement();
messegeElement.MessageVersion = MessageVersion.CreateVersion(EnvelopeVersion.Soap12, AddressingVersion.None);
var binding = new CustomBinding(messegeElement, transportElement);
binding.SendTimeout = new TimeSpan(0, 10, 0);
return binding;
}
I had this problem with a GrandStream camera. I had to add one or more detection zones to it using the camera web UI to get it to detect motion.
I think the problem is, that your notification consumer uses AddressingVersion.None, while the notifications from the ONVIF device are formatted according to WS-Addressing 1.0. Try to change the following line of your GetCustomBinding method:
messegeElement.MessageVersion = MessageVersion.CreateVersion(
EnvelopeVersion.Soap12, AddressingVersion.None);
to
messegeElement.MessageVersion = MessageVersion.CreateVersion(
EnvelopeVersion.Soap12, AddressingVersion.WSAddressing10);
Related
I am trying to create a service to consume it through the internet, but for some reason I cannot access it and I need help finding the error I am making.
I leave the code for you to see.
public class ServiceHost<T> : System.ServiceModel.ServiceHost
{
public ServiceHost(Type serviceType, params Uri[] baseAddresses)
: base(serviceType, baseAddresses)
{
}
public ServiceHost(object singletonInstance, params Uri[] baseAddresses)
:base(singletonInstance, baseAddresses)
{
}
protected ServiceHost()
: base()
{
}
public void EnableMetadataExchange(bool enableHttpGet = true)
{
if (State == CommunicationState.Opened)
{
throw new InvalidOperationException("La comunicación ya está abierta");
}
ServiceMetadataBehavior metadataBehavior
= Description.Behaviors.Find<ServiceMetadataBehavior>();
if (metadataBehavior == null)
{
metadataBehavior = new ServiceMetadataBehavior();
Description.Behaviors.Add(metadataBehavior);
if (BaseAddresses.Any(uri => uri.Scheme == "http"))
metadataBehavior.HttpGetEnabled = enableHttpGet;
}
AddAllMexEndPoints();
}
public bool HasMexEndpoint
{
get
{
return Description.Endpoints.Any(
endpoint => endpoint.Contract.ContractType ==
typeof(IMetadataExchange));
}
}
private void AddAllMexEndPoints()
{
Debug.Assert(HasMexEndpoint == false);
foreach (Uri baseAddress in BaseAddresses)
{
Binding binding = null;
switch (baseAddress.Scheme)
{
case "net.tcp":
{
binding = MetadataExchangeBindings.CreateMexTcpBinding();
break;
}
case "http":
{
binding = MetadataExchangeBindings.CreateMexHttpBinding();
break;
}
case "https":
{
binding = MetadataExchangeBindings.CreateMexHttpsBinding();
break;
}
case "net.pipe":
{
binding = MetadataExchangeBindings.CreateMexNamedPipeBinding();
break;
}
}
if (binding != null)
{
AddServiceEndpoint(typeof(IMetadataExchange), binding, "MEX");
}
}
}
}
Hosting
public void HostService()
{
try
{
Uri tcpBaseAddress = new Uri("net.tcp://192.168.1.110:28620/");
Uri httpBaseAddress = new Uri("http://192.168.1.110:28621/");
ServiceHost<wesling.Services.GC> host = new ServiceHost<wesling.Services.GC>(typeof(wesling.Services.GC), tcpBaseAddress, httpBaseAddress);
//add tcp binding
var netTcpBinding = new NetTcpBinding()
{
MaxBufferPoolSize = int.MaxValue,
MaxReceivedMessageSize = int.MaxValue,
ReaderQuotas = new System.Xml.XmlDictionaryReaderQuotas()
{
MaxStringContentLength = int.MaxValue
},
};
netTcpBinding.Security.Mode = SecurityMode.None;
host.AddServiceEndpoint(typeof(wesling.Services.IGC), netTcpBinding, "GC");
//add WSHttp binding
var httpBinding = new WSHttpBinding()
{
MaxBufferPoolSize = int.MaxValue,
MaxReceivedMessageSize = int.MaxValue,
ReaderQuotas = new System.Xml.XmlDictionaryReaderQuotas()
{
MaxStringContentLength = int.MaxValue
},
};
httpBinding.Security.Mode = SecurityMode.None;
host.AddServiceEndpoint(typeof(wesling.Services.IGC), httpBinding, "GC");
host.EnableMetadataExchange(true);
host.Open();
}
catch (CommunicationException ce)
{
MessageBox.Show(ce.Message);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
The uri ip is the lan ip of the pc where the service is hosted
The customer is like this
public async void GetProduct()
{
try
{
var endPointConfiguration = "WSHttpBinding_IGC";//cfg.GetEndPointConfiguration();
var address = "http://fabianwesling.dynu.com:28621/GC"; //cfg.getAddress();
ServiceReference1.GCClient service = new ServiceReference1.GCClient(endPointConfiguration, address);
var bindins = service.Endpoint.Binding;
if (bindins is NetTcpBinding tcpBinding)
{
tcpBinding.MaxBufferPoolSize = int.MaxValue;
tcpBinding.MaxReceivedMessageSize = int.MaxValue;
tcpBinding.ReaderQuotas.MaxStringContentLength = int.MaxValue;
}
else if (bindins is WSHttpBinding wS)
{
wS.MaxBufferPoolSize = int.MaxValue;
wS.MaxReceivedMessageSize = int.MaxValue;
wS.ReaderQuotas.MaxStringContentLength = int.MaxValue;
}
var result = await service.GetProductsAsync();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
I opened the ports in the Reuter so that it directs those ports to the pc, and I also opened the ports in the firewall, I also activated the Windows .net framework features. But when I try to connect from the client, it tells me that there was no end listening
There must be some concept that I am not understanding, but I cannot identify what it is ... I need your advice, everything is welcome
First try to use the browser on the client computer to visit http//fabianwesling.dynu.com:28621/GC to see if the metadata can be accessed normally.If not, the client and server cannot access normally. Modify the hots file on the client computer:
All in all, you must ensure that the client and server are mutually accessible on the Internet.
If it is not a network problem, you can create a simple WCF service for testing to know where the problem is.Here is a very simple example of WCF service:
https://learn.microsoft.com/en-us/dotnet/framework/wcf/getting-started-tutorial
UPDATE
This is my remote WCF service:
Baseadress is the WAN IP.
Ensure that the local computer can access the WSDL of the remote service through a browser
I'm developing android app on Xamarin Android in c#.
Is there any way to change dynamically URL of SOAP web service? I want to store url in some kind of config file but I have no idea how do it.
correct me, if i'm wrong
but the second constructor of an xamarin - soap webservice class has a property for URL.
Here's an example of my webservice:
public partial class Service : System.Web.Services.Protocols.SoapHttpClientProtocol
{
public Service()
{
this.Url = "http://xxx/service.asmx";
}
public Service(string url)
{
this.Url = url;
}
}
you've added the webreference in xamarin and then use your webservice - instance.
Just call the second constructor and give them an other url as source.
You could manually create your required connection using Channelfactory that changes as it needs to. You'll need the proper connections in the web.config file.
Here's a way you could set it up.
In your web.config.
<appSettings>
<add key="Identity" value="machineidentity" />
<add key="Binding" value="WSHttpBinding_IService" />
<add key="Endpoint" value="http://Devservice/Service.svc" />
<add key="Identity2" value="localmachine" />
<add key="Binding2" value="WSHttpBinding_IService" />
<add key="Endpoint2" value="http://Devservice/Service.svc" />
<add key="devIdentity" value="localmachine" />
<add key="devBinding" value="WSHttpBinding_IService" />
<add key="devEndpoint" value="http://Devservice/Service.svc" />
</appSettings>
C# code
a configuration class to hold values from the web.config
public static Dictionary<int, Connections> EndpointConnections = new Dictionary<int, Connections>
{
{1, new Connections(){Identity = ConfigurationManager.AppSettings["Identity"],Binding = ConfigurationManager.AppSettings["Binding"], Endpoint = ConfigurationManager.AppSettings["Endpoint"]}},
{2, new Connections(){Identity = ConfigurationManager.AppSettings["Identity2"],Binding = ConfigurationManager.AppSettings["Binding2"], Endpoint = ConfigurationManager.AppSettings["Endpoint2"]}},
{3, new Connections(){Identity = ConfigurationManager.AppSettings["devIdentity"],Binding = ConfigurationManager.AppSettings["devBinding"], Endpoint = ConfigurationManager.AppSettings["devEndpoint"]}},
};
Now a static class for creating the endpoint
private ChannelFactory<IService> SetChannelFactory(int configInput)
{
var identity = EndpointIdentity.CreateDnsIdentity(Configuration.EndpointConnections[configInput].Identity);
var myBinding = new WSHttpBinding(Configuration.EndpointConnections[configInput].Binding);
var myuri = new Uri(Configuration.EndpointConnections[configInput].Endpoint);
var myEndpoint = new EndpointAddress(myuri, identity);
return new ChannelFactory<IService>(myBinding, myEndpoint);
}
Now calling and using the endpoint
public async Task<Result> SomeAction(int selection)
{
IService client = null;
Result result = null;
Response response = null;
using (var myChannelFactory = SetChannelFactory(selection))
{
try
{
client = myChannelFactory.CreateChannel();
response = await client.TheServiceFunction().ConfigureAwait(false);
((ICommunicationObject)client).Close();
}
catch
{
if (client != null)
{
((ICommunicationObject)client).Abort();
return new result ( failure = true);
}
}
}
if (response != null)
{
//Whatever you want to do with the response here
return new result ( failure = false);
}
}
I have a client for SOAP service in a console application, I need to move the service client to sharepoint 2010. I do not want to do config files deployment and other sharepoint-related stuff, so I decided to hardcode binding information, the only configurable option is URL. But I encountred some troubles doing this. I have a config:
<system.serviceModel>
<bindings>
<customBinding>
<binding name="SI_PMProjectMaintain_SOUTBinding">
<textMessageEncoding maxReadPoolSize="64" maxWritePoolSize="16"
messageVersion="Soap11" writeEncoding="utf-8">
<readerQuotas maxDepth="10000000" maxStringContentLength="10000000"
maxArrayLength="67108864" maxBytesPerRead="65536" maxNameTableCharCount="100000" />
</textMessageEncoding>
<httpTransport authenticationScheme="Basic" bypassProxyOnLocal="false"
hostNameComparisonMode="StrongWildcard" keepAliveEnabled="false"
proxyAuthenticationScheme="Basic" realm="XISOAPApps" useDefaultWebProxy="true" />
</binding>
</customBinding>
</bindings>
<client>
<endpoint address="http://url/XISOAPAdapter/MessageServlet?senderParty=&senderService=Param1&receiverParty=&receiverService=&interface=interface&interfaceNamespace=url"
binding="customBinding" bindingConfiguration="SI_PMProjectMaintain_SOUTBinding"
contract="PmProjectMaintain.SI_PMProjectMaintain_SOUT" name="HTTP_Port" />
</client>
Also, I have a code:
var service = new ServiceClient();
service.ClientCredentials.Windows.ClientCredential = new NetworkCredential("user", "password");
service.ClientCredentials.UserName.UserName = "user";
service.ClientCredentials.UserName.Password = "password";
var resp = service.Operation();
Console.WriteLine(resp.Response);
It works as expected. Now I want to get rid of xml config and completly move it to code:
public class CustomHttpTransportBinding : CustomBinding
{
public CustomHttpTransportBinding()
{
}
public override BindingElementCollection CreateBindingElements()
{
var result = new BindingElementCollection();
result.Add(new TextMessageEncodingBindingElement
{
MaxReadPoolSize = 64,
MaxWritePoolSize = 16,
MessageVersion = MessageVersion.Soap11,
WriteEncoding = Encoding.UTF8,
//ReaderQuotas = new System.Xml.XmlDictionaryReaderQuotas
//{
// MaxDepth = 10000000,
// MaxStringContentLength = 10000000,
// MaxArrayLength = 67108864,
// MaxBytesPerRead = 65536,
// MaxNameTableCharCount = 100000
//}
});
result.Add(new HttpTransportBindingElement
{
AuthenticationScheme = AuthenticationSchemes.Basic,
BypassProxyOnLocal = false,
HostNameComparisonMode = System.ServiceModel.HostNameComparisonMode.StrongWildcard,
KeepAliveEnabled = false,
ProxyAuthenticationScheme = AuthenticationSchemes.Basic,
Realm = "XISOAPApps",
UseDefaultWebProxy = true
});
return result;
}
}
And I use it like this:
var service = new ServiceClient(new CustomHttpTransportBinding(), new EndpointAddress(url));
service.ClientCredentials.Windows.ClientCredential = new NetworkCredential("user", "password");
service.ClientCredentials.UserName.UserName = "user";
service.ClientCredentials.UserName.Password = "password";
var resp = service.Operation();
Console.WriteLine(resp.Response);
It reaches the server, but throws exception ("Server Error"), so I belive something wrong with my configuration. I can not specify the reader quotas also. I've tried loading configuration from string, but it is not worked for me, same error.
Confi
It seems that I can not convert custom bindings from XML to code in .net 3.5. Can somebody take a look on my code and confirm? Are there other ways to have a service client with custom binding in code?
I have not found a way to move configuration from app.config to code for a custom binding, it works greate for basicHttpBinding, but does not work for customBinding.
I've ended up with loading the configuration dynamicaly from file using custom chanell factory.
public class CustomChannelFactory<T> : ChannelFactory<T>
{
private readonly string _configurationPath;
public CustomChannelFactory(string configurationPath) : base(typeof(T))
{
_configurationPath = configurationPath;
base.InitializeEndpoint((string)null, null);
}
protected override ServiceEndpoint CreateDescription()
{
ServiceEndpoint serviceEndpoint = base.CreateDescription();
ExeConfigurationFileMap executionFileMap = new ExeConfigurationFileMap();
executionFileMap.ExeConfigFilename = _configurationPath;
System.Configuration.Configuration config = ConfigurationManager.OpenMappedExeConfiguration(executionFileMap, ConfigurationUserLevel.None);
ServiceModelSectionGroup serviceModeGroup = ServiceModelSectionGroup.GetSectionGroup(config);
ChannelEndpointElement selectedEndpoint = null;
foreach (ChannelEndpointElement endpoint in serviceModeGroup.Client.Endpoints)
{
if (endpoint.Contract == serviceEndpoint.Contract.ConfigurationName)
{
selectedEndpoint = endpoint;
break;
}
}
if (selectedEndpoint != null)
{
if (serviceEndpoint.Binding == null)
{
serviceEndpoint.Binding = CreateBinding(selectedEndpoint.Binding, serviceModeGroup);
}
if (serviceEndpoint.Address == null)
{
serviceEndpoint.Address = new EndpointAddress(selectedEndpoint.Address, GetIdentity(selectedEndpoint.Identity), selectedEndpoint.Headers.Headers);
}
if (serviceEndpoint.Behaviors.Count == 0 && !String.IsNullOrEmpty(selectedEndpoint.BehaviorConfiguration))
{
AddBehaviors(selectedEndpoint.BehaviorConfiguration, serviceEndpoint, serviceModeGroup);
}
serviceEndpoint.Name = selectedEndpoint.Contract;
}
return serviceEndpoint;
}
private Binding CreateBinding(string bindingName, ServiceModelSectionGroup group)
{
BindingCollectionElement bindingElementCollection = group.Bindings[bindingName];
if (bindingElementCollection.ConfiguredBindings.Count > 0)
{
IBindingConfigurationElement be = bindingElementCollection.ConfiguredBindings[0];
Binding binding = GetBinding(be);
if (be != null)
{
be.ApplyConfiguration(binding);
}
return binding;
}
return null;
}
private void AddBehaviors(string behaviorConfiguration, ServiceEndpoint serviceEndpoint, ServiceModelSectionGroup group)
{
EndpointBehaviorElement behaviorElement = group.Behaviors.EndpointBehaviors[behaviorConfiguration];
for (int i = 0; i < behaviorElement.Count; i++)
{
BehaviorExtensionElement behaviorExtension = behaviorElement[i];
object extension = behaviorExtension.GetType().InvokeMember("CreateBehavior",
BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance,
null, behaviorExtension, null);
if (extension != null)
{
serviceEndpoint.Behaviors.Add((IEndpointBehavior)extension);
}
}
}
private EndpointIdentity GetIdentity(IdentityElement element)
{
EndpointIdentity identity = null;
PropertyInformationCollection properties = element.ElementInformation.Properties;
if (properties["userPrincipalName"].ValueOrigin != PropertyValueOrigin.Default)
{
return EndpointIdentity.CreateUpnIdentity(element.UserPrincipalName.Value);
}
if (properties["servicePrincipalName"].ValueOrigin != PropertyValueOrigin.Default)
{
return EndpointIdentity.CreateSpnIdentity(element.ServicePrincipalName.Value);
}
if (properties["dns"].ValueOrigin != PropertyValueOrigin.Default)
{
return EndpointIdentity.CreateDnsIdentity(element.Dns.Value);
}
if (properties["rsa"].ValueOrigin != PropertyValueOrigin.Default)
{
return EndpointIdentity.CreateRsaIdentity(element.Rsa.Value);
}
if (properties["certificate"].ValueOrigin != PropertyValueOrigin.Default)
{
X509Certificate2Collection supportingCertificates = new X509Certificate2Collection();
supportingCertificates.Import(Convert.FromBase64String(element.Certificate.EncodedValue));
if (supportingCertificates.Count == 0)
{
throw new InvalidOperationException("UnableToLoadCertificateIdentity");
}
X509Certificate2 primaryCertificate = supportingCertificates[0];
supportingCertificates.RemoveAt(0);
return EndpointIdentity.CreateX509CertificateIdentity(primaryCertificate, supportingCertificates);
}
return identity;
}
private Binding GetBinding(IBindingConfigurationElement configurationElement)
{
if (configurationElement is CustomBindingElement)
return new CustomBinding();
else if (configurationElement is BasicHttpBindingElement)
return new BasicHttpBinding();
else if (configurationElement is NetMsmqBindingElement)
return new NetMsmqBinding();
else if (configurationElement is NetNamedPipeBindingElement)
return new NetNamedPipeBinding();
else if (configurationElement is NetPeerTcpBindingElement)
return new NetPeerTcpBinding();
else if (configurationElement is NetTcpBindingElement)
return new NetTcpBinding();
else if (configurationElement is WSDualHttpBindingElement)
return new WSDualHttpBinding();
else if (configurationElement is WSHttpBindingElement)
return new WSHttpBinding();
else if (configurationElement is WSFederationHttpBindingElement)
return new WSFederationHttpBinding();
return null;
}
}
Usefull links:
Loading WCF configuration from different files (source code is not availiable)
Thread with source code of CustomChannelFactory
.NET 4.0 ChanellFactory (wish it was in .net 3.5)
Found an issue with the code referenced in boades reply, in that if you have mixed transport bindings (https/http) under basicHttpbinding you are likely to get an exception similar/reverse of this:
The provided URI scheme 'https' is invalid; expected 'http'. Parameter name: via
I'd also expect you will also have unexpected authorization occurring, again as the code will use the first bindingConfiguration listed in the web.config rather than by name.
The offending line which doesn't take the binding by name, but rather just takes the 1st one(!)
IBindingConfigurationElement be = bindingElementCollection.ConfiguredBindings[0];
This could be corrected by updating CreateBinding method and the call in CreateDescription like so:
protected override ServiceEndpoint CreateDescription()
{
ServiceEndpoint description = base.CreateDescription();
if (CustomisedChannelFactory<TChannel>.ConfigurationPath == null || !System.IO.File.Exists(CustomisedChannelFactory<TChannel>.ConfigurationPath))
return base.CreateDescription();
ServiceModelSectionGroup sectionGroup = ServiceModelSectionGroup.GetSectionGroup(ConfigurationManager.OpenMappedExeConfiguration(new ExeConfigurationFileMap()
{
ExeConfigFilename = CustomisedChannelFactory<TChannel>.ConfigurationPath
}, ConfigurationUserLevel.None));
ChannelEndpointElement channelEndpointElement1 = (ChannelEndpointElement)null;
foreach (ChannelEndpointElement channelEndpointElement2 in (ConfigurationElementCollection)sectionGroup.Client.Endpoints)
{
if (channelEndpointElement2.Contract == description.Contract.ConfigurationName)
{
channelEndpointElement1 = channelEndpointElement2;
break;
}
}
if (channelEndpointElement1 != null)
{
if (description.Binding == null)
description.Binding = this.CreateBinding(channelEndpointElement1.Binding, channelEndpointElement1.BindingConfiguration, sectionGroup);
if (description.Address == (EndpointAddress)null)
description.Address = new EndpointAddress(channelEndpointElement1.Address, this.GetIdentity(channelEndpointElement1.Identity), channelEndpointElement1.Headers.Headers);
if (description.Behaviors.Count == 0 && !string.IsNullOrEmpty(channelEndpointElement1.BehaviorConfiguration))
this.AddBehaviors(channelEndpointElement1.BehaviorConfiguration, description, sectionGroup);
description.Name = channelEndpointElement1.Contract;
}
return description;
}
private Binding CreateBinding(string bindingName, string bindingConfigurationName, ServiceModelSectionGroup group)
{
BindingCollectionElement collectionElement = group.Bindings[bindingName];
if (collectionElement.ConfiguredBindings.Count <= 0)
return (Binding)null;
IBindingConfigurationElement configurationElement = null;
foreach (IBindingConfigurationElement bce in collectionElement.ConfiguredBindings)
{
if (bce.Name.Equals(bindingConfigurationName))
{
configurationElement = bce;
break;
}
}
if (configurationElement == null) throw new Exception("BindingConfiguration " + bindingConfigurationName + " not found under binding " + bindingName);
Binding binding = this.GetBinding(configurationElement);
if (configurationElement != null)
configurationElement.ApplyConfiguration(binding);
return binding;
}
I want to connect to a webservice url which is provided at runtime when user logs in. so i need to set Endpoint.Address in app.config at runtime.
EndpointIdentity spn = EndpointIdentity.CreateSpnIdentity("host/mikev-ws");
Uri uri = new Uri(txtURL.text.trim());
var address = new EndpointAddress(uri, spn);
var client = new EchoServiceClient("WSHttpBinding_IEchoService", address);
client.Close();
I placed this code on button click ,and gets the value of uri from textbox. Code is executing correctly and then getting an error message
"Address property on channelfactory.endpoint was null"
my app.config is:
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="LoginServiceSoap" />
</basicHttpBinding>
</bindings>
<client>
<!--<endpoint address="http://localhost:3073/LoginService.asmx" binding="basicHttpBinding"-->
<endpoint address="" binding="basicHttpBinding"
bindingConfiguration="LoginServiceSoap" contract="LoginService.LoginServiceSoap"
name="LoginServiceSoap" />
</client>
</system.serviceModel>
</configuration>
I've done something similar in a recent project, where I only wanted to set the endpoint and authentication programatically and not use the config file at all:
public static class ServiceClientFactory
{
public static HttpBindingBase BuildNavisionBinding(string endpointUrl)
{
//http://blog.randomdust.com/index.php/2010/10/could-not-establish-trust-relationship-for-the-ssl-tls-secure-channel/
//http://www.codeproject.com/Forums/1649/Csharp.aspx?fid=1649&df=90&mpp=25&sort=Position&select=3126652&tid=3121885
ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(delegate
{
return true;
});
if (endpointUrl.ToLower().StartsWith("https"))
{
var binding = new BasicHttpsBinding(BasicHttpsSecurityMode.Transport);
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
binding.MaxReceivedMessageSize = int.MaxValue - 1;
return binding;
}
else
{
var binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly);
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
binding.MaxReceivedMessageSize = int.MaxValue - 1;
return binding;
}
}
public static TClient CreateClient<TClient, TChannel>(string endpoint, string username, string password)
where TClient : ClientBase<TChannel>
where TChannel : class
{
var client = (TClient)
Activator.CreateInstance(
typeof (TClient),
BuildNavisionBinding(endpoint),
new EndpointAddress(endpoint));
if (null == client.ClientCredentials)
throw new Exception(
string.Format("Error initializing [{0}] client. Client Credentials object was null",
typeof(TClient).Name));
client.ClientCredentials.Windows.ClientCredential =
new NetworkCredential(
username,
password);
client.ClientCredentials.Windows.AllowedImpersonationLevel =
TokenImpersonationLevel.Delegation;
client.Endpoint.Binding.SendTimeout = new TimeSpan(0, 0, 4, 0);
client.Endpoint.Binding.ReceiveTimeout = new TimeSpan(0, 4, 0);
client.Endpoint.Binding.OpenTimeout = new TimeSpan(0, 0, 4, 0);
return client;
}
So from your example:
var client = ServiceClientFactory.CreateClient<EchoServiceClient, IEchoServicePort(txtUrl.text.trim(), /* authentication */);
I'm current looking at Thrift to use as a RPC framework for our apps (mostly written in C# and Silverlight). I've come as far as implementing a service and consuming it from a C# console app (using a socket as transport).
For the C# server side code my code looked like: (basically copying the tutorials included with the source code)
MyServiceHandler handler = new MyServiceHandler();
MyService.Processor processor = new MyService.Processor(handler);
TServerTransport serverTransport = new TServerSocket(9090);
TServer server = new TSimpleServer(processor, serverTransport);
server.Serve();
For the client side code it looked like:
TTransport transport = new TSocket("localhost", 9090);
TProtocol protocol = new TBinaryProtocol(transport);
MyService.Client client = new MyService.Client(protocol);
transport.Open();
client.SomeServiceCall();
However, we will be consuming the service from a Silverlight client, and unfortunately there is no support for sockets in Silverlight for Thrift. I assume I'm forced to use HTTP communication between the client and service, using Thrift's C# THttpClient and THttpHandler classes? I could not find any examples of how to do this out there, can anyone point me in the right direction? Some example server and client side code would be appreciated.
It seems that this issue was already addressed by this guy. According to this JIRA, the fix is available in Thrift 0.9. You can either try this snapshot (note that, as it's not a final release, it might not be stable) or you can apply this patch to the 0.8 release.
I believe by now you would have understood, there is no direct way of communicating from Silverlight to the Cassandra database either using Thrift or any other clients.
I have one simple option related to this. Write a Silverlight enabled web service and consume it from the client.
For example, on the server side you can have a web service which does insert/update/read etc., like this. I just managed to pull out some code which we use for our project. Hope this helps.
using Apache.Cassandra;
using Thrift.Protocol;
using Thrift.Transport;
namespace CassandraWebLibrary
{
public class MyDb
{
String _host;
int _port;
String _keyspace;
bool _isConnected;
TTransport _transport = null;
Apache.Cassandra.Cassandra.Client _client = null;
String columnFamily = "ColumnFamilyName";
public VazhikaattiDB(String host, int port, String keyspace)
{
_host = host;
_port = port;
_keyspace = keyspace;
_isConnected = false;
}
public bool Connect()
{
try
{
_transport = new TFramedTransport(new TSocket(_host, _port));
TProtocol protocol = new TBinaryProtocol(_transport);
_client = new Apache.Cassandra.Cassandra.Client(protocol);
_transport.Open();
_client.set_keyspace(_keyspace);
_isConnected = true;
}
catch (Exception ex)
{
log.Error(ex.ToString());
}
return _isConnected;
}
public bool Close()
{
if (_transport.IsOpen)
_transport.Close();
_isConnected = false;
return true;
}
public bool InsertData(Send your data as parameters here)
{
try
{
List<Column> list = new List<Column>();
string strKey = keyvalue;
#region Inserting into Coulmn family
List<Byte> valbytes = new List<byte>(BitConverter.GetBytes(value)); //You might have to pad this with more bytes to make it length of 8 bytes
Column doublecolumn1 = new Column()
{
Name = Encoding.UTF8.GetBytes("column1"),
Timestamp = timestampvalue,
Value = valbytes.ToArray()
};
list.Add(doublecolumn1);
Column stringcolumn2 = new Column()
{
Name = Encoding.UTF8.GetBytes("column2"),
Timestamp = timestampvalue,
Value = Encoding.UTF8.GetBytes("StringValue")
};
list.Add(stringcolumn2);
Column timecolumn3 = new Column()
{
Name = Encoding.UTF8.GetBytes("column3"),
Timestamp = timestampvalue,
Value = BitConverter.GetBytes(DateTime.Now.Ticks)
};
list.Add(timecolumn3);
#endregion
ColumnParent columnParent = new ColumnParent();
columnParent.Column_family = columnFamily;
Byte[] key = Encoding.UTF8.GetBytes(strKey);
foreach (Column column in list)
{
try
{
_client.insert(key, columnParent, column, ConsistencyLevel.QUORUM);
}
catch (Exception e)
{
log.Error(e.ToString());
}
}
return true;
}
catch (Exception ex)
{
log.Error(ex.ToString());
return false;
}
}
public List<YourReturnObject> GetData(parameters)
{
try
{
ColumnParent columnParent = new ColumnParent();
columnParent.Column_family = columnFamily;
DateTime curdate = startdate;
IndexExpression indExprsecondkey = new IndexExpression();
indExprsecondkey.Column_name = Encoding.UTF8.GetBytes("column");
indExprsecondkey.Op = IndexOperator.EQ;
List<Byte> valbytes = PadLeftBytes((int)yourid, 8);
indExprsecondkey.Value = valbytes.ToArray();
indExprList.Add(indExprsecondkey);
IndexClause indClause = new IndexClause()
{
Expressions = indExprList,
Count = 1000,
Start_key = Encoding.UTF8.GetBytes("")
};
SlicePredicate slice = new SlicePredicate()
{
Slice_range = new SliceRange()
{
//Start and Finish cannot be null
Start = new byte[0],
Finish = new byte[0],
Count = 1000,
Reversed = false
}
};
List<KeySlice> keyslices = _client.get_indexed_slices(columnParent, indClause, slice, ConsistencyLevel.ONE);
foreach (KeySlice ks in keyslices)
{
String stringcolumnvalue = Encoding.UTF8.GetString(cl.Column.Value);
double doublevalue= (Double)BitConverter.ToDouble(cl.Column.Value);
long timeticks = BitConverter.ToInt64(cl.Column.Value, 0);
DateTime dtcolumntime = new DateTime(timeticks);
}
}
catch (Exception ex)
{
log.Error(ex.ToString());
}
return yourdatalist;
}
}
}
Now the above class can be used by your webservice, which in turn will be used by Silverlight. Btw, you'll have to take care of other silverlight issues like size of data to be downloaded from server/webservice etc.,
FYI, our client service of Cassandra runs on port 9160..