I am trying to build a WCF DataService on top of a Code Only DbContext. However as soon as I add the first DbSet property to my DbContext I get "Request Error" when accessing the .svc.
All I need to get the error is to have this DbContext:
public class JukeboxContext : DbContext
{
public DbSet<Song> Songs { get; set; }
}
and then do a basic WCF DataService based on it, and the error happens when i run the service.
Any ideas on what I need to do?
Turnes out the exception means that it can't resolve which field is the primary key.
The easy way to go is to name it ID or ID - otherwise the DataServiceKey attribute will let you use composite keys
Related
I'm trying the built-in Authentication of ServiceStack. My approach is 'OrmLiteAuthRepository' so users' information are stored in Sql Server instead of the default in memory storage. I use Postman to test the endpoints.
My target is receiving user rows, updating user information, creating users, deleting an user row. Those are the endpoints I found in Postman after importing (I didn't create those endpoints):
GET 'http://localhost:47391/api/register',
PUT 'http://localhost:47391/api/json/reply/Register'
POST 'http://localhost:47391/api/json/reply/Register'
I tested POST, Sql Server automatically created the tables to store user data. And the data could be written into Sql Server so I have no problem with POST.
But with PUT, isn't it for updating the existing row? I append '/{id}' to the end. But it created a new row in the database instead of updating the existing one. How does it work?
With GET, I got no implementation error.
{
"ResponseStatus": {
"ErrorCode": "NotImplementedException",
"Message": "Could not find method named Get(Register) or Any(Register) on Service RegisterService",
"StackTrace": " at ServiceStack.Host.ServiceExec`1.Execute(IRequest request, Object instance, Object requestDto, String requestName)\r\n at ServiceStack.Host.ServiceRequestExec`2.Execute(IRequest requestContext, Object instance, Object request)\r\n at ServiceStack.Host.ServiceController.<>c__DisplayClass11.<>c__DisplayClass13.<RegisterServiceExecutor>b__10(IRequest reqCtx, Object req)\r\n at ServiceStack.Host.ServiceController.ManagedServiceExec(ServiceExecFn serviceExec, IService service, IRequest request, Object requestDto)\r\n at ServiceStack.Host.ServiceController.<>c__DisplayClass11.<RegisterServiceExecutor>b__f(IRequest requestContext, Object dto)\r\n at ServiceStack.Host.ServiceController.Execute(Object requestDto, IRequest req)\r\n at ServiceStack.HostContext.ExecuteService(Object request, IRequest httpReq)\r\n at ServiceStack.Host.RestHandler.GetResponse(IRequest request, Object requestDto)\r\n at ServiceStack.Host.RestHandler.ProcessRequestAsync(IRequest httpReq, IResponse httpRes, String operationName)"
}
}
How to implement it? I assume I consider the user a normal Web Service entity? and create 'UserService', and requests like:
[Route("/register")]
public class User : IReturn<UserResponse>
{
...
}
BUT there isn't a model class like 'User' due to the tables are created by ServiceStack itself, how to solve this?
Or is there something I am not aware of. Thanks.
The error message:
Could not find method named Get(Register) or Any(Register) on Service RegisterService
Is saying you're trying to call the built-in ServiceStack Register Service instead of your Service. But the Register Services isn't enabled by default, your AuthFeature likely explicitly enables it, either with:
Plugins.Add(new RegistrationFeature());
Or on the AuthFeature:
Plugins.Add(new AuthFeature(...) {
IncludeRegistrationService = true
});
If you don't want to enable ServiceStack's built-in Register Service you'll need to remove the registration where it's enabled.
If you instead want the Register Service registered at a different path, you can specify a different route with:
Plugins.Add(new RegistrationFeature {
AtRestPath = "/servicestack-register"
});
We have some custom collections such as this:
[Serializable]
public class OccupationCollection : Collection<Occupation>
{
}
We use these in objects like the following:
private OccupationCollection _occupations;
public OccupationCollection CurrentOccupations
{
get
{
if (this._occupations == null)
return new OccupationCollection();
else
return _occupations;
}
}
Now we are making a call to a WCF service, passing objects that contain these type of lists. The lists always end up being null in the service.
I'm pretty sure this has somthing to do with serialization or something like that.
What would the simplest solution that would require minimal changes to the existing objects to get this to work?
Have you hosted your service over HTTP?
If yes, can you use fiddler to check the HTTP traffic and confirm whether serialized version of the parameter is being sent across the wire? If yes, there can be a parameter mismatch in contract between server and client.
Also is the object holding OccupationCollection decorated with Serializable/DataContract attribute? If you have DataContract attribute, ensure that the properties that need to be serialized are marked with Datamember attribute.
More details out here..
http://blog.functionalfun.net/2009/09/if-your-wcf-service-is-unexpectedly.html
I'm building a Silverlight MVVM template and and am getting stuck with the WCF Service returning and Entity Object.
Here's what I did:
Using Entity Framework on the server side
Created a small test database with a couple of tables.
Created a WCF Service on the server side
I then created a small test method returning an integer.
On my client side, I added a service reference and I receive the integer result in my completed method successfully
then changed my test service method to return a "Person" object (which is an Entity from Entity Framework)
updated my service reference and then it doesn't work!
I then the return type to any basic CLR Type and it works again
I checked Fiddler and I get the following 504 error in my service response:
HTTP/1.1 504 Fiddler - Receive Failure
Content-Type: text/html; charset=UTF-8
Connection: close
Timestamp: 08:56:23.783
[Fiddler] ReadResponse() failed: The server did not return a response for this request.
After trying to figure this out, I came across WCF Trace Logging and found this error:
There was an error while trying to serialize parameter :BasicResult. The InnerException message was 'Type 'MVVMProject.Web.DataSource.Person' with data contract name 'Person:http://schemas.datacontract.org/2004/07/MVVMProject.Web.DataSource' is not expected. Consider using a DataContractResolver or add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.'. Please see InnerException for more details.
I don't understand why this is so difficult? Must I set some property on my Entity to make it serializable? If I look at the Entity Framework's designer.cs file, I see a Serializable attribute on the Entity. Surely this means I can pass this via the WCF Service??? I don't understand this error, unfortunately...
Is it even possible to use Entity Framework with WCF Service?
Any help would be greatly appreciated.
I had same problem, it seams that the DataContractSerializer has a problem with the navigation properties of ef objects.
In my test project, I'm using the northwind database. I wanted to test the CodeFirst approach with the recommended DbContext.
The provided navigation properties are virtual and they are loading on demand, but the ef just return with the first level of the entity on navigation properties is filled.
On serializing the entity object the DataContractSerializer failed because the entity object is no longer bound to the DbContext and the serialization of the navigation properties failed.
This is happen when I try to consume a NW Employee object over my wcf service.
My soultion is to copy all data in a new object with the data contract attributes!
the service call:
public IEnumerable<EmployeeWcf> GetAll()
{
IEnumerable<EmployeeWcf> result = null;
result = from e in context.Employees.OrderBy( e => e.LastName )
select new EmployeeWcf
{
EmployeeId = e.EmployeeID,
Firstname = e.FirstName,
Lastname = e.LastName
};
return result;
}
the class:
[DataContract]
public class EmployeeWcf
{
[DataMember]
public int EmployeeId { get; set; }
[DataMember]
public string Firstname { get; set; }
[DataMember]
public string Lastname { get; set; }
}
This work but it seems to me that this is not a perfect solution.
I hope this helps you.
I have a multi-tenant system where each tenant shares the same instance of the codebase, but has their own databases.
I'm using RavenDB for persistence, with a standard c# facade/BLL backend wrapped with Asp.net WebAPI, and I'm finding that at every lower level operation (deep within my business logic classes) that touch the datbase, I need to pass in an identifier so that my RavenDb client session knows which database to operate against.
When the user authenticates, I resolve the appropriate database identifer, store it in the session manager. Every call against the Web API layer passes in a session ID which resolves the database ID in the backend, which is then used to pass into every single facade/BLL call.
All my dependencies are handled via an IoC container at the WebAPI level, but i can't pass in the database ID at this phase because it can be different for every user that is logged in.
this, of course is getting tedious.
can someone give me some guidance as to what I can do to alleviate this? Maybe perhaps some sort of policy injection/AOP solution?
a rough sample of my backend code looks like..
public class WidgetService()
{
private WidgetBLL _widgetBLL;
private ISessionManager _sessionManager;
public WidgetService(WidgetBLL _widgetBLL, ISessionManager sessionManager)
{
_widgetBLL = widgetBLL;
_sessionManager = sessionManager
}
public Widget getWidget(string sessionId, string widgetId)
{
string DbId = sessionManager.ResolveDbId(sessionId)
return _widgetBLL.GetWidget(string dbId, string widgetId);
}
}
public class WidgetManager()
{
public GetWidget(string dbId, string widgetId)
{
using (IDocumentSession session = documentStore.OpenSession(dbId)
{
var widget = session.load<Widget>(widgetid);
}
return widget;
}
}
the DBID is the identifier for that particular tenant that this particular user is a member of.
You need to change how you are using the session.
Instead of opening and closing the session yourself, do that in the IoC code.
Then you pass a session that is already opened for the right db.
I've got a SQL database that uses Guid's for PK's and upon insert, it generates a NewId(). I have an EF data context setup pointing to that database with the primary keys setup with the Entity key:true, Setter:private and StoreGeneratedPattern:Identity because I want the DB to manage the keys and not have code set the PK property.
I have an OData (System.Web.Data.Services.DataService) endpoint to access this data (just like: Hanselman did.
I have another app that has a service reference to this service. Upon trying to create a new object from this reference (i.e. Product), the ProductId Primary Key is being defaulted to Guid.Empty when doing
var serviceEntities = new ServiceEntities(serviceUri); //OData endpoint
var product = new Product();
product.Name = "New Product";
serviceEntities.AddToProducts(product);
serviceEntities.SaveChanges(); // error happens here
When debugging, I look at the Product.ProductId property and it's set to Guid.Empty. When called SaveChanges, I do not want the ProductId field to be sent to the service. The response I get is:
Error processing request stream.
Property 'ProductId' is a read-only
property and cannot be updated. Please
make sure that this property is not
present in the request payload.
Is there a way to do this or what can I do to get this setup correctly and still have the DB generated the keys.
Here is the same setup as the Product example above.
What I ended up doing was adding a ChangeInterceptor. This works but its not the preferred way of doing it.
[ChangeInterceptor("Products")]
public void OnChangeApplications(Product product, UpdateOperations operations)
{
if (operations != UpdateOperations.Add) return;
if (product.ProductId == Guid.Empty)
{
product.ProductId = Guid.NewGuid();
}
}