Breeze Expand Property - breeze

I'm getting the following exception when I try to use Expand on any navigation property.
$id: "1",
$type: "System.Web.Http.HttpError, System.Web.Http",
Message: "An error has occurred.",
ExceptionMessage: "'object' does not contain a definition for 'Include'",
ExceptionType: "Microsoft.CSharp.RuntimeBinder.RuntimeBinderException",
StackTrace: " at System.Web.Http.ApiController.<InvokeActionWithExceptionFilters>d__b.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
If I put the include inside the Breeze controller everything works fine. If I do not and just use expand on the client, I get that error. Any help is greatly appreciated!
This is the URL used to try to retrieve the data
/breeze/maxsys/CallOutcomes?$expand=CallOutcomeAction
Here is the model
[Table("CallOutcomes")]
public class CallOutcome {
[Key]
public int Id { get; set; }
[Required]
public string Description { get; set; }
public bool IsInternal { get; set; }
public int CallOutcomeActionId { get; set; }
[ForeignKey("CallOutcomeActionId")]
[InverseProperty("CallOutcomes")]
public CallOutcomeAction CallOutcomeAction { get; set; }
public ICollection<CallOutcomeHistory> CallOutcomeHistories { get; set; }
}
The Controller looks as follows ( I removed some of the other get methods )
[BreezeController]
[Authorize]
[RequireHttps]
public class MaxsysController : ApiController
protected IMaxsysBreezeRepository Repository { get; set; }
public MaxsysController(IMaxsysBreezeRepository repository)
{
Repository = repository;
}
[HttpGet]
public IQueryable<CallOutcome> CallOutcomes()
{
return Repository.CallOutcomes;
}
}
The error is coming from this method in the BreezeQueryableAttribute.cs
public virtual IQueryable ApplyExpand(IQueryable queryable, string expandsQueryString, HttpRequestMessage request)
{
(from s in expandsQueryString.Split(new char[] { ',' }) select s.Trim()).ToList<string>().ForEach(delegate (string expand) {
queryable = (IQueryable) ((dynamic) queryable).Include(expand.Replace('/', '.'));
});
return queryable;
}
The parameter values are
queryable = {SELECT
[Extent1].[Id] AS [Id],
N'b1d28373-98a2-4a88-9733-7872acd28bd2' AS [C1],
[Extent1].[Description] AS [Description],
[Extent1].[IsInternal] AS [IsInternal],
[Extent1].[CallOutcomeActionId] AS [CallOutcomeActionId],
N'CallOutcomeAction' AS [C2],
N'b1d28373-98a2-4a88-9733-7872acd28bd2' AS [C3],
[Extent2].[Id] AS [Id1],
[Extent2].[Description] AS [Description1]
FROM [dbo].[CallOutcomes] AS [Extent1]
INNER JOIN [dbo].[CallOutcomeActions] AS [Extent2] ON [Extent1].[CallOutcomeActionId] = [Extent2].[Id]}
expandsQueryString = "CallOutcomeAction"
HttpRequestMessage ={Method: GET, RequestUri: 'http://127.0.0.1:82/breeze/maxsys/CallOutcomes?$expand=CallOutcomeAction', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
Connection: keep-alive
Accept: text/html
Accept: application/xhtml+xml
Accept: application/xml; q=0.9
Accept: */*; q=0.8
Accept-Encoding: gzip
Accept-Encoding: deflate
Accept-Encoding: sdch
Accept-Language: en-US
Accept-Language: en; q=0.8
Host: 127.0.0.1:81
User-Agent: Mozilla/5.0
User-Agent: (Windows NT 6.1; WOW64)
User-Agent: AppleWebKit/537.36
User-Agent: (KHTML, like Gecko)
User-Agent: Chrome/27.0.1453.94
User-Agent: Safari/537.36
}}

The problem was solved. There was some pre-release software that was installed to test the CORS support in Web API. Once I removed those changes, expand ended up working. Thanks for the help.

I've also experienced this issue and tracked it down to the application of a BreezeQueryable annotation.
Exception:
ExceptionMessage: "'System.Linq.EnumerableQuery' does not contain a definition for 'Include'",
Endpoint:
[HttpGet]
[BreezeQueryable(PageSize = 1000)] //1000 row limit
public IQueryable<Postcode> Postcodes()
{
return _db.Context.Postcodes;
}
Calling script:
var qry = breeze.EntityQuery
.from('Postcodes')
.where('Name', breeze.FilterQueryOp.StartsWith, searchTerm)
.orderBy('PostcodeId')
.expand('State')
.take(25);
To fix, simply remove the [BreezeQueryable] annotation:
[HttpGet]
//[BreezeQueryable(PageSize = 1000)] //1000 row limit
public IQueryable<Postcode> Postcodes()
{
return _db.Context.Postcodes;
}
Edit - obviously this is not 'fixed'.
Looks like an issue in the Breeze WebApi assembly.

Preliminaries
Are you sure that you can query the MaxsysController.CallOutcomes endpoint without expand?
Are you sure that Repository.CallOutcomes is returning the proper EF concrete type?
You tried something like the following inside the controller's CallOutcomes():
[HttpGet]
public IQueryable CallOutcomes()
{
var foo = Repository.CallOutcomes // break here; remove later.
.Include('CallOutcomeAction').ToList();
return Repository.CallOutcomes;
}
At the break you've confirmed that the method succeeded w/o failure and that foo has values.
Next
Once you've confirmed those points, I think my next move would be to remove the [RequireHTTPS]. I haven't tried that lately and it's not easy for me to set that up quickly on my end. There is a possibility that we screwed up and there is interference between the [RequireHTTPS] attribute and the [BreezeController] attribute.
Also try putting [RequireHTTPS] before [BreezeController]. It shouldn't matter; I'm just guessing now. I'll await your report and then we can move on from there.

I started getting this problem when I tried to limit the page size returned by my actions using the BreezeQueryableAttribute on the action. Without the attribute, the action returns the expected data. Like gopheruk, I believe there is currently a bug in Breeze.
Library versions:
I'm not using any pre-release libraries, and everything is up to date in nuget.
Breeze 1.4.2 (Breeze.WebApi 1.4.0.0, Breeze.WebApi.EF 1.4.0.0)
Microsoft.AspNet.WebApi 4.0.30506
EntityFramework 5.0.0
The request:
http://localhost/breeze/Data/MyObjects?$filter=Id%20eq%201&$expand=User
On the server:
[BreezeController]
public class DataController : ApiController
{
//...
[HttpGet]
[BreezeQueryable(PageSize = 30)] //if this line is commented out, everything works
public IQueryable<MyObject> MyObjects()
{
return _myObjectRepository.All(User.Identity);
}
}
The response:
{"$id":"1","$type":"System.Web.Http.HttpError, System.Web.Http","Message":"An error has occurred.","ExceptionMessage":"'System.Linq.EnumerableQuery<MyObject>' does not contain a definition for 'Include'","ExceptionType":"Microsoft.CSharp.RuntimeBinder.RuntimeBinderException","StackTrace":" at CallSite.Target(Closure , CallSite , Object , String )\r\n at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)\r\n at Breeze.WebApi.QueryHelper.<>c__DisplayClass14.<ApplyExpand>b__11(String expand)\r\n at System.Collections.Generic.List`1.ForEach(Action`1 action)\r\n at Breeze.WebApi.QueryHelper.ApplyExpand(IQueryable queryable, String expandsQueryString)\r\n at Breeze.WebApi.QueryHelper.ApplySelectAndExpand(IQueryable queryable, NameValueCollection map)\r\n at Breeze.WebApi.BreezeQueryableAttribute.OnActionExecuted(HttpActionExecutedContext actionExecutedContext)\r\n at System.Web.Http.Filters.ActionFilterAttribute.CallOnActionExecuted(HttpActionContext actionContext, HttpResponseMessage response, Exception exception)\r\n at System.Web.Http.Filters.ActionFilterAttribute.<>c__DisplayClass2.<System.Web.Http.Filters.IActionFilter.ExecuteActionFilterAsync>b__0(HttpResponseMessage response)\r\n at System.Threading.Tasks.TaskHelpersExtensions.<>c__DisplayClass41`2.<Then>b__40(Task`1 t)\r\n at System.Threading.Tasks.TaskHelpersExtensions.ThenImpl[TTask,TOuterResult](TTask task, Func`2 continuation, CancellationToken cancellationToken, Boolean runSynchronously)"}

Related

How do I set "Parameter content type" using Swashbuckle?

My swagger ui shows "Parameter content type" with various entries: "application/json-patch+json", "text/json", "application/json", and "application/*+json".
I only want "application/json".
There's a similar unsolved issue on the repo, which uses this visual (older ui, but same idea):
Is there some way to set this?
Swashbuckle.AspNetCore version 4.0.1
Swashbuckle.AspNetCore.Filters version 4.5.5
Use the [Produces] and [Consumes] attributes. Swashbuckle (and others, like NSwag) will convert them into the appropriate Swagger documentation.
The [Consumes] attribute's constructor's first parameter is String contentType:
public ConsumesAttribute ( string contentType, params string[] otherContentTypes );
https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.consumesattribute.-ctor?view=aspnetcore-2.2#Microsoft_AspNetCore_Mvc_ConsumesAttribute__ctor_System_String_System_String___
Like so:
[ApiController]
public class MyController : ControllBase
{
[HttpPost( "/foo/bar" )]
[Consumes( MediaTypeNames.Application.Json )] // "application/json"
[Produces( typeof(MyResponseDto) ) ]
public async Task<IActionResult> Post( [FromBody] MyRequestDto dto )
{
//
}
}

Web API 2 Odata V4 PATCH return 404

I have this controller :
using System.Web.Http;
using System.Web.OData;
public class InvRecipientAutoInvoiceController : ODataController
{
// GET: odata/InvRecipientAutoInvoice
[EnableQuery]
public IQueryable<Inv_RecipientAutoInvoice> GetInvRecipientAutoInvoice()
{
return db.Inv_RecipientAutoInvoice.Where(a=>a.CompanyNumber == CompanyNumber);
}
[AcceptVerbs("PATCH", "MERGE")]
public IHttpActionResult Patch([FromODataUri] int RecipientNumber , [FromODataUri] int RecipientType, Delta<Inv_RecipientAutoInvoice> patch)
{
// XXXX Some Update Code
}
}
The GET works and I get result and can even sort them.
but when I do a PATCH request, I get 404 error ,
the PATCH request :
Request URL: http://localhost:61240/odata/InvRecipientAutoInvoice(RecipientNumber%3D443%2CRecipientType%3D400)
Request Method: PATCH
Response Body :
{ "error":{
"code":"","message":"No HTTP resource was found that matches the request URI
'http://localhost:61240/odata/InvRecipientAutoInvoice(RecipientNumber=443,RecipientType=400)'.","innererror":{
"message":"No action was found on the controller 'InvRecipientAutoInvoice' that matches the
request.","type":"","stacktrace":""
} } }
Request body :
{"InvoiceLine1Description":"32132"}
I am using it in an ASP.net web project (not MVC),
the register is :
config.MapODataServiceRoute(
routeName: "ODataRoute",
routePrefix: "odata",
model: builder.GetEdmModel());
what am i missing?
#yaniv
It seems that you want to use the built-in routing conventions to patch the entity with composite keys. However, the built-in routing conventions doesn't support the composite keys.
You can either custom your own routing conventions ( see here ) or just use the attribute routing.
Attribute routing is simple and easy to use. You only need to put an ODataRouteAttribute on your Patch action, then it should work.
[AcceptVerbs("PATCH", "MERGE")]
[ODateRoute("InvRecipientAutoInvoice(RecipientNumber={RecipientNumber},RecipientType={RecipientType})"]
public IHttpActionResult Patch([FromODataUri] int RecipientNumber , [FromODataUri] int RecipientType, Delta<Inv_RecipientAutoInvoice> patch)
{
// XXXX Some Update Code
}
Thanks.
When you make the call, what's the requested contenttype? Is it application/json-patch+json ? ( instead of application/json )

Web api post - passed string value is null at server side

My project is brand new Asp.net 2015 MVC6 beta 8 web application.
I get value as null when I call Web api with post type from C# code.
My server side code:
// POST api/values
[HttpPost]
public void Post([FromBody]string value)
{
if( null != value )
do something;
}
My clientside is:
StringContent cstrJson = new StringContent("{ mesage: hello}"
, System.Text.Encoding.Unicode, "application/x-www-form-urlencoded");
var result = await client1.PostAsync("http://localhost:68888/api/myApi/", cstrJson);
I tried all different combinations of encoding and media, but no improvements.
It's null because the body couldn't be parsed as a string. The content type is application/x-www-form-urlencoded instead of text/plain.
You may want to rethink using a string anyway if your client is sending json, you should accept application/json on the server and let the framework parse it for you.
[HttpPost]
public void Post(MyObject value)
{
var msg = value.Message;
}
public class MyObject
{
public string Message { get; set; }
}
Client Side:
var cstrJson = new StringContent("{'Message' : 'hello'}", System.Text.Encoding.Unicode, "application/json");
var result = await client1.PostAsync("http://localhost:68888/api/myApi/", cstrJson);

How to test post method in fiddler?

I am trying to call the following POST method [http://localhost:45361/api/test], in Fiddler, with [Request Header] tab, having [User-Agent: Fiddler, Content-Type: application/json; charset=utf-8] and in the [Request Body],I am passing the following request {"tag":"5667"} . However, its outputting an error -- > Object reference not set to an instance of an object.
[HttpPost]
public HttpResponseMessage post([FromBody] Query query)
{
IQueryable<data_qy> Data = null;
if (!string.IsNullOrEmpty(query.tag)) //--> line causing the ERROR
{
var ids = query.tag.Split(',');
var dataMatchingTags = db.data_qy.Where(c => ids.Contains(c.TAG));
if (Data == null)
Data = dataMatchingTags;
else
Data = Data.Union(dataMatchingTags);
}
if (!string.IsNullOrEmpty(query.name))
{
var ids = query.name.Split(',');
var dataMatchingTags = db.data_qy.Where(c => ids.Any(id => c.Name.Contains(id)));
if (Data == null)
Data = dataMatchingTags;
else
Data = Data.Union(dataMatchingTags);
}
if (Data == null)
Data = db.data_qy;
if (query.endDate != null)
{
Data = Data.Where(c => c.UploadDate <= query.endDate);
}
if (query.startDate != null)
{
Data = Data.Where(c => c.UploadDate >= query.startDate);
}
var data = Data.ToList();
if (!data.Any())
{
var message = string.Format("No data found");
return Request.CreateErrorResponse(HttpStatusCode.NotFound, message);
}
return Request.CreateResponse(HttpStatusCode.OK, data);
}
Edit:
Query Class:
public class Query
{
public string name { get; set; }
public string tag{ get; set; }
public Nullable<DateTime> startDate { get; set; }
public Nullable<DateTime> endDate{ get; set; }
}
I am little unclear, if this is the correct approach to testing post method or if I need add further code in my above controller. Please advice. Many thanks.
One of the most important things that you need in fiddler is the Content-Type header specification in the post request. Web API has this concept of content negotiation based on request headers and the registration of the content negotiators in the pipeline. Please, see here for more details.
In your case:
Content-Type: application/json; charset=utf-8
Here is the whole request from the fiddler composer:
User-Agent: Fiddler
Host: localhost:26572
Content-Length: 16
Content-Type: application/json; charset=utf-8
and here is the request body:
{"name":"hello"}
With this post request, you should be able to proceed.

How can I send a JSON string to an ASP Web API method with a POST?

I am sending a JSON message to a MVC method. When I debug it calls the method but
it seems like the data is not there. In other words answerDetail is null.
Can someone give me some advice on what I may be doing wrong. Here's what I have:
The following MVC Controller method:
[HttpPost]
[ActionName("CheckAnswers")]
public void CheckAnswers(AnswerDetail2 answerDetail)
{
var a = answerDetail;
}
This message is sent to the method:
POST http://127.0.0.1:81/api/Question/CheckAnswers HTTP/1.1
Host: 127.0.0.1:81
Connection: keep-alive
Content-Length: 722
Accept: application/json, text/plain, */*
Origin: http://127.0.0.1:81
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.66 Safari/537.36
Content-Type: application/json;charset=UTF-8
Referer: http://127.0.0.1:81/questions/5
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Cookie: __RequestVerificationToken=4puG_e0..B181
[{"answerId":5,"text":"<p>x</p>","correct":null,"response":false},
{"answerId":6,"text":"<p>xx</p>","correct":null,"response":false},
{"answerId":7,"text":"<p>xxx</p>","correct":null,"response":false}]
Here's my AnswerDetail2 class:
public class AnswerDetail2
{
public int answerId { get; set; }
public string text { get; set; }
public Nullable<bool> correct { get; set; }
public Nullable<bool> response { get; set; }
}
Update: Please note I changed the header. It's actually going to a Web API method so I think it's okay to post an object. The problem is it's not getting accepted.
U r passing a json string but the paramater u have taken is a class. This wont work.
Try this instead.
[HttpPost]
[ActionName("CheckAnswers")]
public void CheckAnswers(string answerDetail)
{
var a = answerDetail;
//deserialize ur json string..
}
and u r 'POST'ing the data. R u making an ajax POST method?
try this
var myObject =
{
answerId: 123,
text: "my Text",
correct: true,
response: false,
};
var answerDetail = JSON.stringify(myObject);
$.ajax({
type: "POST",
url: <Controller>/CheckAnswers,
data: answerDetail,
success: function(data){
//what ever
}
});
you can send your requests by ajax with PUT method not by form

Resources