Method signature changes after adding WCF service - c#

In my solution I have a create a WCF service which takes message contract class as parameter.
[ServiceContract]
public interface IServiceClass
{
[OperationContract]
Employee AddEmployee(Employee employee);
}
public class ServiceClass : IServiceClass
{
public Employee AddEmployee(Employee employee)
{
//do something
}
}
[MessageContract]
public class Employee
{
[MessageHeader]
public string EmployeeNumber { get; set; }
[MessageBodyMember]
public string FirstName { get; set; }
[MessageBodyMember]
public string LastName { get; set; }
[MessageBodyMember]
public DateTime DOB { get; set; }
}
Then I am adding this service to my client application using Add service reference option. The service added successfully.
Then in my client program I used this method. But the method signature has been changed. It changed as follows.
public void AddEmployee(ref string EmployeeNumber,
ref System.DateTime DOB, ref string FirstName, ref string LastName)
I am using the service in the client as follows.
TcpServiceReference.ServiceClassClient service =
new TcpServiceReference.ServiceClassClient();
NOTE:TcpServiceReference is the service name
I cant understand the reason.

Not exactly sure why WCF does this some times, but if you use the service interface, you'll get the "correct" method signatures:
TcpServiceReference.IServiceClass service =
new TcpServiceReference.ServiceClassClient();
(this is assuming that IServiceClass is the client-side name of the service interface that svcutil (Add Service Reference) has generated for you.)

Have you solved the problem?
Looks like the proxy is not generated successfully, unless you also have a AddEmployee method that returns void (and takes four parameters)?
I would recommend that you re-generate (delete and create) the service reference.
Cheers
--Jocke

Related

Changing namespace for DataContracts in a WCF service

After a major refactoring of our client/service system written in C# with WCF services, we have moved all the service interface contracts to a new namespace, i.e from something like this:
namespace Old.Framework.Name
{
[DataContract]
public class ServiceEntriesResult
{
[DataMember]
public string SomeData { get; set; }
}
[ServiceContract]
public interface IMyService
{
[OperationContract]
ServiceResult GetServiceData();
}
}
To this:
namespace New.Framework.Name
{
[DataContract]
public class ServiceEntriesResult
{
[DataMember]
public string SomeData { get; set; }
}
[ServiceContract]
public interface IMyService
{
[OperationContract]
ServiceResult GetServiceData();
}
}
The only difference is the namespace, but it affects all parameter and return data structures as well as the service interface.
Now, we would very much like to keep new clients compatible with old services. At the moment, if I try to read from an old service with my new client, I get a null result. I can see that the correct service call is triggered on the server side, so it obviously manages to map something right, but the return data object is null.
Any ideas on how to solve this? Is this at all possible?
Looking at
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/data-contract-names
And
DataContractSerializer compatibility after namespace changed
It appears that the default namespace is generated as:
http://schemas.datacontract.org/2004/07/My.Namespace
So in my case, i need to add the following to my DataContract attribute:
namespace New.Framework.Name
{
[DataContract(Namespace = "http://schemas.datacontract.org/2004/07/Old.Framework.Name")]
public class ServiceEntriesResult
{
[DataMember]
public string SomeData { get; set; }
}
[ServiceContract]
public interface IMyService
{
[OperationContract]
ServiceResult GetServiceData();
}
}
This seems to work nicely in my case, so I just need to go through all my DataContracts and add this namespace

WCF OperationContract and dynamic parameter

