Reporting Services Web Service, C# .NET code reuse - c#

I'm building a custom front-end for a collection of reporting services servers. I'm adding the ReportingServices2005 web reference to my project using;
http://server/ReportServer_InstanceName/ReportService2005.asmx?wsdl
At the moment my approach is to add this reference for each server, however I'm then struggling with the code reuse aspect. The reporting services classes are then different namespaces.
I'd like to have a method as below;
public string ListReports(Server1WebService.ReportingService2005 service) {
service.Credentials = System.Net.CredentialCache.DefaultCredentials;
service.Server1WebService.CatalogItem[] children = service.ListChildren("/", true);
string list = String.Empty;
foreach (Server1WebService.CatalogItem i in children) {
if (!i.Hidden)
list += i.Name + "</br>";
}
return list;
}
To make this method reusable I need to know how to refactor this so that any instance of the ReportingService2005 class can be passed regardless of the namespace. At the moment I have to specify Server1WebService for all references to ReportingService2005 and CatalogItem.

Provided that all of the SSRS instances are the same version, You should be able to set the URL property on the proxy object:
Server1WebService server.url = new uri ("http://server/ReportServer_InstanceName/ReportService2005.asmx?wsdl"));
If you have multiple versions to deal with, you may need to provide some type of factory object that can correctly instantiate the correct version.
Hope this helps

Related

Using Autofac, how can I resolve a collection of services and ignore ones that fail to resolve?

Summary
I'm trying to see if there's a way I can ask Autofac to resolve all implementations of a particular type (IEnumerable<TService>), returning all implementations that can be successfully resolved while silently ignoring those that fail.
Details
In one of my libraries I define an ICommunicationService interface which I implement in various other places. I've created a "test bed" ASP.NET Core web service that I can use to pick up all implementations of this interface among its dependencies and allow me to test them.
When I try to access the selection page in the browser, the web service uses the following query to find all candidates:
var query = from service in container.Resolve<IEnumerable<ICommunicationService>>()
let serviceType = service.GetType()
let serviceAssembly = serviceType.GetTypeInfo().Assembly
let assemblyName = serviceAssembly.GetName().Name
let version = serviceAssembly.GetName().Version.ToString()
select new { service, assemblyName, version };
However, recently I added a new kind of "restartable" communication service that doesn't handle any communication itself but delegates to other services. This makes it unsuitable as a candidate for testing. However, since it's a public concrete type, Autofac tries (and fails) to resolve it:
DependencyResolutionException: None of the constructors found with
'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type
'MyCompany.Communications.UsbDevice.RestartableUsbCommunicationService'
can be invoked with the available services and parameters: Cannot
resolve parameter 'Int32 vendorId' of constructor 'Void .ctor(Int32,
Int32, DelegateServiceFactory,
LibUsbDotNet.DeviceNotify.IDeviceNotifier, Serilog.ILogger)'.
I make the assumption that any service that can be resolved is one I want to test (one directly involved with communication with a device), whereas ones that fail to resolve are types like this, that just wrap other services.
I've fixed this by writing the method below, which iterates over every registration that exposes the desired type of service and tries to resolve them in turn. The ones that resolve are returned and the ones that fail to are ignored.
private static IEnumerable<T> TryResolveCollection<T>(IComponentContext context)
{
bool RegistrationProvidesService(IComponentRegistration registration) =>
registration.Services.OfType<IServiceWithType>().Any(it => it.ServiceType.Name == typeof(T).Name);
var registrations = context.ComponentRegistry.Registrations.Where(RegistrationProvidesService);
var resolvedServices = new HashSet<Type>();
foreach (var registration in registrations)
{
T service;
try
{
service = (T)context.ResolveComponent(registration, Enumerable.Empty<Parameter>());
}
catch (DependencyResolutionException)
{
continue;
}
if (resolvedServices.Add(service.GetType()))
{
yield return service;
}
}
}
This could be turned into an extension method on IComponentContext pretty easily, but since I only need it in one place, I just defined it as a private method and use it like this:
var query = from service in TryResolveCollection<ICommunicationService>(container)
let serviceType = service.GetType()
let serviceAssembly = serviceType.GetTypeInfo().Assembly
let assemblyName = serviceAssembly.GetName().Name
let version = serviceAssembly.GetName().Version.ToString()
select new { service, assemblyName, version };
What's interesting is that when I run this, it still manages to resolve a RestartableUsbCommunicationService, even though I have no idea what will happen if I try to run that (I don't know which underlying communication service it's wrapping).

Using a "shared" type on a WCF/MVC project with two services ("cannot convert from...")

