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

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!

Related

MVC .net core: Validator.TryValidateObject is not validating attributes, defined in a buddy class

We used DB-first approach to generate models in a .NET core application. DataAnnotations were put in a "buddy" metadata class so as to avoid writing in an autogenerated file. When controller calls TryValidateModel, all works well, Name property is required.
public partial class User
{
public string Name { get; set; }
}
[ModelMetadataType(typeof(UserMetaData))]
public partial class User : IValidatableObject
{
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) { }
}
public class UserMetaData
{
[Required]
public string Name { get; set; }
}
On a service layer of the app, we want to implement additional validation, that also checks if objects are valid in regards to data annotations. This is done via
Validator.TryValidateObject()
which successfully calls Validate method, but disregards data annotations - user is valid, even with an empty name.
TL;DR:
MVC (web project) knows how to consider data annotations put in a "buddy" class via ModelMetadataType attribute, service layer project does not.
I thought i have found the answer here, but it seems that
TypeDescriptor.AddProviderTransparent
does not work for .net core apps.
Any ideas would be greatly appreciated.
I really hoped for a one line solution :)
I abused ashrafs answer to his own question like so:
var metadataAttr = typeof(T).GetCustomAttributes(typeof(ModelMetadataTypeAttribute), true).OfType<ModelMetadataTypeAttribute>().FirstOrDefault();
if (metadataAttr != null)
{
var metadataClassProperties = TypeDescriptor.GetProperties(metadataAttr.MetadataType).Cast<PropertyDescriptor>();
var modelClassProperties = TypeDescriptor.GetProperties(typeof(T)).Cast<PropertyDescriptor>();
var errs =
from metaProp in metadataClassProperties
join modelProp in modelClassProperties
on metaProp.Name equals modelProp.Name
from attribute in metaProp.Attributes.OfType<ValidationAttribute>()
where !attribute.IsValid(modelProp.GetValue(model))
select new ValidationResult(attribute.FormatErrorMessage(Reflector.GetPropertyDisplayName<T>(metaProp.Name)), new[] { metaProp.Name });
validationResults.AddRange(errs);
}

Entity Framework not retrieving values when used in WCF service

I have this action method in MVC which retrieves data from table using a DbContext:
public ActionResult Index()
{
TwitterContext context = new TwitterContext();
List<TwitterUser> Users = context.User.ToList();
return View(Users);
}
It retrieves the values as expected:
But when I use the same code as a service, it retrieves nothing:
I am calling the service method from the client like this
public class TwitterController : Controller
{
// GET: Twitter
public ActionResult Index()
{
TwitterServiceReference.TwitterContractClient client = new TwitterServiceReference.TwitterContractClient("BasicHttpBinding_ITwitterContract");
List<TwitterServiceReference.TwitterUser> user = client.User().ToList();
return View(user);
}
}
I am using the same connection strings in both cases
<add name="TwitterContext"
connectionString="Integrated Security=true;initial Catalog=TwitterDatabase;server=MYNAME-PC\SQLEXPRESS"
providerName="System.Data.SqlClient"/>
I have all the data filled in the table:
Why is the data empty? How to solve this?
UPDATE:
After checking the SQL Server i see that a database is generated with the name
"TwitterService.TwitterContext , the database is autogenerated when i run the app, and the autogenerated tables data is empty, that is why i am getting empty data returned.
i think you forgot to copy your web.config connection string to that app.config.This might be the issue.
Edit:
i mean the project in which your DataContext class resides
The TwitterUser type you are using in MVC is in namespace Twitter.Models but the one you are using in WCF is in namespace TwitterServiceReference. Therefore, EF will treat them like different things. Your TwitterContext is also in different namespaces and as a consequence this is what is happening (from your comment):
I Just checked the SQL Server Management studio, i think the code created database named "TwitterService.TwitterContext" don't know how and with the same tables as DbContext class
And since it is a brand new database, it has no data and therefore in the WCF service you are not getting any records.
You can have different contexts and use the same database by specifying the same connection string as shown below:
public class TwitterContext : DbContext
{
public MovieDBContext()
: base("TwitterContext")
{
}
}
A Better Approach
Create a separate project and put all your DbContext related code in that project. Most people will refer to this as the Data Access Layer. Then use that project in both the MVC application and the WCF services. This way you will share the TwitterUser and other types in both, and possibly more, applications. You may use Repository Pattern to implement this layer.
But what if my MVC application needs additional info?
There will be cases where TwitterUser may not be sufficient for your MVC views and you may need additional properties. In that case, you can create a Model specifically for that view (some people call this view model but I stay away from that since that is a term used in MVVM and it is very different). The class can be designed like this:
public class TwitterUserModel
{
public TwitterUserModel(TwitterUser user)
{
this.TwitterUser = user;
}
public string AnotherPropertyNeededByView {get; set;}
public TwitterUser TwitterUser { get; set; }
}
Or you can use AutoMapper or similar mappers to map from TwitterUser to TwitterUserModel.
After some trials i got the success
i just added this code
public class TwitterContext : DbContext
{
//Added this Code with the connection string
public TwitterContext() : base(#"Integrated Security=true;server=MYNAME-PC\SQLEXPRESS;database=TwitterDatabase")
{
//Disable initializer
Database.SetInitializer<TwitterContext>(null);
}
public DbSet<TwitterUser> User { get; set; }
public DbSet<TwitterUserData> UserData { get; set; }
}
I first tried adding "TwitterContext" as base("TwitterContext") which is the name of my connection string and matches with my DbContext class name which did not work, so i directly added the connection string, the data is retrieved now and is preventing from generating new database.

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.

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

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

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;

Categories