How to Bind Delta<T> in web api2 odata 3 controller - json

So I'm building out new dataservices, and figured I'd use web api odata. So I added a controller to my project using the scaffolding to generate actions for my entity framework model classes. Everything worked great until I tried the generated put or patch methods. The guid Id from the url binds, but no matter what I try I can't bind the Delta variable. It's always null. After a day of googling i can't find anything newer than about 2011 and those solutions don't work. Does anybody know how to get these to bind?
method signature
[AcceptVerbs("PATCH", "MERGE")]
public async Task<IHttpActionResult> Patch([FromODataUri] Guid key, Delta<AttachmentProposal> patch)
my web api config
public static void Register(HttpConfiguration config)
{
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<AttachmentProposal>("AttachmentProposals");
builder.EntitySet<AttachmentAction>("AttachmentActions");
config.Routes.MapODataServiceRoute("odata", "odata", builder.GetEdmModel());
config.MapHttpAttributeRoutes();
my model is something like
public Guid Id { get; set; }
public string name { get; set; }
public DateTime createDate { get;set; }
public virtual HashSet<AttachmentActions> {get; set;}
if it makes any difference i always try to send up json. typical request body's i've tried are like
{ name: 'some name' }
or
{ every: 'value', single: 'value', property: 'value', on: 'value', my: 'value' model: 'value' }

figured out the answer by making a console app with a reference to the service and watching the traffic. If anybody else is having this problem try adding odata.type: "what ever the type of your object is" to the json in the request

Related

how json parameter pass in asp.net web api action method

My asp.net web api is an standalone application,face problem to pass json sa a parameter.My api method is bellow
[Route("api/UniqueUser/{userInfo}")]
public HttpResponseMessage GetUniqueUserByEmail(string userInfo)
{
}
In above parameter userInfo is a josn like bellow
{"UserInfo":[{"Id":1,"UserName":"Jxj Bdn","Email":"a#a.com"}]}
When I put this in my browser url show me bellow error
JSON data should go in the body of the request for it to be deserialized, not in the query string/browser URL.
Also, 'string userInfo' will not work as you expect. You can define a class that represents the parameters of your JSON object and it will work correctly.
This would work, for example:
public class UserInfo
{
public int Id { get; set;}
public string UserName { get; set;}
public string Email { get; set;}
}
and change this line:
public HttpResponseMessage GetUniqueUserByEmail(UserInfo userInfo)
Edit:
If it's a url that someone needs to pass in you use routing:
https://site/api/UniqueUser/1/Jxj Bdn/a#a.com
And in your controller:
[Route("api/UniqueUser/{id}/{userName}/{email}")]
public HttpResponseMessage GetUniqueUserByEmail(int id, string userName, string email)
Have a look here to see how to do this with traditional query string parameters too:
http://www.asp.net/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api
I would strongly suggest using the first method though, it gives you a strongly type object and is a lot easier to deal with if details change, and you get the benefit of the build in model validation.
Can you not make a simple HTML form for your clients to use?

FunctionIndexingException Can't bind Table to type on Azure WebJob Load

I think this is a case of out of date documentation but I'm unable to find a more recent example.
Using the following code generates an exception on the initialisation of the webjob and it gets stuck in a "pending restart" loop.
public static void GenerateExcelFile(
[QueueTrigger("excel")] JobFile message,
Guid Id,
[Table("JobFile")] IDictionary<Tuple<string, string>, object> table,
{
//More Code
}
Replacing "object" with "JobFile" produces the same error. It's quite a length stacktrace so I've only posted the top of it here. Using ILSpy it looks like this shouldn't work so I'm not sure if this feature has been removed since the tutorial was written.
[09/13/2014 11:07:53 > be5c40: ERR ] Unhandled Exception:
Microsoft.Azure.WebJobs.Host.Indexers.FunctionIndexingException:
Error indexing method 'GenerateExcelFile' --->
System.InvalidOperationException: Can't bind Table to type
'System.Collections.Generic.IDictionary`2[System.Tuple`2[System.String,System.String],System.Object]'.
[09/13/2014 11:07:53 > be5c40: ERR ] at Microsoft.Azure.WebJobs.Host.Tables.TableAttributeBindingProvider.TryCreateAsync(BindingProviderContext context)
[09/13/2014 11:07:53 > be5c40: ERR ] at Microsoft.Azure.WebJobs.Host.Bindings.CompositeBindingProvider.<TryCreateAsync>d__0.MoveNext()
I've tried this on 0.5 beta and 0.6 beta of the SDK.
The documentation that you are pointing to is out of date. IDictionary binding was removed for Tables. You can use ICollector binding for Inserting or replacing, TableEntity/ IQueryable binding for reading and CloudTable binding for modifying an entity.
The following samples demonstrate Tables usage
https://github.com/Azure/azure-webjobs-sdk-samples/tree/master/BasicSamples/TableOperations
https://github.com/bradygaster/siteMonitR
As I had to search a while to find how to use the ICollector binding, I thought I'd share.
It looks like it belongs to a new release in Microsoft.Azure.WebJobs so make sure you are using the version 0.6.0-beta.
In your case it would be something like
public static void GenerateExcelFile(
[QueueTrigger("excel")] JobFile message,
Guid Id,
[Table("JobFile")] ICollector<JobFile> tableBinding
{
//More Code
}
public class JobFile
{
public string PartitionKey { get; set; }
public string RowKey { get; set; }
public string Name { get; set; }
}
P.S. I have not tested this! :P
See the link for details
http://blogs.msdn.com/b/webdev/archive/2014/09/12/announcing-the-0-6-0-beta-preview-of-microsoft-azure-webjobs-sdk.aspx

Automapper map to existing object stack overflow exception

i'm coding a restService who can update some datas in a database via Nhibernate.
The service receive DTO objects from a client.
I'm using Automapper to map my Dto to NhibernateObject.
The problem is my DTO class reference itself. here an example :
public class UserDto
{
public String Name{get;set;}
public string Lastname{get;set;}
public UserDto UserOwner{get;set;}
}
here's my BusinessClass
public class User
{
public String Name{get;set;}
public string Lastname{get;set;}
public String Adress{get;set;}
public User UserOwner{get;set;}
}
Sometimes User object and UserOwner properties references the same object.
So when i do that
User usr = Automapper.Mapper.Map<UserDto,User>(myUserDtoObject); // this works fine
but when i do
Automapper.Mapper.Map(myUserdtoObject,MyUserNhibernateObject); // i've got a stackoverflowexception
I can use the first option but if i do that, when my new UserEntity returned by Map function is created the value of "Adress" properties is not set (UserDto does not contains it).
You need to use MaxDepth - AutoMapper doesn't know how far to go down your rabbit hole.
ForMember(dest => dest.UserOwner, opt => opt.MaxDepth(1))
This is important for NHibernate, which uses proxy objects to load indefinitely. The other option is to ignore the UserOwner member, but that's likely not your intent here.

Setting lifetime manager for registrations done using UnityConfiguration scanner

I have a ASP.NET MVC4 application and am using Unity for IOC. I am using Unity.MVC4 and UnityConfiguration Nuget packages to help with the registration.
I need to automatically register a load of interfaces and their related types to the Unity container. To do this I created a dummy interface; IDependencyInjectionScanner that all my real interfaces inherit from. Below is the code showing that.
public interface IDependencyInjectionScanner
{
}
public interface IChair : IDependencyInjectionScanner
{
NumberOfLegs { get; set; }
}
public class Chair : IChair
{
public NumberOfLegs { get; set; }
}
public interface ITable : IDependencyInjectionScanner
{
NumberOfChairs { get; set; }
}
public class Table : ITable
{
public NumberOfChairs { get; set; }
}
I then used UnityConfiguration to bind the registrations using the scanner. I have get the interfaces being correctly resolved in the controller. Below is the code that shows how I did the binding.
Scan(scan =>
{
scan.AssembliesInDirectory(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin"));
scan.With<FirstInterfaceConvention>();
scan.Include(x => (x.GetInterface(typeof(IDependencyInjectionScanner).Name) != null));
scan.ForRegistries();
});
The problem is that I want to register all the types found by the scanner using the hierarchical lifetime manager but can figure out how to do this. The GitHub page for UnityConfiguration https://github.com/thedersen/UnityConfiguration states that this could be achieved by the code below:
Configure<IChair>().AsHierarchicalControlled();
However I if I have to do that for each of the interfaces bound by the scanner then the scanner is of no use as I may as well do:
Register<IChair, Chair>().AsHierarchicalControlled();
Can someone assist me with finding a solution to this please.
Here's an answer to your question using UnityConfiguration. You can create a custom convention to configure the lifetime. Just be careful because it looks like the calls within the Scan() method are order dependent.
public class HierarchicalLifetimeConvention : IAssemblyScannerConvention
{
public void Process(Type type, IUnityRegistry registry)
{
registry.Configure(type).AsHierarchicalControlled();
}
}
and then add that to your Scan() call...
Scan(scan =>
{
scan.AssembliesInDirectory(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin"));
scan.With<FirstInterfaceConvention>();
scan.With<HierarchicalLifetimeConvention>(); //<-- New convention
scan.Include(x => (x.GetInterface(typeof(IDependencyInjectionScanner).Name) != null));
scan.ForRegistries();
});
As suggested by #TylerOhlsen I used the built-in Registration by Convention feature of Unity 3.0. I have got it to add the registration mappings and they are using the hierarchical lifetime manager. below is the code for that
container.RegisterTypes(
AllClasses.FromLoadedAssemblies().Where(
t => t.GetInterface(typeof(IDependencyInjectionScanner).Name) != null),
WithMappings.FromMatchingInterface,
WithName.Default,
WithLifetime.Hierarchical);
There is one thing that is disturbing me; when I look at the registrations I have 4 (based on the example code above). 2 type mappings for the Chair type and 2 type mappings for the Table type.
Can anyone shed any light on why this is, as I was only expecting two mappings.

Handling references in breezejs

We are developing a single page app using ASP.NET MVC4 with Web Api + Ko + Breeze using EF Code First.
Our (simplified) data model looks like this
class Product {
public String Name { get; set; }
public ICollection<ImageCollection> ImageSets { get; set;}
public Image DefaultImage { get; set; }
}
class ImageCollection {
public ICollection<Image> Images { get; set; }
}
class Image {
public String ImageUrl { get; set; }
}
DefaultImage is a navigation property (with foreign key) and is one of the images in the ImageSets.
We are exposing a Web API method of Products() and with default Breeze configuration. JSON serialized output on the wire has references for objects (i.e., PreserveReferencesHandling.Object) so when I want to bind the ImageUrl ko is unable to resolve the value.
Html looks like this
<img data-bind="attr: { src: DefaultImage().ImageUrl, title: Name}" />
When I switch the serializer to do PreserveReferencesHandling.None, the binding works.
Question: how do I make the default config to work? or if I switch to PreserveReferencesHandling.None for Breeze what are the gotchas/downsides?
Thanks!
In general, you do NOT want to switch PreserveReferencesHandling to None because you will lose the ability to serialize circular references and your payloads will get much larger.
I don't actually understand why your binding would begin to work after setting this. The first step to understanding this is probably to check that the ko objects actually contain the correct data after your query.
Remember that breeze navigation properties are lazy-loaded, so you may not have loaded them with your initial query. Take a look at the "EntityAspect.loadNavigationProperty" method.