I've two WCF services connected to my client. I want to use a User-object, retrieved from service #1, and use this as paramter for service #2. Here is my MVC-Controller TournamentController.cs code:
private readonly GCTournamentServiceClient _tournamentClient = new GCTournamentServiceClient();
public ActionResult Join(int id)
{
GCUserServiceClient userClient = new GCUserServiceClient();
// Get current user
var currentUser = userClient.GetUser(0);
if (currentUser != null)
{
// Get selected tournament
var selectedTournament = _tournamentClient.GetTournament(id);
// Check if there are available seats in the tournament
if (selectedTournament.Seats > selectedTournament.RegistredUsers.Count)
{
// Check if user exist amoung registred users
if (!selectedTournament.RegistredUsers.Contains(currentUser))
{
selectedTournament?.RegistredUsers.Add(currentUser);
}
}
}
}
The error Visual Studio prompt me with:
Argument 1: cannot convert from 'GConnect.Clients.WebClient.GCUserService.User' to 'GConnect.Clients.WebClient.GCTournamentService.User'
So the problem is currentUser, which has the type GCUserService.User. I'm unable to use this as parameter for RegistredUsers
The error itself makes perfect sense, however, I'm not quite sure how I'm suppose to convert this type (properly). Some articles states, that a "shared"-service has to be created, which holds the User-type. I just can't believe, that a solution like that, should be necessary.
I might have misunderstood some basic stuff here (working with WCF and MVC), but please enlighten me, if that's the case.
So the problem is currentUser, which has the type GCUserService.User.
I'm unable to use this as parameter for RegistredUsers
There are 2 approaches to solve this problem:
1)
Create a class library project (Visual Studio) and add the User class in that project, compile it and add its assembly's (.dll) reference to both services and the client (your MVC application). Next retrieve that user object as you are already doing it
var currentUser = userClient.GetUser(0);
GetUser will return the type of User that is defined in a separate assembly which is added as reference as suggested above. The TournamentService will also reference the same assembly and the RegistredUsers.Add(User userToadd) method will take the same User object and WCF runtime should be able to serialise/desterilise it.
2)
In your MVC client application, new up the User object that is acceptable by the TournamentService.RegistredUsers.Add method. Populate its properties from the currentUser and pass in that object as parameter to RegistredUsers.Add method.
Best Practice
Ideally, I would recommend the first approach which is more work but a better practice and that your User class is maintained centrally and code is reused.
Hope this helps!

Why the result from the web service references and Service references are different?

I am bit curious about one thing which has happen while trying to understand the concept of Service References and Web Service References.
What I did is?
In my project I have added a web service as a Service Reference and trying to get my script run through the use of client.
But while getting result it is throwing an exception as in the following image:
I have tried to trace out the cause but not able to get the proper answer for that.
I have following code for the resultant object.
[
ComVisible(false),
Serializable,
SoapTypeAttribute("RecordList", "http://www.someadd.com/dev/ns/SOF/2.0"),
XmlType(TypeName="RecordList", Namespace="http://www.someadd.com/dev/ns/SOF/2.0")
]
public class MyRecordListWrapper
{
private IxRecordList recordList = null;
private const string XMLW3CSchema = "http://www.w3.org/2001/XMLSchema";
[SoapElement("Headers")]
public Header[] Headers = null;
[SoapElement("Records")]
public Record[] Records = null;
// some methods to work on intialization
public SmRecordListWrapper(ref IxRecordList p_RecordList)
{
recordList = p_RecordList;// record list initialization
Headers = CreateWrapperHeaders(); // will return header class object
Records = CreateWrapperRecords(); // will return record object
}
}
Can anyone tell me why this error is showing for me?
While adding reference as a Web Service Reference
when I add the same reference as a web reference that time the program is not showing any error and runs successfully?
So can anyone tell me what is the difference in working with the same code using service reference and web service reference?
and Which is a correct way to ass references?
Hope I will get some more described answers to make the things easy to understand.
Thanks in advance.
Adding a web reference, visual studio uses xsd.exe to generate the classes from the service metadata. This uses XmlSerializer under the hood.
Adding a service reference, visual studio uses svcutil.exe to generate the classes from the metadata. This uses DataContractSerializer under the hood.
Two separate tools, two outcomes. For general information, DataContractSerializer is a lot less forgiving when it comes to generating classes from metadata.

.NET WebService WebMethod with custom object as Parameter

I have created a .NET WebService. There I have implemented the following WebMethod:
[WebMethod]
public string CheckLicense(License license) {
return "";
}
The Type License comes from a different Assembly X which I have referenced to the WebService. The Fulltype of License is Prayon.Shared.Library.Licensing.License
Now, I have build a client which also references the Assembly X. When I try no to call the WebService with CheckLincense:
private void CheckLicense(License license) {
using(var service = new Prayon.Service.Web.PrayonService()) {
service.CheckLicense(license);
}
}
There service.CheckLicense() want an object of Type Prayon.Service.Prayon.Service.Web.License.
I don't know what I am doing wrong. What does I have to do, that I can pass a object of Type Prayon.Shared.Library.Licensing.License to service.CheckLicense()?
If you want to use a method in your License object you need to :
Call your WebService, obtain a service.License object, use it to create an instance of your local License object, after that you will have your 'local' License object with state (properties) filled by your WebService answer.
Otherwise i do not see why you would want to use a 'local' License object ?
You should not share the assembly between your web service and your client but instead create an instance of your License from your web service proxy types (should be found into service.x)
You need to create an instance from the webserviceproxy rather than from the asembly.
When you add a service reference to your webserice from the client it will generate proxy classes for you.Inside these you will have the License defined.use this to create an instance and pass it rather than referencing directly from your assembly.

