I'm writing some service.
I'm asking 2 questions regarding 2 situations:
Situations
1) i use it "in-house", meaning as a referenced dll
2) I use it over the net, as webService
Questions
A)
If it exposes an interface to some client-class.
and now I added some method to that interface.
Do I have to recompile all its clients to recognize
the new interface even if they don't use the new methods?
B)
If a method in my service's interface expect some PoCo with property a,b
and one of the client calls that method with the same Poco type, but this Poco is defiened in the client-code as one that has a,b as before and also c,d.
Do I have to recompile the service? Does the contract breaks?
For web services, if you are certain that changes will happen, you may want to design a 'versioned' interface that takes generic arguments and a version number. I have done this using an XML string that holds the contents of the parameters passed (XML string passed to web service a HTML encoded to avoid breaking SOAP envelope).
In the web service methods, checking the version number than proceeds to parse the XML into the expected type(s) for that method:
public void GenericWebServeceMethod(int version, string encodedXMLParameters)
{
string xmlParameters = HTMLDecode(encodedParameters);
switch(version)
{
case 1:
ParameterType1 p1 = DecodeVersion1XML(xmlParameters);
MethodVersion1(p1);
break;
case 2:
ParameterType2 p2 = DecodeVersion2XML(xmlParameters);
MethodVersion2(p2);
break;
}
}
When new versions are added, you extend the switch statement with new versions which still allows clients using older versions of the parameter types to continue working.
For a referenced DLL used 'in-house' you can force the reference to be version independent by adding the reference using the 'Browse' option in Visual Studio, and then selecting the reference's properties and ensuring that the 'Specific Version' property is set to False. Then you can still use a system similar to above to change the way than XML-encoded parameters are handled based on a passed version number. Adding new methods is allowed but the calling client will not 'see' them until it it recompiled.
Related
I'd be grateful if you could help with this:
I have a base class for a Provider.
This is subclassed to create a
DefaultProvider.
DefaultProvider implements an interface called “IExportProvider”
DefaultProvider is subclassed to create a
DanishProvider.
The base class has various virtual methods that return string values in English.
The DanishProvider overrides these to return strings in Danish.
At runtime, I load either the DefaultProvider or the DanishProvider depending on a config. value. I do this by scanning the current assembly for the type name, which is either “DefaultProvider” or “DanishProvider”, and which implements “IExportProvider”
var importServiceType = assembly.DefinedTypes.FirstOrDefault(type => (type.Name.Equals(providerServiceName, StringComparison.OrdinalIgnoreCase)) && type.ImplementedInterfaces.Any(i => i == typeof(I)));
I then use Activator.CreateInstance to create an instance of the type:
return (IExportProvider)Activator.CreateInstance(importServiceType, …)
Problem
I use the code in a .Net 4.8 Web application, where I load the DanishProvider. This mostly works OK and I can see the Danish strings are returned. However, occasionally (after several days) the application appears to incorrectly load the DefaultProvider, and I see English strings returned.
Once it has switched to English it appears to stay on the DefaultProvider.
Twice I have done an IISRESET which fixes the problem immediately.
The settings
are not being changed.
There is only one type in the assembly called “DanishProvider”
The code is executing on a single UAT server. I have not seen the issue on my local PC.
I do not believe that the issue is related to caching.
Q. Is there a problem with the code approach described above?
Thank you
I have a CodeFix provider, purpose of which to inject service to current class, like add new private field, parameter to constructor, assignment statement in constructor, using ServiceNamespace, and use of this field in proper place(s).
At some point, I have a name of this service, and I need to find namespace for this service to add correct using. I'm doing this via compilation.GetSymbolsWithName(typeName), but this method can return several matched symbols from different namespaces/assemblies.
So, the question: is there any way I can show this variants to user so he can decide the correct type?
For each diagnostic, you can add more than one fix.
I'm working with pre-defined IOrganizationService object I cannot modify — it's out of my control.
And it seems to be by default this IOrganizationService returns resolved types as if EnableProxyTypes() was called implicitly somewhere on originating OrganizationServiceProxy.
And this ruins all business logic, since early bound entities could not be serialized that easily, since on different installations they could be resolved to different types. So even if they would be successfully serialized, cannot be 100% certain that they could be de-serialized.
To be more specific it's plugin for XrmToolBox. If hosting application loaded plugin that uses early bounding, these generated types will be discovered via reflection and applied to results of Retrieve and RetrieveMultiple calls.
For example RetrieveMultiple to account entity response will normally contain Entity[], each item of this array will have LogicalName property set to account.
But if early bound types was discovered in assembly, let's call it EarlyBouldLibrary.dll, the same RetrieveMultiple will return EarlyBouldLibrary.Account[]. Each item of this array will be derived from Entity and will contain same field as in first example, but with additional properties.
The problem is in following. If we will try to serialize EarlyBouldLibrary.Account[] to XML it will fail. Because EarlyBouldLibrary.Account is unknown.
Ok, it's possible to add types to known. But there is no guarantee, that one day another early bound types will be found in NewEarlyBouldLibrary.dll... And they will have name NewEarlyBouldLibrary.Account... Which is unknown...
Ok, let's assume serialization succeeded, no matter which types are referenced EarlyBouldLibrary.Account or NewEarlyBouldLibrary.Account, but if we will try to de-serialize, on environment where these early bound libraries not present... action will fail.
So only way will be not to use specific early bound types, that out of our control, and use standard and safe Entity. But it's impossible while system automatically resolves proxy types.
So, question: is there any way to disable resolve of early bound types?
You can redirect your proxy to another assembly containing the types you need (or even containing no early bound types at all).
The example below resets the proxy assembly:
private void ResetProxyAssembly(IOrganizationService service)
{
var serviceProxy = (OrganizationServiceProxy) service;
serviceProxy.EnableProxyTypes();
}
In certain scenarios this may not work. Instead you can explicitly select the desired proxy assembly:
private void ResetProxyAssembly(IOrganizationService service)
{
var serviceProxy = (OrganizationServiceProxy) service;
serviceProxy.EnableProxyTypes(typeof(MyEarlyBoundEntity).Assembly);
}
Could you add [assembly: Microsoft.Xrm.Sdk.Client.ProxyTypesAssembly] to the AssemblyInfo file of the EarlyBouldLibrary.dll or NewEarlyBouldLibrary.dll assembly?
I wrote a few SOAP Webservices in Java, running on a JBoss 5.1.
Two of them share a class, AddressTO. The Webservices are deploying correctly on my ApplycationServer and all went well until I try to use the class addressTO in my C#-client. There are two types in the client application, addressTO and addressTO1. This is a problem because this causes errors like:
Fehler 1 Eine implizite Konvertierung vom Typ
"acsysteme.i4workspace.client.webservices.addressTO1[]" in
"acsysteme.i4workspace.client.webservices.addressTO[]" ist nicht möglich.
[...]
This means that it is impossible to cast the to types implicitly.
AddressTo is something like a core class which can be used by other webservices.
The webreferences for the C#-client are created by the command
wsdl.exe /parameters:CreateWebService.xml
The xml-file contains the urls to the differend .wsdl-files of my webservices.
Does someone know how to handle this problem?
Use the /sharetypes option when calling wsdl.exe:
/sharetypes
Turns on type sharing feature. This feature creates one code file
with
a single type definition for identical types shared between
different
services (namespace, name and wire signature must be identical).
Reference the services with http:// URLs as command-line
parameters
or create a discomap document for local files.
If the classes match exactly, they should only be generated once if you generate code for both services in a single command. Both services will be using the same class, so no conversion will be necessary.
Edit:
If the XML namespaces do not match (which is a common occurrence), .NET will consider them to be different types, and rightly so. You will either have to fix the web services so the types are exactly the same (recommended), or do conversion between the two generated types. This will result in a lot of boring property assignment code, so you might want to consider using something like AutoMapper to handle the conversion for you.
wsdl.exe should generate partial classes, so if you want, you can define implicit conversions between the different types:
public static implicit operator addressTO1(addressTO source)
{
addressTO1 result = new addressTO1();
// Assign properties, etc.
return result;
}
I'm not usually a big fan of implicit conversions myself, but in this case it might be warranted.
I solved it!
I followed the hint of Thorarin to use the wsdl.exe option sharetypes. But to use this option is not enougth. First, you need to setup the correct namespace (using a URI) in the Webservice class in your java server with the following annotation:
#WebService(targetNamespace="http://com/project/client/webservices/")
public class WebServiceImplementation implements WebService{
// ... your #WebMethod-methods
}
Second, you need to modify the settings in createWebService.xml accordingly: The namespace of the webservice needs to be added like this:
<wsdlParameters xmlns="http://microsoft.com/webReference/">
<!-- Defaultsettings -->
<language>CS</language>
<sharetypes>true</sharetypes>
<namespace>com.project.client.webservices</namespace>
<!-- output -->
<out>soap/WebServices.cs</out>
<appSettingUrlKey>BaseUrl</appSettingUrlKey>
<appSettingBaseUrl>http://localhost:8080</appSettingBaseUrl>
<!-- web service locations -->
<documents>
<document>http://localhost:8080/Core?wsdl</document>
<document>http://localhost:8080/WebService0?wsdl</document>
<document>http://localhost:8080/WebService1?wsdl</document>
</documents>
</wsdlParameters>
That's it! Call wsdl.exe /parameters:createWebService.xml and you are done.
Thanks for your help!
We are using a Microsoft ERP which dynamically exposes web services. The services generated by the service is out of our control. We have no say so in how the objects, including the type definitions, are created and exposed. When a new method is added or removed from the web service, all of the type enumerations are renumbered and everything that uses the web service, after updating to the new definitions, is hosed up. So essentially,
enumeration Type1
Item1
Item2
Item3
... could become
enumeration Type6
Item1
Item2
Item3
...with the enumeration type name changing, but members of the type remaining static. The service outputs a service which looks exactly like the end result of using the XSD.exe to generate objects. So anytime someone exposes a new method on the service (via the ERP GUI), the objects are rebuilt, types are assigned to the service definitions in alphabetical order, reexposed, leaving the entire code base shot.
I attempted to use reflection to determine the type and then parse out the static member into the new business object, but it doesn't work because I can't type cast the enumeration without knowing the actual name of the type. The following won't work.
System.Type t = service.BusinessObjectEnumeration.GetType();
service.SomeField = Enum.Parse(t,"Item1");
...as the compiler throws an error because I'm not explicitly casting the enumeration.
Any ideas how I can overcome this issue while dynamically casting the type to the correct enumeration?
Again, I cannot modify the actual objects exposed by the service, only the code subscribing to the service.
Re the example code:
System.Type t = service.BusinessObjectEnumeration.GetType();
service.SomeField = Enum.Parse(t,"Item1");
Maybe the way to do this is via reflection:
var prop = service.GetType().GetProperty("SomeField");
prop.SetValue(service, Enum.Parse(prop.PropertyType, "Item1"), null);
I had a similar issue with Nav web services. The solution I used to to create a new web reference for each service you expose through Nav rather than just a single one for all services. This prevents the name collisions you are experiencing without using reflection or hacks.
Why should you need to parse things?
If I understand Enums correctly, they pass on the value (and not the Enum itself).
EDIT: What I mean is, enum is not same as a class. For a class, one expects an instance to be passed/received. For an enum, it is one or combination of its members, which is passed in form of an int value.
EDIT2: Are you trying to use the enum as some kind of struct here?
EDIT3: You will have to see what type is the enum in debug mode, to figure out how to reflect on it.
object enumValueReturned = service.BusinessObjectEnumeration;
Put the enumValueReturned in watch window & play with it using reflection (GetMembers) to see, how would you reach Item1.