I've looked around - can't find the answer to this, even in lots of sample code.
I'm trying to compile a WCF Service Application that uses [datacontract] classes as parameters on the interface members, which are from a global C# class library... This is on the server side. When I import the service reference into the client, it's re-namespace-based the global class library classes, and generated a bunch of serialization code!
I cannot add a reference to the global class library in the client project and use the classes freely. this seems clunky. I've checked the button when importing the service reference "reuse types", but I don't know what that does, but it's not the right thing.
During the import of the service library, it allows me to specify the namespace for the about-to-be-generated proxy classes. I'm pretty sure this isn't supposed to be the same namespace as the classes used on the server side!
example:
GLOBAL CLASS LIBRARY
namespace SquallGlobal
[datacontract] class ProcessStartInfo{ }
WCF SERVICE
namespace Squall
[servicecontract] interface IJob{
[OperationContract] StartJob( SquallGlobal.ProcessStartInfo psi );
}
END USER PROJECT
WCF Service imported under namespace 'Squall_Imported'
using SquallGlobal;
if I want to call proxy.StartJob( ), I need to pass in a Squall_Imported.ProcessStartInfo, not SquallGlobal.ProcessStartInfo!
Thus, the final question: How do I keep the proxy-generation code from re-basing the namespace on global classes used in interface methods?
Related
I have a WCF Data Service layer that is exposing POCO entities generated by the POCO T4 template. These POCO entities are created in their own project (i.e. Company.ProjectName.Entities) because I'd like to share them wherever possible.
I have a set of interfaces in another project (Company.ProjectName.Clients) that reference these POCO types by adding an assembly reference to the Company.ProjectName.Entities.dll. One of the implementation of these interfaces is a .NET client that I want to consumes the service using the WCF Data Service Client Library.
I've used the Add Service Reference to add service reference. This generated the DataServiceContext client class and the POCO entities that are used by the service. However, these POCO types gemerated by the Add Service Reference utility now have a different namespace (i.e. Company.ProjectName.Clients.Implementation.WcfDsReference).
What that means is that the POCO types defined in the interfaces cannot be used by the types generated by the utility without have to cast or map.
i.e. Suppose I have:
1. POCO Entity: Company.ProjectName.Entities.Account
2. Interface: interface IRepository<Company.ProjectName.Entities.Account>{....}
3. Implementation: ServiceClientRepository : IRepository<Company.ProjectName.Entities.Account>
4. WcfDsReference: Company.ProjectName.Clients.Implementation.WcfDsReference
& Company.ProjectName.Clients.Implementation.WcfDsReference.Account
Let's say I want to create a DataServiceQuery query on the Account, I won't be able to do this:
var client = new WcfDsReference(baseUrl);
var accounts = client.CreateQuery<Company.ProjectName.Entities.Account>(...)
OR: client.AddToAccounts(Company.ProjectName.Entities.Account)
, because the CreateQuery<T>() expects T to be of type & Company.ProjectName.Clients.Implementation.WcfDsReference.Account
What I currently have to do is to pass the correct entity to the CreateQuery method and have to map the results back to the type the interface understands. (Possible with a mapper but doesn't seems like a good solution.)
So the question is, is there a way to get the Add Service Reference utility to generate methods that use the POCO types that are in the Company.ProjectName.Entities namespace?
One solution I am thinking of is to not use the utility to generate the DataServiceContext and other types, but to create my own.
The other solution is to update the IRepository<T> interface to use the POCO types generated by the utility. But this sounds a little bit hacky.
Is there any better solution that anyone has come up with or if there's any suggestion?
Ok, a few hours after starting the bounty I found out why it wasn't working as expected on my end.
It turns out that the sharing process is quite easy. All that needs to be done is mark the model classes with the [DataServiceKey] attribute. This article explains the process quite well, in the 'Exposing another Data Model' section
With that in mind, what I was trying to do is the following:
Placing the model on a separate class library project C, sharing it with both webapplication projects A and B
Create the data service on project A
Add the service reference on project B
Delete the generated model proxies out of the service reference, and update it to use my model classes in project C
Add the DataServiceKey attribute to the models, specifying the correct keys
When I tried this it did not work, giving me the following error:
There is a type mismatch between the client and the service. Type
{MyType} is not an entity type, but the type in the
response payload represents an entity type. Please ensure that types
defined on the client match the data model of the service, or update
the service reference on the client.
This problem was caused by a version mismatch between project C (which was using the stock implementations on the System.Data.OData assemblies) and the client project B that was calling the service (using the Microsoft.Data.OData assemblies in the packages). By matching the version on both ends, it worked the first time.
After all this, one problem remained though: The service reference procedure is still not detecting the models to be shared, meaning proxies are being created as usual. This led me to opt out of the automatic service integration mechanic, instead forcing me to go forward with a simple class of my own to serve as the client to the Wcf Data service. Basically, it's a heavily trimmed version of the normally autogenerated class:
using System;
using System.Data.Services.Client;
using System.Data.Services.Common;
using Model;
public class DataServiceClient : DataServiceContext
{
private readonly Lazy<DataServiceQuery<Unit>> m_units;
public DataServiceClient(Uri _uri)
: base(_uri, DataServiceProtocolVersion.V3)
{
m_units = new Lazy<DataServiceQuery<Unit>>(() => CreateQuery<Unit>("Units"));
}
public DataServiceQuery<Unit> Units
{
get { return m_units.Value; }
}
}
This is simple enough because I'm only using the service in readonly mode. I would still like to use the service reference feature though, potentially avoiding future maintenance problems, as evidenced by the hardcoded EntitySet name in this simple case. At the moment, I'm using this implementation and have deleted the service reference altogether.
I would really like to see this fully integrated with the service reference approach if anyone can share a workaround to it, but this custom method is acceptable for our current needs.
I have been able to do this before with a prototype project but because now it is going into "implementation" with other things included in the project, there are some problems I am having in terms of WCF being able to pick up messages from the MSMQ.
The solution is structured that we have "teams". Each project represents this (to some extent).
Teams can only access the projects they require.
So:
IncomingMessaging project (references "Messages" project)
This contains classes which are concrete classes which derive from base classes of its own type (in "Messages" project). The base classes are all in "Messages" project. Example:
FirstReport -> FirstReportBase
SecondReport -> SecondReportBase
ThirdReport -> ThirdReportBase
Each of the base classes derive from "MainBase". This also resides in "Messages" project.
The MainBase has been decorated with the KnownTypes attribute and has all the base known types. (FirstReportBase, SecondReportBase etc...)
I then have a WCFImplementation.Messages project, which references "Messages" and the "IncomingMessaging" project.
The WCFImplementation.Messages project has a concrete class which is used for WCF binding to MSMQ the actual implementation like so:
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
public class InboundMessagingService : IInboundMessagingService
{
[OperationBehavior(TransactionScopeRequired = true)]
public void ProcessIncomingMessage(MsmqMessage<MainBase> msg) { ... }
}
The IInboundMessagingService has the ServiceContract attribute and also has the ServiceKnownType attributes for all the base types
with me so far?
I then have a simple Console app project to host the service. Note at this point I have send a FirstReport message to the queue:
FirstReport fr = new FirstReport(....);
The console app is run and I get a faulted service. It is unable to read the message on the queue! (poison message)
What am I missing?
Does the Interface for the IInboundMessagingService have to be decorated with the concrete classes? Can I not just use base classes?
I got it to work with the prototype before as I have both the concrete (FirstReport) and base classes (FirstReportBase) decorated as the service known types in the interface but this was a "loose" type project structure
Problem seemed to have been that the objects and subobjects etc... needed all to be marked with the DataContract attributes.
Then the main concrete/base class should have knowntypes attributes decorated with the known types which are the subclasses of this base class.
I'm creating a service reference to a web service written in Java. The generated classes now follow the Java casing convention used in the web service, for example class names are camelCase rather than PascalCase.
Is there a way to get the desired casing from the service reference?
CLARIFICATION:
With WSE based services, one could modify the generated Reference.cs to provide .NET standard casing and use XmlElementAttribute to map to the Java naming presented by the external web service, like this:
[System.Xml.Serialization.XmlElementAttribute("resultType", Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
[System.Runtime.Serialization.DataMember]
public virtual MyResultType ResultType
{ ... }
Not terribly maintenance-friendly without writing custom code to either generate the proxy code or modify it after it's been generated.
What I'm after is one or more options to present a WCF generated client proxy to calling applications using the .NET casing conventions, achieving the same as I did previously with WSE. Hopefully with less manual effort.
Well, since your WCF client proxies are partial classes, you could always add a second file for the same class, which implements the PascalCasedMethodName for each javaCasedMethodName and then just call the Java method from your new method.
public partial class MyClientProxy
{
......
public MyResultType GetResultType(string inputParam)
{
return this.getResultType(inputParam);
}
......
}
Seems a bit redundant - but that should really work, I think. Since your code is stored in a separate file, it won't be overwritten if you re-create the client proxy - and since it's the second part of a partial class, it will be "merged into" the class definition for your client code to call.
I have a plugin that I will instantiate at runtime and I want to pass it a WCF service from the application host. The application host is responsible for creating the connection to the service. The reason for this is that a single service can be used by multiple plugins, but the plugins should only know about its interface since there may be several implementation of IMyPluginServices. For instance, the Run method of the plugin instance would be:
public void Run(IMyPluginServices services)
{
services.DoSomething();
}
The problem I am running into is that I don't know how to create a service of type IMyPluginServices and pass it to the Run function. The service reference generated by VS 2010 doesn't seem to create an object of type IMyPluginServices that I can pass to it. Any help would be greatly appreciated. Thanks.
When you add a service reference in VS 2010 for a service it generates an interface named IMyService which contains methods for each OperationContract in your service. It also generates a concrete class named MyServiceClient, which can be constructed and then used to invoke your service.
Now, the problem that you're running into, I believe, is that MyServiceClient is a subclass of ClientBase<IMyService>, and does not implement the generated IMyService interface (which is a real pain).
To get around this problem I ended up making a new interface:
public interface IMyServiceClient : IMyService, IDisposable, ICommunicationObject
{
}
(Note: IDisposable and ICommunicationObject are only required if you want your module to be able to detect/react to faulted channels and other such things).
I then extend MyServiceClient with a partial class (in the assembly that contains my WCF Service reference):
public partial class MyServiceClient : IMyServiceClient
{
}
Now in my modules I can accept an IMyServiceClient instead of an IMyService, and still execute all of the methods that I need to. The application in control of the modules can still create instances of MyServiceClient as it always did.
The beauty of this is that your new interface and partial class don't need any actual code - the definitions suffice to get the job done.
I have a seperate assembly ( reference by WebService) in which I have created a class ( Let's say ABC ) and a collection of that class ( ABCCollection : IList where T:ABC ). Now when I build the proxy files (output.config and Service1.cs) then the defienation of these two classes are not exposed. Instead the ABCCollection is exposed in Servic1.cs is like ABCCollection4IP3 .
Please let me know the possible cause for this issues..
They are not meant to be the same type. This is by design. Consider how it would work if your service were in .NET and your client in Java. They would clearly be two different types.
A best practice when designing a WCF service is to split up your project into seperate assemblies:
Assembly SomeProject.ServiceContract
This assembly contains your service contract (just the interfaces).
Example:
[ServiceContract (...)]
public interface ICan {
[ServiceOperation (...)]
void EatCandies (MyListOfCandies candies);
}
Assembly SomeProject.DataObjects
This assembly contains all your data objects which is used by your service contract.
Example:
[DataObject]
public class MyListOfCandies : List<Candy> {
...
}
In your project, which is consuming your web service reference the assembly "SomeProject.DataObjects" and then add your web service. You'll see that Visual Studio will no longer generate any stub objects but will use your implementation.
You can do the same with the ServiceContract assembly. This way you still can use web services, but you will get compile errors when you change your interface.