Change DataMember property on WCF web service depending on input/output? - c#

Scenario: An entity from data model is passed into a WCF Web Service with various information, saved into a database and then returned back with the object fully populated with additional information.
public class Request
{
public virtual Guid RequestID { get; set; }
public virtual string RequestType { get; set; }
public virtual System.DateTime CreatedDate { get; set; }
//More properties here populated from DB
}
[OperationContract]
Request CreateRequest(Request input);
In this example, the RequestID and CreatedDate are populated only when the record is inserted into the database, and therefore should not be visible during the initial request. They should be visible when the object is returned however.
The current approach that we are going with is to create two classes (RequestInput, RequestOutput) in our web service implementation project which inherit from the entity.
We will then add [DataMember] attributes on various properties that are required and [IgnoreDataMember] on those that should be ignored.
Is this the correct approach?

I wouldn't say it is a correct or incorrect way. But it is more usual to use names something along the line of
[DataContract]
Request{...}
and
[DataContract]
Response{...}
the Request and Response should ideally be decoupled from the model representation you are using in the client and the server - ie you have a facade or adaptor that maps them to your model from your service code.
this is along the lines of how I would do it - but this is very subjective dependant on size of entities etc - you may want to involve an auto-mapper somehow.
// higher level code
var entity = new Entity { properties we know before call };
// pass down to service layer
var response = service.CreateRequest(new Request { Prop1 = entity.Prop1... } );
entity.RequestID = response.RequestId;
entity.CreatedDate = response.CreatedDate;

Related

Smaller version of domain objects to send to a (low memory) client

I have domain objects with lots of properties (members) that I am using in my API. In some of the transactions, it makes more sense to send the domain objects to the client rather than a whole new response class with the same members
Now my question is, is there a way that I hide some properties of the class and only send some properties (members) to the client (and in a different call send a different set of properties and hide rest)?
You can create new response DTO(Data Transfer objects) classes containing the only properties you want to send back. Then create factory methods for converting your objects of Domain Classes to response DTO.
This will serve your purpose. Later if you want to add new properties in your DTO that actually exist in other domain classes you can add those easily.
Just to add in case you intend to use it. You can have Request DTO's as well, The client's data may not necessarily map to your domain classes. Again you can create a factory method that will convert your request DTO into domain class/classes object(s).
As already has been answered, DTO is more suited for this. As an alternative, you can "hide" unwanted properties from the client behind a more specific public interface. Your domain objects can simply implement them.
Example:
class Document : IEntity, IVersionable, IDocumentContentDescription
{
public string Name { get; set; }
public string Description { get; set; }
public string MimeType { get; set; }
public long Length { get; set; }
public int Version { get; set; }
}
public interface IDocumentContentDescription
{
string MimeType { get; }
long Length { get; }
}

Best way to do property level authorization in ServiceStack?

