I'm writing an application which needs to host several WCF services. One of the strengths of WCF is the ability to configure services without having to recompile, by specifying settings in the app.config file.
When self-hosting, there does not appear to be an out-of-the-box way to automatically host services which are in the app.config file. I found this question which mentions a possible solution of dynamically enumerating services listed in app.config at runtime, and creating a ServiceHost for each.
However, my services, contracts, and the hosting application are all in different assemblies. This causes Type.GetType(string name) to fails to locate my service type (returns null) because it is defined in a different assembly.
How can I reliably host all services listed in the app.config file dynamically (i.e., without hard-coding new ServiceHost(typeof(MyService)) in my self-hosting application?
Note: My app.config was generated using the "WCF Configuration Editor" in Visual Studio 2010.
Note also: My primary goal is to have this driven by the app.config file so there is a single point of configuration. I don't want to have to configure this in a separate location.
EDIT: I am able to read the app.config file (see here), but need to be able to resolve types in different assemblies.
EDIT: One of the answers below prompted me to try specifying the AssemblyQualifiedName in app.config instead of just the basic type name. This was able to get around the Type.GetType() problem, however ServiceHost.Open() now fails with an InvalidOperationException regardless of how I attain the type:
// Fails
string typeName = typeof(MyService).AssemblyQualifiedName;
Type myType = Type.GetType(typeName);
ServiceHost host = new ServiceHost(myType);
host.Open(); // throws InvalidOperationException
// Also fails
Type myType2 = typeof(MyService);
ServiceHost host2 = new ServiceHost(myType2);
host2.Open(); // throws InvalidOperationException
Exception details:
Service 'SO.Example.MyService' has zero application (non-infrastructure) endpoints. This might be because no configuration file was found for your application, or because no service element matching the service name could be found in the configuration file, or because no endpoints were defined in the service element.
I guess WCF attempts to match the literal string for the service name when parsing the app.config file internally.
EDIT/ANSWER: What I ended up doing was basically what was in the answer below. Instead of using Type.GetType() I know that all of my services are in the same assembly, so I switched to:
// Get a reference to the assembly which contain all of the service implementations.
Assembly implementationAssembly = Assembly.GetAssembly(typeof(MyService));
...
// When loading the type for the service, load it from the implementing assembly.
Type implementation = implementationAssembly.GetType(serviceElement.Name);
// get the <system.serviceModel> / <services> config section
ServicesSection services = ConfigurationManager.GetSection("system.serviceModel/services") as ServicesSection;
// get all classs
var allTypes = AppDomain.CurrentDomain.GetAssemblies().ToList().SelectMany(s => s.GetTypes()).Where(t => t.IsClass == true);
// enumerate over each <service> node
foreach (ServiceElement service in services.Services)
{
Type serviceType = allTypes.SingleOrDefault(t => t.FullName == service.Name);
if (serviceType == null)
{
continue;
}
ServiceHost serviceHost = new ServiceHost(serviceType);
serviceHost.Open();
}
Based on the other answers, I extended the code to the following, which searches all assemblies for the services in the app.config
That should definitely be possible ! Check out this code fragment - use it as a foundation and go from here:
using System.Configuration; // don't forget to add a reference to this assembly!
// get the <system.serviceModel> / <services> config section
ServicesSection services = ConfigurationManager.GetSection("system.serviceModel/services") as ServicesSection;
// enumerate over each <service> node
foreach(ServiceElement aService in services.Services)
{
Console.WriteLine();
Console.WriteLine("Name: {0} / Behavior: {1}", aService.Name, aService.BehaviorConfiguration);
// enumerate over all endpoints for that service
foreach (ServiceEndpointElement see in aService.Endpoints)
{
Console.WriteLine("\tEndpoint: Address = {0} / Binding = {1} / Contract = {2}", see.Address, see.Binding, see.Contract);
}
}
This right now just prints out the info - but you could definitely use this to actually build up your service hosts inside your own NT service !
Update: ok, sorry, I missed your most important point - the fact the actual services are in different assemblies.
In that case, you need to dynamically load those assemblies, as needed - you could e.g. "just simply know" what assemblies to load, or you could put them all into a specific subdirectories and load all assemblies in that directory, or you could just inspect all assemblies in the same location where your MyOwnServiceHost.exe resides and check if you find any types that you need.
This part - which service type to find in which assembly - isn't handled by WCF configuration - you need to do this yourself, by whichever means makes most sense to you.
// find currently executing assembly
Assembly curr = Assembly.GetExecutingAssembly();
// get the directory where this app is running in
string currentLocation = Path.GetDirectoryName(curr.Location);
// find all assemblies inside that directory
string[] assemblies = Directory.GetFiles(currentLocation, "*.dll");
// enumerate over those assemblies
foreach (string assemblyName in assemblies)
{
// load assembly just for inspection
Assembly assemblyToInspect = Assembly.ReflectionOnlyLoadFrom(assemblyName);
if (assemblyToInspect != null)
{
// find all types
Type[] types = assemblyToInspect.GetTypes();
// enumerate types and determine if this assembly contains any types of interest
// you could e.g. put a "marker" interface on those (service implementation)
// types of interest, or you could use a specific naming convention (all types
// like "SomeThingOrAnotherService" - ending in "Service" - are your services)
// or some kind of a lookup table (e.g. the list of types you need to find from
// parsing the app.config file)
foreach(Type ty in types)
{
// do something here
}
}
}
You correctly identified the answer to your problem in your question link and #marc_s answer also gives the correct approach too. The actual issue you are having is that you need to dynamically get the Type instance of an assembly that may only be referenced through a config file so it may not be loaded into the current AppDomain.
Look at this blog post for a way to dynamically reference assemblies in your code. Although the post is specifically for an ASP.NET application, the general approach should work in a self hosted scenario. The ideas is to replace the Type.GetType(string) call with a private method call that dynamically loads the requested assembly (if needed) and returns the Type object. The parameter you send this method will still be the element.Name and you'll need to figure out which is the correct assembly to load. A simple convention-based assembly naming scheme should work. For example, if service type is:
MyNamespace.MyService.MyServiceImpl
then assume the assembly is:
MyNamespace.MyService
Related
maybe someone can help me out with this one:
I'm writing a Program, that dynamically loads assemblies that have different implementations of the same interface "IMyService".
The case may occur, that some of the assemblies aren't even there (imagine this as a set of different modules of a software a user can buy... Some are bought, some aint, therefore the functionality isn't available and the dll isn't delivered).
So what I'm trying to do is the following:
private IServiceProvider ConfigureServices()
{
const string vendor = "MyVendor";
var assembly = Assembly.LoadFrom($"{vendor}.dll");
var myVendorType = assembly.GetType($"{vendor}.Services.{vendor}Service");
if (myVendorType == null)
throw new Exception($"Module '{vendor}' not found");
var services = new ServiceCollection();
// ... other services
serviceCollection.TryAddSingleton<IMyService, myVendorType>();
return serviceCollection.BuildServiceProvider();
}
Unfortunately this won't compile, since the IDE is telling me that it can't resolve the Symbol "myVendorType", when the Type is provided as the Implementation of the "TryAddSingleton..."
I know that the creation of an instance needs an Activator like so:
Activator.CreateInstance(myVendorType);
but, I have no idea what to do, when I want to provide the type to implement to the Service-Collection.
I hope someone has an idea :)
As I mentioned in the comment you could register instance directly in service collection.
For that you could do services.TryAddSingleton<IMyService>(sp => (IMyService)Activator.CreateInstance(myVendorType))
Or even just services.TryAddSingleton<IMyService>( (IMyService)Activator.CreateInstance(myVendorType))
The first option is useful when you need to get something other from ServiceProvider to correctly instantiate the needed instance
I'm trying to execute a method from an object which is loaded from an assembly that is dynamically generated with CompilerResults.CompiledAssembly. However, I need this assembly to have restricted permissions because the methods it contains have an unexpected behavior.
I've searched a lot and the Microsoft docs suggest to use the security features provided by .NET Framework 4. This is one of the many sources I've read about sandboxing, which uses a different AppDomain with restricted permissions. Theoretically, I can create instances from the class within the assembly that is loaded in the restricted AppDomain and this instance will be considered a remote object.
To use the new object as a reference, a proxy is created internally when you try to access the object if the class extends the MarshalByRefObject class. In my case, the class which extends MarshalByRefObject is at the top of the hierarchy.
A simplified version of the code based on the link I've mentioned before is:
var permissionSet = ...;
var dllPath = ...;
// Create the AppDomainSetup
var info = new AppDomainSetup
{
// Set the path to the assembly to load.
ApplicationBase = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)
};
var strongName = assembly.Evidence.GetHostEvidence<StrongName>();
// Create the domain
var sandboxDomain = AppDomain.CreateDomain("RestrictedDomain", null, info, permissionSet, new StrongName[] { strongName });
var handle = Activator.CreateInstanceFrom(sandboxDomain, dllPath, "MyNamespace.MyClass");
var myLoadedTypeInstance = handle.Unwrap() as MyClass;
The code above works, the assembly is loaded in the AppDomain (only can do it referencing it's dll location, not it's name). But when I try to use a method from myLoadedTypeInstance with two serializable parameters I get an exception. The call is something like myLoadedTypeInstance.MyMethod(param1, param2) and the exception is:
"Method InitializeLifetimeService is not supported on this proxy, this can happen if the method is not marked with OperationContractAttribute or if the interface type is not marked with ServiceContractAttribute."
Some details:
I don't know if it matters, but the first parameter is an objects which contains a property of type TChannel, created with ChannelFactory<T>.CreateChannel().
It confuses me that the code fails when initializing lifetime service, but if I use RemotingServices.GetLifetimeService(myLoadedTypeInstance) as ILease I can access all the remote object's lifetime info.
I've also tried to treat it as a remote object explicitly using Activator.GetObject() with the URL provided by RemotingServices.GetObjectUri(myLoadedTypeInstance), which is redundant because I end up creating two instances of the object. But it was a desperate move which had the same ending.
I cannot debug this with VisualStudio as it's running in a separate process.
I have a WCF service that implements two service contracts...
public class MyService : IService1, IService2
and I am self-hosting the service...
host = new ServiceHost(typeof(MyService));
Everything was working fine when the service implemented only one service contract, but when I attempt to set up autofac to register both like this:
host.AddDependencyInjectionBehavior<IService1>(_container);
host.AddDependencyInjectionBehavior<IService2>(_container);
... it throws an exception on the second one, reporting:
The value could not be added to the collection, as the collection already contains an item of the same type: 'Autofac.Integration.Wcf.AutofacDependencyInjectionServiceBehavior'. This collection only supports one instance of each type.
At first glance I thought this was saying my two contracts were somehow being seen as the same type but on second reading I believe it is saying that the
AutofacDependencyInjectionServiceBehavior is the type in question, i.e. I cannot use it twice!
And yet, I found this post that explicitly showed using it multiple times in a slightly different form:
foreach (var endpoint in host.Description.Endpoints)
{
var contract = endpoint.Contract;
Type t = contract.ContractType;
host.AddDependencyInjectionBehavior(t, container);
}
Unfortunately, that gave the very same error message.
Is it possible to register more than one service contract on one service and, if so, how?
In fact you can register multiple endpoint for a single host with Autofac.
That is true that you cannot add multiple AutofacDependencyInjectionServiceBehavior but this behaviour iterates through all the endpoints and registers them in the ApplyDispatchBehavior method: source
In order to make this work you need to register your service AsSelf()
builder.RegisterType<MyService>();
Then you can configure your endpoint normally:
host = new ServiceHost(typeof(MyService));
host.AddServiceEndpoint(typeof(IService1), binding, string.Empty);
host.AddServiceEndpoint(typeof(IService2), binding, string.Empty);
And finally you need to call the AddDependencyInjectionBehavior with the sevicehost type itself:
host.AddDependencyInjectionBehavior<MyService>(container);
Here is a small sample project (based on the documentation) which demonstrates this behavior.
Update (the bold text) based on #nemesv's answer:
Further investigation revealed that with autofac one cannot register multiple endpoints on a single ServiceHost if one registers the WCF service contracts.
(See #nemesv's answer for the correct way to do it.)
Here is why:
Either form of this extension method...
host.AddDependencyInjectionBehavior<IService1>(_container);
host.AddDependencyInjectionBehavior(t, container);
...resolves down to adding a ServiceBehavior (according to Alex Meyer-Gleaves initial announcement of WCF integration in autofac)...
host.Description.Behaviors.Add(behavior);
Now this Behaviors property is an instance of KeyedByTypeCollection<TItem>, which can hold only only one object of a given type. Since the behavior being added will always be an instance of AutofacDependencyInjectionServiceBehavior, one can therefore only add one endpoint.
QED
The workaround is to use multiple ServiceHosts, each with a single endpoint.
(As a point of interest, I would be curious to know the impact on performance and scalability between those two approaches.)
We have 2 orgs running in our on-premise crm 2011 system.
We have generated early bound classes for both orgs.
One of our plugins is throwing the "a proxy type with the name account has been defined by another assembly" error when deactivating an account.
That plugin only references one of the early bound dll's.
How do I get the CRM system to respect the namespace of these references.
I've tried the few items that show up from Google and none are working.
Since you can reproduce this with 2 vanilla orgs I would imaging there is something OUTSIDE the code layer we can do without having to go back and refactor a bunch of code for the 2 orgs.
Thanks,
Jon
The problem is actually with WCF attempting to deserialize the server response and not being able to identify the correct type. The best method to sort this issue is to pass in the current assembly using Assembly.GetExecutingAssembly() to the ProxyTypesBehavior() while creating the proxy like so.
using (serviceProxy = new OrganizationServiceProxy(config.OrganizationUri,
config.HomeRealmUri,
config.Credentials,
config.DeviceCredentials))
{
// This statement is required to enable early-bound type support.
serviceProxy.ServiceConfiguration.CurrentServiceEndpoint.Behaviors.Add(new ProxyTypesBehavior(Assembly.GetExecutingAssembly()));
}
You may run into this issue when referencing different assemblies containing proxy-classes, i.e. one assembly wrapping the server SDK (Microsoft.Xrm.Sdk) and another assembly wrapping the client SDK (Microsoft.Xrm.Sdk.Client).
In such a scenario it seems to be required to tell the OrganizationServiceProxy which assembly should be used to resolve the proxy classes.
This should help:
var credentials = new ClientCredentials();
credentials.Windows.ClientCredential = new System.Net.NetworkCredential(userName, password, domain);
var proxy = new OrganizationServiceProxy(new Uri(discoveryUrl), null, credentials, null);
proxy.EnableProxyTypes(typeof(CrmServiceContext).Assembly);
var context = CrmServiceContext(proxy);
The important thing is to call EnableProxyTypes by passing the correct assembly. I saw another solution using CrmConnection but CrmConnection is only available in the client SDK, which means that you can't instantiate a "server-OrganizationServiceProxy" this way. EnableProxyTypes(Assembly assembly) works for both sides.
Hope this helps.
Regards,
MH
It maybe years since this question has been raised. However, I faced this problem recently and have been extremely worried about thousands of lines of code to be changed. However, I was lucky to find the following simple change to get myself out of hell:
Suppose there are two context objects you deal with:
an OrganizationServiceContext object: context1
a CrmSvcUtil Context object: context2
and a single OrganizationServiceProxy object: service
if in a single method, you make multiple CRUD operations using the same service object but with either of context objects as exemplified above, it is highly probable that this error be raised. However, by doing the following, you can prevent it to happen.
Every time you want to work with context1, you precede the context object with the service object as following:
service.EnableProxyTypes(typeof(OrganizationServiceContext).Assembly);
using (var context1 = new OrganizationServiceContext(_service)){
// your classic code here
}
Also, every time you want to work with context2, you follow the same structure:
service.EnableProxyTypes(typeof(HiwebContext).Assembly);
using (var context = new XYZContext(this._service)){
// your CrmSvcUtil none-classic code here
}
this normally means that there is one or more assemblies with the same method name or property to fix this use the fully qualified name of the assembly.. for example in the using System.IO for example if you had a method named the same way in your Class code that conflicts with System.IO.... you would write your fix like
thisObject.System.IO.Path( ---- ) = somthing for example.. does this make sense..?
I found that adding the Assembly.GetExecutingAssembly() solved the problem.
adding the Assembly.GetExecutingAssembly() solve my problem, you also need to add using System.Reflection;
thanks
A problem with "Add Service Reference", and actually with SvcUtil over all its features.
In order to reproduce you just need to add an OperationContract with argument or returning the following class :
[XmlSchemaProvider("MySchema")]
public class MyStructure : IXmlSerializable
{
private XmlElement e;
private static void Func(object o, ValidationEventArgs args)
{
}
public static XmlQualifiedName MySchema(XmlSchemaSet xs)
{
//xs.XmlResolver = new XmlUrlResolver();
XmlSchema s = XmlSchema.Read(new XmlTextReader(new StringReader("<?xml version=\"1.0\"?><xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"><xs:complexType name=\"MyStructure\"><xs:sequence><xs:any /></xs:sequence></xs:complexType></xs:schema>")), null);
xs.Add(s);
return new XmlQualifiedName("MyStructure");
}
#region IXmlSerializable Members
public System.Xml.Schema.XmlSchema GetSchema()
{
throw new NotImplementedException();
}
public void ReadXml(XmlReader reader)
{
XmlDocument doc = new XmlDocument();
e = (XmlElement)doc.ReadNode(reader);
}
public void WriteXml(XmlWriter writer)
{
e.WriteTo(writer);
}
#endregion
}
The result is that when you use AddWebReference or AddSerivceReference without a reference to the class library containing the MyStructure type, everything will be fine ad you will get an xmlElement representation at the auto created proxy.
However, when you have a reference you will get the following warning :
================
Warning 1 Custom tool warning: Cannot import wsdl:portType
Detail: An exception was thrown while running a WSDL import extension: System.ServiceModel.Description.DataContractSerializerMessageContractImporter
Error: Referenced type 'ServiceLibrary.MyStructure, ServiceLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' with data contract name 'MyStructure' in namespace '' cannot be used since it does not match imported DataContract. Need to exclude this type from referenced types.
XPath to Error Source: //wsdl:definitions[#targetNamespace='http://tempuri.org/']/wsdl:portType[#name='IService1'] \Projects\WCFSample\WCFExample\TestAddReference\Service References\ServiceReference1\Reference.svcmap 1 1 TestAddReference
======================
And no proxy will be generated for you.
Now, the internet is full with descriptions of this when you have a generic DataContract, and/or using IsReference attribute.
This is a much serious problem, since any non-typed data will do this problem.
Could not find any way to solve the problem. What if I want to know the type at the client side, by sharing the class library of the contracts ?
This type of exception generally means there is at least one difference in the type contracts generated by the service as compared to the referenced types (as the message indicates!). But it may not be obvious at first glance, as I found out. Make sure all nested and referenced types are up to date with the server. In my case, nested types were updated on the server. I thought I had updated by locally referenced assembly (and the shared reference types) but I missed some. It took close examination to find the culprit.
See additional information in this question
I have a suggestion:
I had similar errors, including:
the .svcmap file cannot be found. It may have been moved or deleted. To generate a new .svcmap file, delete the service reference and add it again.
And at that point, no way to delete the service reference unless I close VS2010 and open it again.
The situation is: my WCF service is running, I programmatically added a Description.ServiceMetadataBehavior at an HTTP address that I define.
In VS2010, I try to add a service reference at the HTTP address, I see my service, I add the reference, and voila, errors and warning.
The problem: my HTTP address is containing some key words that WCF doesn't like. Specifically the word COM (it breaks with LPT too).
So my solution: modify my HTTP address not to have the word COM. It worked for me.
If the service is hosted over HTTPS, go into the server's IIS Manager. Under "SSL Settings" for the site, make sure "Require SSL" is checked, and check the Client Certificates radio button for "Accept".