OData Expand fails on Client Win8.1 universal app - c#

Just a quick question, is this not supported in a Win 8.1 universal class library? Or if it is, can anyone help with what i'm doing wrong.
http://jbsapplication.azurewebsites.net/Modules?$filter=Name%20eq%20'JBS%20Electronic%20forms'&$expand=Menus
When I do this from the browser or Fiddler I receive the correct response.
My code in the client view model class is as follows (using the OData Client v2 code generated objects)
var application = new UriBuilder(ServiceBaseAddress);
var context = new Models.Application(application.Uri);
var modulesQuery = context.Modules.Expand(m=>m.Menus).Where(m => m.Name == ApplicationName);
var modules = await ((DataServiceQuery<Module>) modulesQuery).ExecuteAsync();
_currentModule = modules.FirstOrDefault();
The following exception is generated on the last line
A first chance exception of type 'Microsoft.OData.Core.ODataException' occurred in Microsoft.OData.Core.DLL
Additional information: When writing a JSON response, a user model must be specified and the entity set and entity type must be passed to the ODataMessageWriter.CreateODataEntryWriter method or the ODataFeedAndEntrySerializationInfo must be set on the ODataEntry or ODataFeed that is being writen.
If I remove the Expand portion of the request, everything is fine, but I need to then perform another round trip to get the menus.
A cut down reference for the Module class:
[Key("Id")]
public class Module: BindableBase
{
public string Name
{
get { return _name; }
set { SetProperty(ref _name, value); }
}
DataServiceCollection<Menu> _menus = new DataServiceCollection<Menu>(null,TrackingMode.AutoChangeTracking);
public DataServiceCollection<Menu> Menus
{
get { return _menus; }
set
{
_menus = value;
OnPropertyChanged("Menus");
}
}
}

I have encountered the problem you describe when I forgot to add the expanding entity into the ODataModelBuilder as an EntitySet. Try this in your ASP.NET OData Web API:
builder.EntitySet<Menus>("Menus");

Models with an ID property need to be explicitly expanded by clients, and expandable models need to be registered as entitysets with the builder for the auto-generated OData client to be able to call expand.

Related

How to query OData while using alternate property names