I'm currently developing a SPA in Angular, and so I've created a REST service using ServiceStack. I am also using ServiceStack's default authentication and authorization solution, which allows me to decorate services with the Authenticate attribute, and also allows me to authorize roles.
However, since my application has users, and users own resources, I need a way to restrict non-authorized users from performing certain actions. Furthermore, I would like to be able to create a single service for each discrete entity which can properly figure out what is safe to write to the database and what is safe to return to the user depending on their level of authorization.
So as an example, let's say I've created a service to handle operations on a Group entity. One of the actions I allow on a Group is to get the details for it:
Route: api/groups/{Id}
Response: Name, Description, CoverImageUrl, Members
However, depending on who the user is, I wish to restrict what data is returned:
Not authenticated: Name, CoverImageUrl
Authenticated: Name, CoverImageUrl, Decription
Member of requested group: Full access
Admin of website: Full access
So one simple approach to doing this is to create 3 different response DTOs, one for each type of response. Then in the service itself I can check who the user is, check on their relation to the resource, and return the appropriate response. The problem with this approach is that I would be repeating myself a lot, and would be creating DTOs that are simply subsets of the "master" DTO.
For me, the ideal solution would be some way to decorate each property on the DTO with attributes like:
[CanRead("Admin", "Owner", "Member")]
[CanWrite("Admin", "Owner")]
Then somewhere during the request, it would limit what is written to the database based on who the user is and would only serialize the subset of the "master" DTO that the user is permitted to read.
Does anyone know how I can attain my ideal solution within ServiceStack, or perhaps something even better?
The direct approach is the easiest, but you could also take advantage of custom filters attributes.
[Route("/groups/{Id}"]
public class UpdateGroup
{
public int Id { get; set; }
public string Name { get; set; }
public string CoverImageUrl { get; set; }
public string Description { get; set; }
}
[RequiresAnyRole("Admin", "FullAccess")]
[Route("/admin/groups/{Id}"]
public class AdminUpdateGroup
{
public int Id { get; set; }
public string Name { get; set; }
public string CoverImageUrl { get; set; }
public string Description { get; set; }
//... other admin properties
}
Service implementation:
public object Any(UpdateGroup request)
{
var session = base.SessionAs<AuthUserSession>();
if (session.IsAuthenticated) {
//.. update Name, CoverImageUrl, Description
}
else {
//.. only update Name, CoverImageUrl
}
}
public object Any(AdminUpdateGroup request)
{
//... Full Access
}
What ended up being the most pragmatic solution for me was actually pretty simple. The basic idea is that whichever service requires row-level authorization should implement a GetUserRole method, which in my case returns the user's most permissive role.
protected string GetUserRole(Domain.Group entity)
{
var session = SessionAs<AuthUserSession>();
var username = session.UserName;
if (session.Roles.Contains("Admin"))
{
return "Admin";
}
if (entity.Id == default(int) || entity.Leader.Username.Equals(username))
{
return "Leader";
}
// More logic here...
return session.IsAuthenticated ? "User" : "Anonymous";
}
Then I can use the user's role to figure out what to let them write:
var entityToWriteTo = ... // code that gets your entity
var userRole = GetUserRole(entityToWriteTo);
if (new[] {"Admin"}.Contains(userRole))
{
// write to admin-only entity properties
}
if (new[] {"Admin", "Leader"}.Contains(userRole))
{
// write to admin or leader entity properties
}
// Etc.
And the same logic applies for reads: You populate a DTO with properties set conditionally based on their role. Later on when you return the DTO back to the client, any properties that you haven't set either won't be serialized or will be serialized with a null value.
Ultimately, this solution allows you to use a single service for a resource instead of creating multiple services each with their own request DTO. There are, of course, refactorings you can do that makes this solution more streamlined. For example, you can isolate all of your reads and writes to one part of your code which will keep the services themselves free of role checks and things like that.

Domain Model with partially loaded objects

Let's say I have an application which consists of both client and server. Client is using MVVM pattern (with WPF) and server is simply a WCF service which fetches some data from database and returns data as DTO-objects to client. In client, DataAccess layer converts these DTOs to domain objects and passes them to Model. ViewModel uses Model to fetch data (Domain Object) and populates itself with it.
To optimize database performance, each ViewModel is given only the data it really needs and nothing more (as recommended by many sources). For example, let's say there is an entity called DbCustomer which has 30 properties, and there are also 3 different Views related to customers: CustomerProfileView, CustomersListView and CustomerThirdView. Every view needs different portion of data: CustomerProfileView uses 20 properties, CustomersListViewuses 10 properties and CustomerThirdView uses only 4 properties. For each View, only required properties are fetched from database and delivered to ViewModel.
Now the problem arises: how should I design my Domain Objects to support this?
Solution 1, one partially loaded Domain Object (no-go)
If I have only one Customer Domain Object which is used by all ViewModels, it would have different data depending on the ViewModel that requested it. Obviously this is a no-go way because if I have to use this Customer object somewhere else I cannot be sure does it have enough properties loaded.
For example, I might have method GetDataStoragePath which is supposed to return string describing path to customer's private files. The method requires properties FirstName, LastName, SSN and IsExternalCustomer. Now, let's say CustomerThirdView doesn't need IsExternalCustomer, so it is not loaded when CustomerThirdViewModel requests Model to load Customer. Now if I use this Customer somewhere else (it is not a ViewModel specific object), the method GetDataStoragePath will fail.
Solution 2, three different Domain Objects
In another solution there would be 3 different Domain Objects (used as data containers) with suitable interfaces, and thenGetDataStoragePath would depend only from this interface. Example:
public interface ICanGetDataStoragePath {
string FirstName { get; }
string LastName { get; }
string SSN { get; }
bool IsExternalCustomer { get; }
}
public CustomerProfileData : ICanGetDataStoragePath { ... } // Implements interface
public CustomerListViewData : ICanGetDataStoragPath { ... } // Implements interface
public CustomerThirdViewData { ... } // Does NOT implement interface
public class CustomerLogic : ICustomerLogic {
public string GetDataStoragePath(ICanGetDataStoragePath customer) {...}
}
This would lead to Anemic Domain Model but it is not a problem in my opinion. However, it seems messy since I can easily imagine that there would be 20 different methods with different needs which would result in 20 interfaces (and only for Customer, there are LOTS of other domain objects also). Of course in this simple case I could pass all four parameters separately to GetDataStoragePath but in real life there are many more required properties.
Are there any other options? What would be the best way to solve the problem?
Your model obviously has to much Data. Why not make 3 models and one composite model?
i.e.
public class CustomerProfile
{
public string Phone { get; set; }
// other profile fields
}
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string SSN { get; set; }
public bool IsExternalCustomer { get; set; }
public CustomerProfile Profile { get; set; }
}
Then you'd put all of your always required fields into the Customer class and group the rest together, i.e. in a CustomerProfile class. If it's null, then that data wasn't fetched and isn't available

Looking for a workaround to serializing a method or constant

Apparently my education has failed me, because I didn't realize that methods in C# cannot be serialized. (Good to know.)
I am trying to create a WCF service that returns a simple class I created. The problem is that this simple class contains methods that I want to expose, and the caller of my service won't have any access to them (assuming they won't have a .dll containing the class declaration).
public class Simple
{
public string Message { get; set; }
private const string _Hidden = "Underpants";
public string Hidden
{
get { return _Hidden; }
}
public string GetHidden()
{
return _Hidden;
}
}
I set up a WCF service (let's call it MyService) to return an instance of my Simple class. To my frustration, I'm only getting a partial build of my class back.
public void CallService()
{
using (var client = new MyService.Serviceclient())
{
Simple result = client.GetSimple();
string message = result.Message; // this works.
string hidden = result.Hidden; // this doesn't.
string fail = result.GetHidden(); // Underpants remains elusive.
}
}
Is there any type of workaround where I'm able to set up a property or method on my class that will be accessible to whomever calls my service? How does one handle constants or other methods that are set up in a class that only exists in a service?
Typically you would create three different projects.
1. Service project
2. Client project
3. Data project
The Data project contains only the data classes - no application code. The methods and constants in these data classes should be independent of the Service/Client projects.
The Data project is included as a reference in both the Service and Client projects so that serialization and deserialization happen against the same binary - and you get to retain your constants/methods/etc.
The downside here is that all your clients will either have to be .NET apps, or you will have to provide different data libraries for each platform you wish to support.
As far as I know the only things that can be returned in a WCF service are primitives or a class with public properties that have a get method on them. From a high level WCF exists to allow you to specify a contract between the client and the server that it in theory transportation agnostic (ie you can swap out an HTTP endpoint for a netTcp endpoint and the service will function the same way from a contractual level).
The question to answer then is what data are you trying to pass back in this service call. If it's an object called simple with the data points of Message and Hidden then I would advise creating a data class called Simple that has those values as properties:
[DataContract]
public class Simple
{
[DataMember]
public string Hidden { get; set; }
[DataMember]
public string Message { get; set; }
}
When the client receives the response back Message and Hidden will be populated with whatever you have set their values to on the server side.
The DataMember attribute can only be used on properties and fields. This means that a WCF response can only serialize these types.
If you really want to only use the const in your WCF contract You could convert it to a field and place the DataMember attribute on it:
public class Simple
{
[DataMember]
public string Message { get; set; }
[DataMember]
public const string Hidden = "Underpants";
}
To be able to do this the field must be accessible (public).
Add the DataMember attribute to your property. To do so, you must have both a get and a set defined.
[DataMember]
public string Hidden
{
get { return _Hidden; }
set { }
}
technically you could do
public class thingToSerialize{
public Func<ArgType1,ArgType2...,ReturnType> myFunction{get;set;}
}
and then assign it a lambda that takes the arguments and returns the return type
before serializing

How do I expose non persisted properties using a WCF Data Service?

I've created an entity model using Entity Framework 4, which I've exposed via a WCF Data Service. One of my entities needs to have properties defined that are not persisted to the database, but the Entity Model designer doesn't allow you to do this.
To get round this I've defined all my objects as POCO objects, which allows you to add non persisted properties to your objects, but not your model.
The issue I have is that, because these non persisted properties only exist in the objects themselves and not the model, they are not exposed via the WCF Data Service.
Is there any way to define properties in an entity model that are not persisted to the database?
Thanks in advance for any replies
Ryan
Well the classes for the model are partial. You can write yours non persisted properties in the other parts of the classes. Please write down if this works because I am not using WCF Data Services, but every time when i need a property in business objects which is not mapped to field in the DB I make it that way.
I think ikirachen is on the right track with using a partial class to define additional properties. For WCF to expose them, you must also mark the properties with the DataMember attribute. I created a small WCF service to test this:
[ServiceContract]
public interface IService1
{
[OperationContract]
string DoStuff(User user);
}
public class Service1 : IService1
{
public string DoStuff(User user)
{
string result = string.Empty;
foreach (Guid leadId in user.AssociatedLeadIds)
{
// This is going to console on client,
// just making sure we can read the data sent to us
result += leadId.ToString() + "\n";
}
return result;
}
}
// partial entity model class
public partial class User
{
// This is not persisted to our DB with the user model
[DataMember]
public ICollection<Guid> AssociatedLeadIds { get; set; }
}
And here is the client code, showing the AssociatedLeadIds exposed via WCF:
class Program
{
static void Main(string[] args)
{
User u = new User
{
// Here we set our non-persisted property data
AssociatedLeadIds = new Guid[]
{
Guid.NewGuid(),
Guid.NewGuid(),
Guid.NewGuid()
},
// The rest are persisted properties
ApplicationId = Guid.NewGuid(),
UserName = "TestUser",
LoweredUserName = "testuser",
LastActivityDate = DateTime.Now,
IsAnonymous = false
};
using (Service1Client svc = new Service1Client())
{
// Here we call the service operation
// and print the response to the console
Console.WriteLine(svc.DoStuff(u));
}
Console.ReadKey();
}
}
Hopefully this helps!

Categories