Question

Below is all the code that I am currently using for a self-hosted WCF service in a Windows Service using NetTcpBinding and MEF. So far this works well for pulling data. However when I try to add a service for pushing data, this code doesn't work. The problem is caused by the assembly that contains the WCF service. For the push mechanism I need the ServiceContract and OperationContract attributes on an interface instead of the class itself.

Unfortnately the service does not accept the interface and requires the attributes to be set on the class. Something else that is not working is the inheritance of the default interface on the interface of the specific interface.

I am currently a bit puzzled about how to solve this. My programming skills are not that advanced. Can someone help me out or point me to an article that can give me some guidance?

While doing some searching about this, I came across some articles about creating custom ServiceHost and ServiceHostFactory. How does this work? Most articles seem to be more complex, then what I need.

I hope that I have provided all information required. If not, I will be glad to try to answer the requests for more information.

BootStrapper used to load the WCF Services:

    internal class Bootstrapper
    {
        [ImportMany(typeof(IWcfSvc))]
        internal IEnumerable<IWcfSvc> Services { get; set; }
    }

This is the default interface that every WCF service should have:

    [InheritedExport]
    public interface IWcfSvc
    {
        string Name { get; }
        void Init();
    }

The MEF class to load the WCF Services:

        private void Load()
        {
            Bootstrapper bootStrapper = new Bootstrapper();
            AggregateCatalog catalog = new AggregateCatalog();

            catalog.Catalogs.Add(new DirectoryCatalog(this.PluginPath));
            CompositionContainer container = new CompositionContainer(catalog, CompositionOptions.DisableSilentRejection);

            try
            {
                container.ComposeParts(bootStrapper);
            }
            catch (CompositionException ex)
            {
                this.log.Error("CompositionException", ex);
            }

            List<string> svcNames = new List<string>();
            foreach (var service in bootStrapper.Services)
            {
                if (svcNames.Count(c => c == service.Name) == 0)
                {
                    service.Init();
                    Type serviceType = service.GetType();
                    Uri address = new Uri(string.Format(URI, this.Host, this.Port, service.Name));
                    this.wcfLoader.CreateWcfService(serviceType, address);
                    svcNames.Add(service.Name);
                }
                else
                {
                    this.log.Warn(string.Format("Tried to load {0} multiple times, which was refused.", service.Name));
                }
            }
        }
    }

Class to start the WCF Services:

        private NetTcpBinding netTcpBinding
        {
            get
            {
                return new NetTcpBinding()
                {
                    MaxBufferSize = int.MaxValue,
                    MaxReceivedMessageSize = int.MaxValue,
                    MaxBufferPoolSize = long.MaxValue,
                    CloseTimeout = new TimeSpan(0, 2, 0),
                    OpenTimeout = new TimeSpan(0, 2, 0),
                    ReceiveTimeout = new TimeSpan(0, 2, 0),
                    SendTimeout = new TimeSpan(0, 2, 0),
                    Security = new NetTcpSecurity()
                    {
                        Mode = SecurityMode.TransportWithMessageCredential,
                        Transport = new TcpTransportSecurity() { ClientCredentialType = TcpClientCredentialType.Certificate },
                        Message = new MessageSecurityOverTcp() { ClientCredentialType = MessageCredentialType.UserName }
                    }
                };
            }
        }

        internal void CreateWcfService(Type serviceType, Uri address)
        {
            Action<string> StoreAddress = delegate(string addr)
            {
                string addrLine = string.Format(ADDRLINE, addr.EndsWith(URLSEPARATOR + MEX, StringComparison.CurrentCultureIgnoreCase) ? MEXBINDING : BINDING, addr);
                this.log.Info(addrLine);
            };

            try
            {
                CustomBinding binding = new CustomBinding(this.netTcpBinding);
                BindingElementCollection bec = binding.Elements;
                foreach (var be in bec)
                    if (be is TcpTransportBindingElement)
                        (be as TcpTransportBindingElement).ConnectionPoolSettings.LeaseTimeout = TimeSpan.FromSeconds(this.LeaseTimeOut);

                ServiceHost serviceHost = new ServiceHost(serviceType, address);
                serviceHost.Credentials.ServiceCertificate.SetCertificate(StoreLocation.LocalMachine, StoreName.My, X509FindType.FindByIssuerName, this.CertIssuerName);
                ServiceEndpoint endpoint = serviceHost.AddServiceEndpoint(serviceType, binding, string.Empty);
                endpoint.Address = new EndpointAddress(address, EndpointIdentity.CreateDnsIdentity(this.Host));

                ServiceMetadataBehavior serviceMetadataBehavior = new ServiceMetadataBehavior();
                serviceHost.Description.Behaviors.Add(serviceMetadataBehavior);
                serviceHost.AddServiceEndpoint(typeof(IMetadataExchange), MetadataExchangeBindings.CreateMexTcpBinding(), MEX);

                ServiceDebugBehavior svcDebugBehavior = serviceHost.Description.Behaviors.Find<ServiceDebugBehavior>();
                if (svcDebugBehavior == null)
                    serviceHost.Description.Behaviors.Add(new ServiceDebugBehavior());
                svcDebugBehavior.IncludeExceptionDetailInFaults = this.Debug;

                serviceHost.Opening += (sender, e) => this.serviceHost_State(sender, e, serviceHost, ActionState.Opening);
                serviceHost.Opened += (sender, e) => this.serviceHost_State(sender, e, serviceHost, ActionState.Open);
                serviceHost.Closing += (sender, e) => this.serviceHost_State(sender, e, serviceHost, ActionState.Closing);
                serviceHost.Closed += (sender, e) => this.serviceHost_State(sender, e, serviceHost, ActionState.Closed);

                serviceHost.Open();

                serviceHost.Description.Endpoints.ToList().ForEach(f => StoreAddress(f.Address.Uri.AbsoluteUri));
                this.serviceHosts.Add(serviceHost);
            }
            catch (Exception ex)
            {
                this.log.Error("Could not load WCF service...", ex);
            }
        }

Example of a WCF Service that uses a pulling mechanism:

        [OperationContract]
        public List<BillOfMaterials> GetBillOfMaterials(int startProductID, DateTime checkDate)
        {
            using (DbAccess db = new DbAccess())
            {
                List<SqlParameter> parms = new List<SqlParameter>();
                parms.Add(new SqlParameter()
                {
                    DbType = DbType.Int32,
                    Direction = ParameterDirection.Input,
                    ParameterName = "StartProductID",
                    Value = startProductID
                });
                parms.Add(new SqlParameter()
                {
                    DbType = DbType.DateTime,
                    Direction = ParameterDirection.Input,
                    ParameterName = "CheckDate",
                    Value = checkDate
                });

                return db.GetData<BillOfMaterials>("uspGetBillOfMaterials", parms);
            }
        }
Was it helpful?

Solution

The problem was that I was referring to the Type of the class within the ServiceEndpoint. This should be the interface of the ServiceContract.

What I originally had:

    ServiceEndpoint endpoint = serviceHost.AddServiceEndpoint(serviceType, binding, string.Empty);

Should be:

    ServiceEndpoint endpoint = serviceHost.AddServiceEndpoint(serviceInterfaceType, binding, string.Empty);

Where serviceInterfaceType is the Type of the interface containing the ServiceContract.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top