I have a WCF Service that based on Writing Highly Maintainable WCF Services. Requests are processed using a CommandService:
[WcfDispatchBehaviour]
[ServiceContract(Namespace="http://somewhere.co.nz/NapaWcfService/2013/11")]
[ServiceKnownType("GetKnownTypes")]
public class CommandService
{
[OperationContract]
public object Execute(dynamic command)
{
Type commandHandlerType = typeof(ICommandHandler<>).MakeGenericType(command.GetType());
dynamic commandHandler = BootStrapper.GetInstance(commandHandlerType);
commandHandler.Handle(command);
return command;
}
public static IEnumerable<Type> GetKnownTypes(ICustomAttributeProvider provider)
{
var coreAssembly = typeof(ICommandHandler<>).Assembly;
var commandTypes =
from type in coreAssembly.GetExportedTypes()
where type.Name.EndsWith("Command")
select type;
return commandTypes.ToArray();
}
}
Everything works great (thanks Steve) but now I need to add the ability to upload a file to the service. From what I've read and based on errors received during testing, WCF needs to use a [MessageContract] when uploading a file using a Stream. So I've decorated my command class and put the non-Stream members into the message header, and updated my binding definition to use streaming:
[MessageContract]
public class AddScadaTileCommand
{
[MessageHeader(MustUnderstand = true)]
public int JobId { get; set; }
[MessageHeader(MustUnderstand = true)]
public string MimeType { get; set; }
[MessageHeader(MustUnderstand = true)]
public string Name { get; set; }
[MessageBodyMember(Order = 1)]
public Stream Content { get; set; }
}
Unfortunately when I call the service with a file to upload I get an error:
There was an error while trying to serialize parameter
http://somewhere.co.nz/NapaWcfService/2013/11:command. The
InnerException message was 'Type 'System.IO.FileStream' with data
contract name
'FileStream:http://schemas.datacontract.org/2004/07/System.IO' is not
expected.
So I added a new method to the service specifically for the file upload request:
[OperationContract]
public void Upload(AddScadaTileCommand addScadaTileCommand)
{
Type commandHandlerType = typeof(ICommandHandler<>).MakeGenericType(typeof(AddScadaTileCommand));
dynamic commandHandler = BootStrapper.GetInstance(commandHandlerType);
commandHandler.Handle(addScadaTileCommand);
}
This works perfectly, unless I change the AddScadaTileCommand parameter to dynamic in the method definition, in which case I get the same error as above. This appears to indicate that the [MessageContract] attributes are not applied or ignored when using dynamic as the type of the parameter. Is there any way to resolve this or will I need to create separate methods for requests that involve streams?

Usage of custom data types on client and server wcf service

I have a custom datatype I put in a class Library SharedTypes
namespace SharedTypes
{
public class District
{
public long Id { get; set; }
public string Name { get; set; }
}
}
I then have a WCF server with this service
using System.ServiceModel;
using SharedTypes;
namespace WCF.WCFInterfaces
{
[ServiceContract]
public interface IWcfService
{
[OperationContract]
District GetDistrict(long id);
[OperationContract]
void CreateDistrict(District district);
[OperationContract]
List<District> GetDistricts();
}
}
On the client side I have a Interface (So I inject the implementation)
using SharedTypes;
namespace WcfInterfaces
{
public interface IDistrictManager
{
void CreateDistrict(District district);
District GetDistrict(long id);
List<District> GetDistricts();
}
}
I finally have the implementation the client should use
public class DistrictManager : IDistrictManager
{
private readonly WcfServiceClient _salesService;
public DistrictManager()
{
_salesService = new WcfServiceClient();
}
public void CreateDistrict(District district)
{
_salesService.CreateDistrictAsync(district);
}
public District GetDistrict(long id)
{
return _salesService.GetDistrict(id);
}
public List<District> GetDistricts()
{
var list = _salesService.GetDistricts();
return list.ToList();
}
}
But here the problem arises, this implementation expects to use a version of District it gets from the service reference
WcfClientLibrary.SalesService.District
Instead of
SharedTypes.District
They are the same, but VS dont know that
So I get errors that the interface is not properly implemented because I have 2 different types of the District class.
How can I get the Service reference to use the SharedTypes.District instead? Or is it my way of implementing it that is way off?
Right click your service reference in client project and check "Reuse Types in Referenced Assemblies".
Be sure that you have added SharedTypes.District to your client service reference project.
When adding your WCF reference on the client side. Click on the advanced options.
There is a setting that you can specify to tell it to re-use types from specified assembly(s).
You'll be able to specify the assembly(s).

Can ServiceStack services contain multiple methods?