Unable to cast object of type MyObject to type MyObject

I have this scenario where a webservice method I'm consuming in C# returns a Business object, when calling the webservice method with the following code I get the exception "Unable to cast object of type ContactInfo to type ContactInfo" in the reference.cs class of the web reference
Code:
ContactInfo contactInfo = new ContactInfo();
Contact contact = new Contact();
contactInfo = contact.Load(this.ContactID.Value);
Any help would be much appreciated.
This is because one of the ContactInfo objects is a web service proxy, and is in a different namespace.
It's a known problem with asmx-style web services. In the past I've implemented automatic shallow-copy to work around it (here's how, although if I were doing it again I'd probably look at AutoMapper instead).
For example, if you have an assembly with the following class:
MyProject.ContactInfo
and you return an instance of it from a web method:
public class DoSomethingService : System.Web.Services.WebService
{
public MyProject.ContactInfo GetContactInfo(int id)
{
// Code here...
}
}
Then when you add the web reference to your client project, you actually get this:
MyClientProject.DoSomethingService.ContactInfo
This means that if, in your client application, you call the web service to get a ContactInfo, you have this situation:
namespace MyClientProject
{
public class MyClientClass
{
public void AskWebServiceForContactInfo()
{
using (var service = new DoSomethingService())
{
MyClientProject.DoSomethingService.ContactInfo contactInfo = service.GetContactInfo(1);
// ERROR: You can't cast this:
MyProject.ContactInfo localContactInfo = contactInfo;
}
}
}
}
It's on that last line that I use my ShallowCopy class:
namespace MyClientProject
{
public class MyClientClass
{
public void AskWebServiceForContactInfo()
{
using (var service = new DoSomethingService())
{
MyClientProject.DoSomethingService.ContactInfo contactInfo = service.GetContactInfo(1);
// We actually get a new object here, of the correct namespace
MyProject.ContactInfo localContactInfo = ShallowCopy.Copy<MyClientProject.DoSomethingService.ContactInfo, MyProject.ContactInfo>(contactInfo);
}
}
}
}
NOTE
This only works because the proxy class and the "real" class have exactly the same properties (one is generated from the other by Visual Studio).
As several of the other answers have suggested, it is because .NET sees them as two different classes. I personally would recommend using something like AutoMapper. I've been using it, and it seems pretty awesome. You can copy your objects in 1-2 lines of code.
Mapper.CreateMap<SourceClass, DestinationClass>();
destinationInstance = Mapper.Map<SourceClass, DestinationClass>(sourceInstance);
Actually this is not a bug. It's a problem with the version changes of your own project!
Because your final run don't use the original imported references on compile!
For example, I was making a chat server, client. I used a packet structure to transmit data on client project.
Then imported the same reference to server project.
When casting Packet packet = (Packet)binaryFormatter.Deserialize(stream); I got the same error. Because the actual running reference at server project is not the reference now at client project! Because I have rebuilt client project many times after!
In casting <new object>=(<new object>) <old object> always the new object needs to be a newer or same version as the old object!
So what I did was I built a separate project to create a DLL for the Packet class and imported the DLL file to both projects.
If I did any change to Packet class, I have to import the reference to both client and server again.
Then the casting won't give the above exception!
How are you referencing the class in your web service project as well as consumer project? If you have simply used a file link, this could well explain the cause of the error. The way serialiasation works for .NET (Web Services or otherwise I believe) is by using reflection to load/dump the data of an object. If the files are simply linked, then they are actually getting compiled to different types in different assemblies, which would explain why you have the same name but can't cast between them. I recommend creating a 'Core' library which both the web service and consumer project references, and contains the ContactInfo class which you use everywhere.
This isn't a problem - it's a feature.
They are two independent classes. Compare the two, and notice that the proxy class has none of the constructors, methods, indexers, or other behavior from the original class. This is exactly the same thing that would happen if you consumed the ASMX service with a Java program.
Seems like you have two different classes on both ends. Your application has ContactInfo class and your webservice also have the ContactInfo class. Both are two completely different classes. One way is to use the WebService class on your side. If you are using ContactInfo inside your web service then it will be serialized and will be available on the client side for use.
You can also modify your References.cs file generated by Visual Studio when the web reference is added. If you remove the proxy generated classes and add a reference (using statements) to your personal classes, you'll be able to use them straight away, without shallow copy / reflection or heavy mapping. (but you'll have to re-apply your modification if you regenerate the proxy layer).
I also tried to serialize the proxy object and deserialize them back in my DTO classes but it was quite heavy resources wise so I ended up modifying the References cs generated layer.
Hope it will help other people coming here :)
Kindly.

Categories

Resources