I am using Newtonsoft to change property names on the web api json output.
public class User : IEntity
{
[Newtonsoft.Json.JsonProperty(PropertyName = "user_name"]
public string Username { get; set; }
}
I've enabled odata query so that i can add queries to the request.
[HttpGet]
[Route("api/users")]
[EnableQuery]
public IQueryable<User> GetUser()
{
return dbContext.DbSet<User>();
}
When i make a query using the alternate property name, it fails.
GET /api/users?$select=user_name
The query specified in the URI is not valid. Could not find a property named 'user_name'
The query works fine if I use the entity model name, Username (which is not visible to public). How can i fix this while still using Newtonsoft to handle deserialization?
I've not been able to achive this with the Json.Property attribute but instead this way:
// in class WepApiConfig
ODataModelBuilder builder = new ODataConventionModelBuilder();
var conf = builder.EntitySet<User>("users");
conf.EntityType.Property(f => f.Username).Name = "user_name";
the query
GET /api/users?$select=user_name
should work now (with my OData service it works fine)
My answer is based on the reply to this question but with minor corrections.

'ObjectContent`1' type failed to serialize the response body for content type 'text/xml; charset=utf-8' [duplicate]

I was working on ASP.NET MVC web API, I'm having this error:
The 'ObjectContent`1' type failed to serialize the response body for content type 'application/xml; charset=utf-8'.
My controller is:
public Employee GetEmployees()
{
Employee employees = db.Employees.First();
return employees;
}
why I m getting this error?
For me this was a problem with circular referencing.
The accepted answer did not work for me because it only changes the behaviour of the JSON formatter, but I was getting XML when I called the service from the browser.
To fix this, I switched off XML and forced only JSON to be returned.
In the Global.asax file, put the following lines at the top of your Application_Start method:
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
Now only JSON results will be returned. If you need XML results, you will need to find a different solution.
in your global.asax file, in the Application_start() method add this line:
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
I hope that helps you!
I got the same problem. And I solved it. I put the default constructor to the DTO class.
Ex:
public class User
{
public User()
{
}
}
Hope it work with you!
Put this in constructor. Hope this solve the problem:
public MyController()
{
db.Configuration.ProxyCreationEnabled = false;
}
I found two solutions to this. The first and easiest to implement is to change any IEnumerables, ICollections to a type of List. The WebAPI can serialize this objects, it however cannot serialize interface types.
public class Store
{
[StringLength(5)]
public string Zip5 { get; set; }
public virtual List<StoreReport> StoreReports { get; set; } //use a list here
}
The other option is to not use the native JSON serializer and run this override in the Register method of the WebApi Config:
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
config.Formatters.Remove(config.Formatters.XmlFormatter);
Solution is simple.
After LINQ query add .ToList() (or ToDictionary if need).
It will do eager loading than lazy loading of the data
**
this bug occur when calling from request web api/wcf/... from client side, but as side effect, you will need to include depending relations by include keyword.
**
public CustomerPortalContext()
: base("Name=CustomerPortalContext")
{
base.Configuration.ProxyCreationEnabled = false;
}
If you are working with EF, besides adding the code below on Global.asax
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
Dont`t forget to import
using System.Data.Entity;
Then you can return your own EF Models
please check the web api documentation for this problem, Handling Circular Object References
Regards
If you use web api with Entity Framework, a solution can be
Failed to serialize the response in Web API with Json
Basically, you need to create a model corresponding to each EF model, this removes dependencies between classes and allow easy serialization.
Code: (taken from the referenced link)
Create a UserModel
public class UserModel
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
Change my method GetAll()
public IEnumerable<UserModel> GetAll()
{
using (Database db = new Database ())
{
List<UserModel> listOfUsers = new List<UserModel>();
UserModel userModel = new UserModel();
foreach(var user in db.Users)
{
userModel.FirstName = user.FirstName;
userModel.LastName = user.LastName;
listOfUsers.Add(userModel);
}
IEnumerable<UserModel> users = listOfUsers;
return users;
}
}
Default Entity 6 use XML to apis, in your project, find the file "Global.asax" File and add this line:
GlobalConfiguration.Configuration.Formatters.Remove(GlobalConfiguration.Configuration.Formatters.XmlFormatter);
This line remove the XML Formatter.
but if you found this problem with other entities/classes, you have to create a new DTO for each class, and if you have a lot of them, you can find a problem, also I think that create a DTO only for solving this problem is no the best way...
Did you try this?
var json = GlobalConfiguration.Configuration.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling =
Newtonsoft.Json.PreserveReferencesHandling.All;
Regards
hmmm, Following may help.
I was getting the same exception, and in my case I was passing the actual poco entity created for entity code first. Since, it contains relation with other entities, I just created the viewmapper/dto entity on top of it to return.
It works fine now.
Poco Entity:
public class Tag
{
public int Id{get;set;}
public string Title{get;set;}
public IList<Location> Locations{get;set;}
}
ViewMapper/Dto
public class TagResultsViewMapper
{
public int Id{get;set;}
public string Title{get;set;}
//just remove the following relationship
//public IList<Location> Locations{get;set;}
}
Your question is quite similar to mine. You must not return data from database directly. For this, you must create Model and associate data you want show.
In my example, There are data about User that Json couldn't serialize, I had create a userModel and, in my API, I return userModel instead User from database.
The logic of convert or associate data between User and UserModel must be in API.
Failed to serialize the response in Web API with Json
This was the specific error I was getting back from my odata Web API call:
The 'ObjectContent`1' type failed to serialize the response
body for content type 'application/json; odata.metadata=minimal'.
I finally figured out that my dbContext class had a poorly formatted table name being assigned in onModelCreating.. so the SqlClient was dying looking for a table that didn't exist in my db!!

MVC requests interfering with calls to AutoQuery-enabled ServiceStack API

I have an MVC solution that contains a ServiceStack API alongside an MVC UI that makes calls to the API services. Some of those services are AutoQuery GET endpoints, and I'm running into a problem where the ServiceStack service picks up posted form values or unrelated querystring values and throws argument errors when I call the services.
I've tried a number of ways of calling the services:
using (var fooSvc = new HostContext.ResolveService<FooService>(HttpContext))
{
var foos = (QueryResponse<Foo>)fooSvc.Get(new Foos());
// Do stuff here
}
No dice. Posted form values screw it up and I get a System.FormatException saying Input string was not in a correct format on this line in my service:
var q = AutoQuery.CreateQuery(request, Request.GetRequestParams());
So I tried:
var foos = (QueryResponse<Foo>)HostContext.ServiceController.Execute(new Foos());
In this case, I get a System.NotImplementedException saying it couldn't find a Post(Foos) or Any(Foos) (the call in question is a GET).
I'm sure I'm missing something simply. Mythz, got another rescue for me?
EDIT: I hand-typed that code...the initial block had AutoQuery when I meant QueryResponse...
EDIT 2: Here is the general structure of my AutoQuery services. These are all GET on the service because those endpoints also need to support POST to create a resource. For example, I might have the URI at http:/service.com/api/users and want to be able to GET with AutoQuery or POST to create a new user.
[Authenticate]
public class UsersService : Service
{
public IAutoQuery AutoQuery { get; set; }
public object Get(Users request)
{
var q = AutoQuery.CreateQuery(request, Request.GetRequestParams());
// Here I add some other conditions to the query object to filter results based on the user's role, etc.
return AutoQuery.Execute(request, q);
}
[RequiredRole("Admin")]
public object Post(CreateUser request)
{
var user = request.ConvertTo<User>();
Db.Save(user);
return user;
}
}
When you call a Service you're just calling a method directly on the target instance. What does the method returns? It's unlikely it's AutoQuery<Foo> since it's not a valid type in ServiceStack:
var foos = (AutoQuery<Foo>)fooSvc.Get(new Foos());
If you're getting a FormatException check the QueryString param that's causing it. Something in the Request is incompatible with the AutoQuery field.
Did you create the method yourself because AutoQuery generates services using the Any method so that it can be called by any verb. If it's a Custom Service change it to Any.
Otherwise you can Execute the method with the current HTTP Request with:
var foos = (QueryResponse<Foo>)HostContext.ServiceController
.Execute(new Foos(), HttpContext.ToRequest());
You can also override what Verb that gets executed with:
var req = HttpContext.ToRequest();
req.Items[Keywords.InvokeVerb] = "GET";
var foos = (QueryResponse<Foo>)HostContext.ServiceController
.Execute(new Foos(), req);
Custom AutoQuery Implementation
If you have conflicted fields trying to forward the request I would just change them to use fields that don't conflict with AutoQuery request. But if you still want to continue using the conflicted fields you can create a
Custom AutoQuery Implementation that removes it from the AutoQuery services instead, e.g:
public class UsersService : Service
{
public IAutoQuery AutoQuery { get; set; }
public object Any(Users request)
{
Dictionary<string, string> args = Request.GetRequestParams();
args.Remove("OrganizationId");// E.g remove conflicted fields
var q = AutoQuery.CreateQuery(request, args);
return AutoQuery.Execute(request, q);
}
}

WCF RIA Entity With Include Not Materializing in Silverlight Client

I'm having an impossible time figuring out why my returned child entities are null for a particular query:
The item has a child property with a 0 or 1 multiplicity called MetaDataItem.
var res = ObjectContext.Contents.Include("MetaDataItem");
This works. I can confirm that res contains 1 instance of MetaDataItem.
On the client:
var loadOperation = _cContext.Load(_cContext.GetItemQuery(itemId.Value)....
Content item = _cContext.Contents.First(c => c.ContentId == itemId.Value);
Here item.MetaDataItem is null instead of containing the one instance.
Here is the metadata class (note that I DO have an include attribute):
[MetadataTypeAttribute(typeof(Content.ContentMetadata))]
public partial class Content
{
internal sealed class ContentMetadata
{
[Include]
public MetaDataItem MetaDataItem { get; set; }
}
}
So, why isn't my object populating? What else should I be looking at? Unfortunately, this appears to be where the magic of RIA services occurs, so I can't even really get a handle on attempting to debug the issue. Any help would be most appreciated.

Azure Table Storage Query from IronPython

I've got a project that uses IronPython as a scripting engine to perform various tasks. One of those tasks needs to do some table lookup's on the Azure Table storage, however the table layouts are different, and will change often, so I need the model classes to be defined in Python.
Here is the problem I'm running into, whenever I run a query it complains that a base class from my project is not supported by the client library.
Unhandled Exception: System.InvalidOperationException: The type 'IronPython.NewTypes.IPTest.BaseModelClass_1$1' is not supported by the client library.
Python Code:
import clr
import System
clr.AddReference("System.Core")
clr.ImportExtensions(System.Linq)
class MyTable(AzureTableService.BaseModelClass):
def __new__(self, partitionKey, rowKey):
self.PartitionKey = partitionKey
self.RowKey = rowKey
return super.__new__(self)
MyTableDetails = "";
#I can manually create an entity, and it recognizes the base class, but not when I try to return from a query
#working = MyTable("10", "10040")
#print working.PartitionKey
y = AzureTableService.GetAzureTableQuery[MyTable]("MyTable")
z = y.Where(lambda c: c.PartitionKey == "10" and c.RowKey == "10040")
print(z.Single())
C# Code:
public class AzureTableService {
private CloudStorageAccount mStorageAccount;
public AzureTableService() {
CloudStorageAccount.SetConfigurationSettingPublisher((configName, configSetter) => {
var connectionString = ConfigurationManager.AppSettings[configName];
configSetter(connectionString);
});
mStorageAccount = CloudStorageAccount.FromConfigurationSetting("DataConnectionString");
}
private TableServiceContext AzureTableServiceContext {
get {
var context = mStorageAccount.CreateCloudTableClient().GetDataServiceContext();
context.IgnoreResourceNotFoundException = true;
return context;
}
}
public IQueryable<T> GetAzureTableQuery<T>(string TableName) {
return AzureTableServiceContext.CreateQuery<T>(TableName);
}
public class BaseModelClass : TableServiceEntity {
public BaseModelClass(string partitionKey, string rowKey) : base(partitionKey, rowKey) { }
public BaseModelClass() : base(Guid.NewGuid().ToString(), String.Empty) { }
}
}
Is there anything obvious that I'm missing? In my commented code, it seems to recognize my base class properties when I manually create it, however it does not when I try returning it from a query.
The problem you are facing is related to System.Data.Services.Client which is used by Microsoft.WindowsAzure.StorageClient.
It restricts which types can be used in the data services client. This seems to prevent any implementation of IDynamicMetaObjectProvider (basically any dynamic object and therefore any object of an IronPython class) to be used during deserialization of your query result.
I tested the scenario using Azure Storage 1.7.0.0, 2.0.6.0 and 2.1.0.0-rc confirming your results.
You could always have a look at the source and see if another deserializer for AtomPub could be used.

Categories