Environment is Visual Studio 2012, ServiceStack, ASP.NET Web Application Project (followed https://github.com/ServiceStack/ServiceStack/wiki/Create-your-first-webservice)
Looking through some of the classes in ServiceStack.Examples, I noticed that most of the services contain only one method. Either some override on Execute() or, if a REST service, some override of OnPost/Get/Put/Delete().
How should I approach making a full API set, if I have tens of functions I need implemented RegisterUser(), RemoveUser(), AddFriend(), RemoveFriend() ... One service per method?
public RegisterUserService : IService<User> { public object Execute(User> dto) { ... } }
public RemoveUserService : IService<User> { public object Execute(User> dto) { ... } }
public AddFriendService : IService<Friend> { public object Execute(Friend dto) { ... } }
public RemoveFriendService: IService<RequestDTO4> { public object Execute(Friend dto) { ... } }
I'm pretty lost on how to begin implementing a full API set. I've read the first and second wiki page on 'Creating your first webservice', which I've copied to make 1 service method. But now I want to make 10 or 40 service methods and I'm not sure how to do that.
I noticed that implementing from IRestService<T> allows you up to 4 methods instead of the one Execute() method, simply because each method corresponds to a different HTTP verb. So is there something like that I could write? Basically something like:
public MyService : IService/IRestService/ServiceBase?<User>
{
public object AddUser(User user) { }
public object RemoveUser(User user) { }
public object ModifyUser(User user) { }
}
Just looking for something that doesn't necessarily have to contain all methods in one service class, but as many as reasonably possible ... do I really have to create 1 service for each service method?
Note on pursuing a strictly RESTful architecture: I only read up a little on REST, but it seems like I'd have to strictly follow rules like: treat everything as a resource even if you have to re-design your models, no verbs in the URL names (/Friends, not /GetFriends because REST gives you OnGet(), OnPost(), OnPut(), and OnDelete() ... basically I'm interested in the easiest, quickest, and most painless way of implementing a a few dozen service methods. It's a personal project, so the requirements won't vary too much.
Thanks in advance for guiding me through this first step.
EDIT: Just saw this related question: How to send commands using ServiceStack?
Mythz said there's no "ServiceStack way" to design. The guy's question is pretty much like mine. I'm wondering how to stack a lot of service methods in a service.
EDIT 2: Just saw Need help on servicestack implementation, and Separate or combined ServiceStack services?.
I just tested the code below successfully with working routes:
[Route("/registerUser/setEmail/{Email}")]
[Route("/registerUser/setPassword/{Password}")]
[Route("/registerUser/setPhoneNumber/{PhoneNumber}")]
[Route("/lalal2395823")]
[Route("/test3234/test23423511")]
public class RegisterUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Nickname { get; set; }
public string PhoneNumber { get; set; }
public string Email { get; set; }
public string Password { get; set; }
}
But what I'd like is for each [Route("path")] to go to a different method, instead of having them all parsed in Execute() and having to parse which string isn't null or empty.
My Solution
I decided to take Rickard's advice and make a proper REST API, because it seems simpler and cleaner in the end.
This is now my class using the new ServiceStack API (new as of 9/24/12):
using UserModel = Project.Model.Entities.User;
[Route("/User", "POST")]
[Route("/User/{FirstName}", "POST")]
[Route("/User/{FirstName}/{LastName}", "POST")]
[Route("/User/{FirstName}/{LastName}/{Nickname}", "POST")]
[Route("/User/{FirstName}/{LastName}/{Nickname}/{PhoneNumber}", "POST")]
[Route("/User/{FirstName}/{LastName}/{Nickname}/{PhoneNumber}/{Email}", "POST")]
public class CreateUser : IReturn<UserModel>
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Nickname { get; set; }
public string PhoneNumber { get; set; }
public string Email { get; set; }
public string Password { get; set; }
}
public class CreateUserService : Service
{
public object Post(CreateUser request)
{
try
{
using (var session = FluentNHibernateHelper.OpenSession())
{
using (var transaction = session.BeginTransaction())
{
var user = new UserModel()
{
FirstName = request.FirstName,
LastName = request.LastName,
Nickname = request.Nickname,
PhoneNumber = request.PhoneNumber,
Email = request.Email,
Password = request.Password,
};
session.SaveOrUpdate(user);
transaction.Commit();
return user;
}
}
}
catch
{
throw;
}
}
}
This is now a lot simpler with ServiceStack's new API Design released in (v3.9.15+).
#Rickard makes a lot of good points on how to re-structure your service so it's more REST-ful which is now easier to achieve with ServiceStack's new API which is now less restrictive and more flexible where the same service can handle multiple different Request DTOs and you're no longer restricted in the Response Type you can return.
Following the HTTP way you have to turn your way of thinking upside down. You need to think in terms of resources, i.e. users, friends, etc. Using HTTP you already have a finite set of methods, namely Get, Put, Post, and Delete.
Hence, the service API design could look like this:
RegisterUser() => POST /users
RemoveUser() => DELETE /users/{userid}
AddFriend() => POST /users/{userid}/friends
RemoveFriend() => DELETE /users/{userid}/friends/{friendid}
ModifyUser() => PUT /users/{userid}
etc.
To implement for example RemoveFriend in ServiceStack you could do like this:
public class UserFriendService : RestServiceBase<UserFriendRequest>
{
public override object OnPost(UserFriendRequest request)
{
// pseudo code
var user = GetUser(request.UserId);
var friend = GetUser(request.FriendId); // FriendId is a field in the HTTP body
user.Friends.Add(friend);
return HttpResult.Status201Created(user, ...);
}
//...
}
[Route("/users/{userId}/friends")]
public class UserFriendRequest
{
public string UserId { get; set; }
public string FriendId { get; set; }
}

