WCF Service, Same Contract, But Some Data member are specific to client - wcf

This is the scenario, Say I have a OperationContract GetEmployeeDetails, it returns Employee class.
class Employee
{
public int EmpId { get; set; }
public string EmpName { get; set; }
public string Salary{ get; set; }
}
Now I have two clients say HR and Manager.
When Manager client create the proxy, salary property should not be exposed.
When HR client create the proxy, salary property should be exposed.
I am using WCF Services, cant implement REST, dont have any control on the client side.
Cant use Interface to achieve this as the actual entity is very Complex, with multiple inner classes.
Please let me know, Is there any way I could implement this.

Don't do it this way. Do a proper authentication/authorization mechanics (data should be returned on the basis of user rights).
Reason: even if this was possible, your system would be a cake for any malicious attacker with as little as Visual Studio in hands. Giving out confidential data (and salary certainly is such data) based on client type is asking for trouble.
Never do access restriction in such way.

You could consider having a DataContract that exposes Salary as an optional property,
e.g.:
[DataContract]
public class Employee
{
[DataMember(IsRequired = true, Order = 0)]
public int EmpId { get; set; }
[DataMember(IsRequired = true, Order = 1)]
public string EmpName { get; set; }
[DataMember(IsRequired = false, Order = 2)]
public decimal? Salary{ get; set; }
}
Then only return a non-null value for the optional property if the client is authorized (e.g. must be in the "HR" role in your case).
Obviously this requires that you authenticate your clients, and have an authorization mechanism in place (e.g. an ASP.NET RoleProvider).
If your optional property is a value type, it should be a Nullable value so that you can return its value as null - e.g. in the above example, I've made Salary a decimal?.
With this approach, all clients see the data contract, so know of the existence of the optional property (which could in some situations be considered as a security weakness), but only authorized clients will see a value for the optional property.

Related

Does including Collections in Entities violate what an entity is supposed to be?

I am building a Web API using Dapper for .NET Core and trying to adhere to Clean Architecture principles. The API is consumed by an external Angular front-end.
I have repositories that use Dapper to retrieve data from the database, and this data then passes through a service to be mapped into a DTO for display to the user.
It is my understanding that an entity should be an exact representation of the database object, with no extra properties, and that I should use DTOs if I require some additional properties to show the user (or if I wish to obscure certain properties from the user too).
Suppose I have a DTO:
public class StudentDTO
{
public Guid Id { get; set; }
public string Name { get; set; }
public List<Assignment> Assignments { get; set;}
}
and its corresponding Entity:
public class Student
{
public Guid Id { get; set; }
public string Name { get; set; }
}
With this model, should I want to get a student with all of their assignments, I'd need to have two repository calls, and do something like this in the service:
public StudentDTO GetById(Guid id)
{
var student = this.studentRepository.GetById(id);
var assignments = this.assignmentRepository.GetByStudentId(id);
return SomeMapperClass.Map(student, assignments);
}
But this seems inefficient and unnecessary. My question is, should I not just retrieve the Assignments when I get the student entity in the repository, using a JOIN? Or would this violate what an entity is supposed to be?
I apologise, I do realise this is a rather simple question, but I'd really like to know which method is the best approach, or if they both have their use cases
I think it would be more efficient, since map uses reflections, that is slower tens times
public StudentDTO GetById(Guid id)
{
var student = this.studentRepository.GetById(id);
student.Assignments = this.assignmentRepository.GetByStudentId(id);
return student;
}
but the common way is
return _context.Students.Include(i=>i.Assignments).FirstOrDefault(i=> i.Id==id);
This is why the generic repository is a bad idea in the most casses, since it is hard to guess what set of data you will need.

DataContract classes uninitialized at client side