Returning List<T> with WCF service

I got an Employee class and each employee has a list of applied leaves. Is it possible to have the list AppliedLeave as a [DataMember] in WCF?
[DataContract]
public class Employee
{
[DataMember]
public string UserID { get; set; }
[DataMember]
public int EmployeeNumber { get; set; }
[ForeignKey("EmployeeUserID")]
[DataMember]
public List<Leave> AppliedLeave
{
get { return _appliedLeaves; }
set { _appliedLeaves = value; }
}
private List<Leave> _appliedLeaves = new List<Leave>();
...
}
Is there any other way to do this?
thank you for your consideration of this matter
I extend my Question
This is my Leave Class:
[DataContract]
public class Leave
{
[Key()]
[DataMember]
public Guid LeaveId { get; set; }
[DataMember]
public string LeaveType { get; set; }
[DataMember]
public DateTime StartDate { get; set; }
[DataMember]
public string EmployeeUserID { get; set; }
}
this shows ServiceContract ---->
[ServiceContract]
public interface IEmployeeService
{
[OperationContract]
Employee GetEmployeeByUserId(string userId);
[OperationContract]
void AssignSupervisor(string userId, string supervisorUserId);
[OperationContract]
void DeleteEmployeeByUserId(string userId);
....
}
In Client application,
EmployeeServiceClient employeeService = new EmployeeServiceClient();
Employee employee = employeeService.GetEmployeeByUserId(id);
But when Employee gathered from the service its shows Null for leaves,
Can somebody help me? what have I done wrong here?
Yes, it is possible to return generics from WCF service operations.
But by default they are casted to Array on client side. This can be customized while proxy generation.
WCF: Serialization and Generics
Also you have to decorate the service with all the types to which generics can be resolved, using KnownTypeAttribute.
Known Types and the Generic Resolver
I also found my server side list would always arrive at the client as a null pointer. After browsing around a lot for this problem it strikes me it is nearly always denied at first ("your code should work")
Found the issue.. I had configured my solution using one "WCF Service" project and one "Winforms app" project with a generated service reference. Both interface and implementation of Service1 were in the WCF service project, as expected. But any list member returned null.
When I put my IService1.cs = the interface only = in a separate class library instead, reference the class library on both sides (using) and generate the service reference again, my list does work ! The generated code on the client side looks much simpler.
I did not need any special attributes, change service reference configuration, or interface references for this.
You could use IList<T> instead of List<T>.

Categories

Resources