I have the following class I'd like to send from my WCF (C#) service to my client (WPF):
[DataContract]
public class OutputAvailableEventArgs
{
[DataMember]
public int ID { get; set; }
[DataMember]
public string Message { get; private set; }
[DataMember]
public bool IsError { get; private set; }
public OutputAvailableEventArgs(int id) : this(id, false, "") { }
public OutputAvailableEventArgs(int id, string output) : this(id, false, output) { }
public OutputAvailableEventArgs(int id, bool isError, string output)
{
ID = id;
IsError = isError;
Message = output;
}
}
It's used by the service as follows:
var channel = OperationContext.Current.GetCallbackChannel<IClientCallback>();
channel.OutputAvailable(new OutputAvailableEventArgs(1, false, "some message"));
At the client side, the members get their default values.
I tried marking them with IsRequired attribute but now the OutputAvailable at the client is not called. The code at the service side seems to run smoothly (I didn't notice anything with the debugger).
How can I transfer a DataContract class with WCF while maintaining the members' values?
(I saw solutions that suggested to use OnSerialized and OnDeserialized but I don't need just a default constructor.)
I saw many different solutions for this problem. For other people's sake I'll write some of them down + what worked for me:
It seems that in some cases specifying the items' order solves the problem. Please see this SO question for full details.
If it's some default initialization you're after, you can use OnSerialized and OnDeserialized methods to call your initialization methods.
I also tried using the IsRequired attribute on my DataMembers but still didn't get my objects.
What worked for me was adding NameSpace property in the DataContract attribute. Apparently, In order to have the contracts be considered equal, you must set the Namespace property on the DataContract to the same value on both sides.

WCF Client code does not reflect WCF service data contract changes automatically

I originally have defined a data contract for testing purpose as
[DataContract]
public class CreditCard: ISensitive
{
[DataMember]
public string CardNumber { get; set; }
}
The Wcf client reads this contract properly and I can use client reference classes for manipulating CardNumber field. However, when I use .NET Entity framework to generate the data contract's actual implementation I do not see the changes reflected completely in the client reference classes.
[DataContract]
public partial class CreditCard: EntityObject
{
// all Properties coming from database table
[DataMember]
public string CardNumber { get; set; }
[DataMember]
public string CardHolderName { get; set; }
[DataMember]
public DateTime ExpiryDate { get; set; }
}
public partial class CreditCard: ISensitive
{
// interface implementation and other methods
}
I am only able to manipulate CardNumber property, while other properties are not generated in the client code. I also do not see other entity types being generated on client code. Is there some versioning problem I am neglecting? Am I updating the service reference incorrectly? Why does the client code does not show DataContract classes generated by Entity Framework?
If you need to update your service definition on Client side, go to Solution Explorer. Under your client project, open 'Connected Services' and right-click on your Service Reference that connects to your buggy service. Select 'Update Service'.
That should renew the service definition on your client side to match the host.

WCF serialization and Value object pattern in Domain Driven Design

The book Domain Driven Design by Eric Evans describes pattern called value object. One of the important characteristics of a value object is that it is immutable.
As an example I have a value object "Clinic" which must have a name and an id. To make it a value object I do not provide setters on name and id. Also to make sure that there is not invalid instance I take name and id in a constructor and do not provide at parameter less constructor.
public class Clinic
{
public Clinic(string name, string id)
{
Name = name;
Id = id;
}
public string Name{get; private set;}
public string Id{get; private set;}
}
The problem is that when I try to return this object from a WCF Service I get an exception that the object does not have parameter less constructor and the properties do not have public setters. I want to avoid adding parameter less constructor and public setters because then my domain model pattern goes for a toss. How can I get around this problem?
Regards,
Unmesh
I had a similar problem with serializing immutable types before, in the end I decided to implement the ISerializable interface and use the SerializationInfo to store & retrieve the private variables at both ends of the serialization/deserialization process:
http://theburningmonk.com/2010/04/net-tips-making-a-serializable-immutable-struct/
I just built and run a test app using the same technique and it seems to work for me. So in terms of changes to your Clinic class you could change it to:
[Serializable]
public class Clinic : ISerializable
{
public Clinic(string name, string id)
{
Name = name;
Id = id;
}
public Clinic(SerializationInfo info, StreamingContext context)
{
Name= info.GetString("Name");
Id= info.GetString("Id");
}
public string Name{get; private set;}
public string Id{get; private set;}
[SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("Name", Name);
info.AddValue("Id", Id);
}
}
This will solve the problem you're having passing the data back from WCF. But from a design point of view, I agree with what Ladislav is saying and typically you will want to separate your domain objects with objects purely intended for message passing (DataTransferObjects), and in that case here's an example of how you MIGHT approach it:
// the domain object (NOT EXPOSED through the WCF service)
public class Clinic
{
public Clinic(string name, string id)
{
Name = name;
Id = id;
}
public string Name{ get; private set;}
public string Id{ get; private set;}
// other methods encapsulating some business logic, etc.
...
}
// the corresponding DTO object for the domain object Clinic
// this is the type exposed through the WCF layer, that the client knows about
[DataContract]
public class ClinicDTO
{
[DataMember]
public string Name { get; set; }
[DataMember]
public string Id { get; set; }
}
// WCF service contract, NOTE it returns ClinicDTO instead of Clinic
[ServiceContract]
public interface IClinicService
{
[OperationContract]
ClinicDTO GetClinicById(string id);
}
To ease the pain of converting from Clinic to ClinicDTO, you could either add a method on Clinic to do this or implement an implicit/explicit converter. I've got an example on how to do this here:
http://theburningmonk.com/2010/02/controlling-type-conversion-in-c/
Hope this helps!
The problem is that your value object is not serializable. How do you plan to use the service? Do you plan to share domain objects / value objects with your clients? If yes than it IMO violates your domain driven desing - only business layer should be able to work with domain objects and call their methods. If you don't want to share objects you will probably create proxy by add service reference which will generate data contrats for the client. These contrats will have public parameterless constructor and all properties settable (and no domain methods).
If you want to have real Domain driven design you should not expose your domain objects in WCF. Instead you should create set of DTO and expose those DTOs. Service layer will be responsible of converting those DTOs to Domain objects / value objects and vice-versa.

How can I pass a List<Interface> over WCF?

I have a WCF service where I am trying to return a List (where IWatchable is a custom interface I have built) in one of my operation contracts. When I test the service on the client the method returns an object[] instead of List<IWatchable>. Is it possible to return a List of IWatchable, since IWatchable is an interface with WCF?
Method:
public List<IWatchable> GetWorkload( Guid nodeId, int maximum )
IWatchable:
public interface IWatchable
{
string ActionName { get; set; }
Guid ActionReference { get; set; }
}
Hopefully a bit more info will be helpful...
I have a derived interface:
public interface IAMRAWatchable: IWatchable
And three concrete implementations from IAMRAWatchable:
public class InstrumentationWatch: IAMRAWatchable
public class OutputWatch: IAMRAWatchable
etc...
In my WCF method that returns List<IWatchable> I want to send an InstrumentationWatch and an OutputWatch to the client... Is this possible or am I going about this the wrong way?
Resolved
Thanks to John I found my solution. KnownType wasn't working since I was using List<IWatchable> - So I wrapped my list into a new class and added the attributes to it. I'll need to re-factor my code but for others who are interested here is the class:
[DataContract]
[KnownType( typeof( InstrumentationWatch ) )]
[KnownType( typeof( OutputWatch ) )]
public class WorkInfo
{
[DataMember]
public List<IWatchable> WorkQueue { get; set; }
}
and my WCF method:
public WorkInfo GetWorkload( Guid nodeId, int maximum )
An interface can never be serialized. It is only a description of behavior.
You can serialize objects which implement the interface, but you must tell WCF what their types are. See Data Contract Known Types.