-
Notifications
You must be signed in to change notification settings - Fork 475
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[feature/netcore] "/$count" not work #1189
Comments
Source code: public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseAuthentication();
var builder = new ODataConventionModelBuilder(app.ApplicationServices);
builder.EntitySet<Conta>(nameof(Conta));
app.UseMvc(routebuilder =>
{
routebuilder.Select().Expand().Filter().OrderBy().MaxTop(100).Count();
routebuilder.MapODataServiceRoute("odata", "api", builder.GetEdmModel());
routebuilder.EnableDependencyInjection();
});
} public void ConfigureServices(IServiceCollection services)
{
services.AddOData();
services.AddMvc(options =>
{
////https://github.com/OData/WebApi/issues/597
////https://q-a-assistant.info/computer-internet-technology/exception-connecting-excel-to-net-core-v1-1-odata-v4-add-at-least-one-media-type/3883239
//// loop on each OData formatter to find the one without a supported media type
foreach (var outputFormatter in options.OutputFormatters.OfType<Microsoft.AspNet.OData.Formatter.ODataOutputFormatter>().Where(_ => _.SupportedMediaTypes.Count == 0))
{
// to comply with the media type specifications, I'm using the prs prefix, for personal usage
outputFormatter.SupportedMediaTypes.Add(new Microsoft.Net.Http.Headers.MediaTypeHeaderValue("application/prs.odatatestxx-odata"));
}
foreach (var inputFormatter in options.InputFormatters.OfType<Microsoft.AspNet.OData.Formatter.ODataInputFormatter>().Where(_ => _.SupportedMediaTypes.Count == 0))
{
// to comply with the media type specifications, I'm using the prs prefix, for personal usage
inputFormatter.SupportedMediaTypes.Add(new Microsoft.Net.Http.Headers.MediaTypeHeaderValue("application/prs.odatatestxx-odata"));
}
}).AddJsonOptions(opt =>
{
opt.SerializerSettings.PreserveReferencesHandling = PreserveReferencesHandling.None;
opt.SerializerSettings.Converters.Clear();
opt.SerializerSettings.Converters.Add(new DtoJsonConverter());
opt.SerializerSettings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter());
opt.SerializerSettings.ContractResolver = new DtoContractResolver();
});
services.AddDbContext<ErpContext>(options =>
{
options.UseMySql(Session.Connection.GetStringConexao());
});
} Controller: [Produces("application/json")]
[Route("api/[controller]")]
public class ContaController : ODataController
{
private ErpContext _erpContext;
/// <summary>
/// Inicializa uma nova instância da classe <see cref="ContaController"/>.
/// </summary>
/// <param name="erpContext"></param>
public ContaController(ErpContext erpContext)
{
_erpContext = erpContext;
}
/// <summary>
/// Retorna todas as contas cadastradas baseado na requisição OData
/// </summary>
/// <returns>Lista de todos os registros</returns>
[HttpGet]
[ProducesResponseType(typeof(void), 401)] //Unauthorized
[ProducesResponseType(typeof(void), 500)] //Internal Server Error
[ProducesResponseType(typeof(IQueryable<Conta>), 200)] //OK
[EnableQuery]
public IActionResult Get() //Microsoft.AspNet.OData.Query.ODataQueryOptions<Conta> queryOptions
{
return Ok(_erpContext.Conta.AsQueryable());
}
/// <summary>
/// Retorna uma conta especifica
/// </summary>
/// <para name="codigo">Código da conta</para>
/// <returns>Registro do código informado</returns>
[HttpGet("{codigo}")]
[ProducesResponseType(typeof(Conta), 200)] //OK
[ProducesResponseType(typeof(void), 401)] //Unauthorized
[ProducesResponseType(typeof(void), 404)] //Not Found
[ProducesResponseType(typeof(void), 500)] //Internal Server Error
[EnableQuery]
public IActionResult Get([FromODataUri]int codigo)
{
var item = new ContaDal().Where(dto => dto.Codigo == codigo).GetFirst();
if (item == null)
{
return NotFound();
}
return Ok(item);
}
/// <summary>
/// Insere uma nova conta
/// </summary>
/// <para name="value">Conta para ser inserida</para>
/// <returns>Conta criada preenchida com o código de chave primária</returns>
[HttpPost]
[ProducesResponseType(typeof(void), 400)] //Bad Request
[ProducesResponseType(typeof(void), 401)] //Unauthorized
[ProducesResponseType(typeof(void), 500)] //Internal Server Error
[ProducesResponseType(typeof(Dto.Core.ValidationResult), 422)] //Unprocessable Entity
[ProducesResponseType(typeof(ContaInsertUpdate), 201)] //Created
public IActionResult Post([FromBody]ContaInsertUpdate value)
{
if (value == null)
{
return BadRequest();
}
var dto = value.ToDto();
var resultValidation = Bll.Helpers.Validation.Validade(dto);
if (resultValidation != null && resultValidation.HasError)
{
return UnprocessableEntity(resultValidation);
}
if (new ContaDal().Insert(dto) == false)
{
return StatusCode(500);
}
//TODO Retornar o objeto inserido (Com código)
return Created(GetAbsoluteUriWithOutQuery(Request) + "/" + value.Codigo, value);
}
/// <summary>
/// Atualiza conta existente
/// </summary>
/// <para name="value">Conta para ser atualizada</para>
[HttpPut("{codigo}")]
[ProducesResponseType(typeof(void), 200)] //OK
[ProducesResponseType(typeof(void), 400)] //Bad Request
[ProducesResponseType(typeof(void), 401)] //Unauthorized
[ProducesResponseType(typeof(void), 404)] //Not Found
[ProducesResponseType(typeof(Dto.Core.ValidationResult), 422)] //Unprocessable Entity
[ProducesResponseType(typeof(void), 500)] //Internal Server Error
public IActionResult Put(int codigo, [FromBody]ContaInsertUpdate value)
{
if (value == null || value.Codigo != codigo)
{
return BadRequest();
}
var dto = value.ToDto();
var resultValidation = Bll.Helpers.Validation.Validade(dto);
if (resultValidation != null && resultValidation.HasError)
{
return UnprocessableEntity(resultValidation);
}
using (var dal = new ContaDal())
{
var item = dal.Where(dtoQ => dtoQ.Codigo == codigo).GetFirst();
if (item == null)
{
return NotFound();
}
dal.Update(value.ToDto());
}
return Ok();
}
/// <summary>
/// Apaga uma conta existente
/// </summary>
/// <para name="codigo">Código da conta para ser apagada</para>
[HttpDelete("{codigo}")]
[ProducesResponseType(typeof(void), 200)] //OK
[ProducesResponseType(typeof(void), 401)] //Unauthorized
[ProducesResponseType(typeof(void), 404)] //Not Found
[ProducesResponseType(typeof(Dto.Core.ValidationResult), 422)] //Unprocessable Entity
[ProducesResponseType(typeof(void), 500)] //Internal Server Error
public IActionResult Delete(int codigo)
{
using (var dal = new ContaDal())
{
var dto = dal.Where(dtoQ => dtoQ.Codigo == codigo).GetFirst();
if (dto == null)
{
return NotFound();
}
dto.Deleted = true;
var resultValidation = Bll.Helpers.Validation.Validade(dto);
if (resultValidation != null && resultValidation.HasError)
{
return UnprocessableEntity(resultValidation);
}
dal.Delete(dto);
}
return Ok();
}
public class ContaInsertUpdate
{
public ContaInsertUpdate()
{
EmpresafuncionarioCodigo = 0;
CdLimite = 0.00000;
CdUtilizado = 0.00000;
CdSaldo = 0.00000;
PermitirQuitacao = true;
}
public int Codigo { get; set; }
public int? LojaCodigo { get; set; }
public int? SetorCodigo { get; set; }
public string Codigocontabel { get; set; }
public int? FuncionarioCodigo { get; set; }
public int? ContacontabelCodigo { get; set; }
public int? PlanodespesaCodigo { get; set; }
public int? SubplanodespesaCodigo { get; set; }
public int? PlanoreceitaCodigo { get; set; }
public int? SubplanoreceitaCodigo { get; set; }
public int? EmpresafuncionarioCodigo { get; set; }
public double? CdLimite { get; set; }
public double? CdUtilizado { get; set; }
public double? CdSaldo { get; set; }
public string NBanco { get; set; }
public int? Tipo { get; set; }
public int? ContaCP { get; set; }
public string NumeroConta { get; set; }
public string Agencia { get; set; }
public string Descricao { get; set; }
public string Autor { get; set; }
public double? Saldo { get; set; }
public double? SaldoDisponivel { get; set; }
public bool? Imprimir { get; set; }
public int? Numeracaocheque { get; set; }
public int? Relatoriocheque { get; set; }
public int? Relatoriorelacaocheque { get; set; }
public bool? PermitirQuitacao { get; set; }
public Dto.Entities.Conta ToDto()
{
return new Dto.Entities.Conta
{
Agencia = Agencia,
EmpresafuncionarioCodigo = EmpresafuncionarioCodigo,
Autor = Autor,
CdLimite = CdLimite,
CdSaldo = CdSaldo,
CdUtilizado = CdUtilizado,
Codigo = Codigo,
Codigocontabel = Codigocontabel,
ContacontabelCodigo = ContacontabelCodigo,
ContaCP = ContaCP,
Descricao = Descricao,
FuncionarioCodigo = FuncionarioCodigo,
Imprimir = Imprimir,
LojaCodigo = LojaCodigo,
NBanco = NBanco,
Numeracaocheque = Numeracaocheque,
NumeroConta = NumeroConta,
PermitirQuitacao = PermitirQuitacao,
PlanodespesaCodigo = PlanodespesaCodigo,
PlanoreceitaCodigo = PlanoreceitaCodigo,
Relatoriocheque = Relatoriocheque,
Relatoriorelacaocheque = Relatoriorelacaocheque,
Saldo = Saldo,
SaldoDisponivel = SaldoDisponivel,
SetorCodigo = SetorCodigo,
SubplanodespesaCodigo = SubplanodespesaCodigo,
SubplanoreceitaCodigo = SubplanoreceitaCodigo,
Tipo = Tipo
};
}
}
protected static Uri GetAbsoluteUriWithOutQuery(HttpRequest request)
{
UriBuilder uriBuilder = new UriBuilder();
uriBuilder.Scheme = request.Scheme;
uriBuilder.Path = request.Path.ToString();
uriBuilder.Host = request.Host.Host;
if (request.Host.Port.HasValue)
{
uriBuilder.Port = request.Host.Port.Value;
}
return uriBuilder.Uri;
}
/// <summary>
/// 422 UNPROCESSABLE ENTITY
/// </summary>
/// <param name="value">Description error</param>
/// <remarks>
/// The server understands the content type of the request entity (hence a 415 Unsupported Media Type status code is inappropriate), and the syntax of the request entity is correct (thus a 400 Bad Request status code is inappropriate) but was unable to process the contained instructions.
/// </remarks>
protected ObjectResult UnprocessableEntity(object value)
{
return StatusCode(422, value);
}
} |
I was debugging to see if I could find any solution, and I realized I was not calling ODataPathRouteConstraint.Match (...), so I decided to remove the controller attribute |
When debugging happened this error in this line:
URL: http://localhost:8080/api/conta?$count=true Does anyone have any suggestions for what it might be? |
After hours is a cause of the problem, the integrity name can not be the same as the driver name and service URL. It does not work: builder.EntitySet <Conta> ("Conta");
public class ContaController : Core.ODataController It works: builder.EntitySet <Conta> ("ContaTest");
public class ContaTestController : Core.ODataController Is this a limitation of the library or was it to work? |
+1 @ErliSoares. I am also able to get the plain response using [Route()] but it does not contain any of the "@OData" properties, similar to your GET Portman response. However, I was unable to get it to work even by changing the names. Can confirm issue using:
|
Show the controller and the configuration, maybe I'll find something to help you |
Startup: namespace WebApiPoc
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddOData();
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
IEdmModel model = GetEdmModel(app.ApplicationServices);
app.UseMvc(routeBuilder =>
{
routeBuilder.Select().Expand().Filter().OrderBy().MaxTop(100).Count();
routeBuilder.MapODataServiceRoute("odata", "api", model);
routeBuilder.EnableDependencyInjection();
});
}
private static IEdmModel GetEdmModel(IServiceProvider serviceProvider)
{
var builder = new ODataConventionModelBuilder(serviceProvider);
builder.EntitySet<Models.Organization>("OrganizationTest");
return builder.GetEdmModel();
}
}
} Controller: namespace WebApiPoc.Controllers
{
public class OrganizationTestController : ODataController
{
// GET api/values
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
[HttpGet("{id}")]
public string Get(int id)
{
return "value";
}
// POST api/values
[HttpPost]
public void Post([FromBody]string value)
{
}
// PUT api/values/5
[HttpPut("{id}")]
public void Put(int id, [FromBody]string value)
{
}
// DELETE api/values/5
[HttpDelete("{id}")]
public void Delete(int id)
{
}
}
} Thanks, @ErliSoares! |
I was unable to identify for sure, but test the URL: http://host:port/api/OrganizationTest |
If it does not work, I'll give you some changes to see if it works. |
@ErliSoares - that did it! Thanks! If I remember correctly, there's a way to make routes case insensitive. I'll look, and if I find anything I'll report back. |
Thanks, if I find a way I'll report back too. |
@ErliSoares - Form the comments on Jan 6/7, the Resource not found for the segment 'conta' is expected if you query http://.../contra instead of http://.../Contra, i.e. it is case sensitive. I've tried querying with $count=true for both cases where the entity name does not match the clr type name and when they do, both work as expected but it is case sensitive. I do notice than in your initial bug, you are using "conta" in the Url and "nameof(Contra)" in the model. nameof("Contra") will produce "Contra" so it's unclear why you received a response when you should have received a 404. Are you still having issues with $count? |
#1231 filed for documenting how to configure case insensitivity. |
@ErliSoares did you try to use |
@ErliSoares we're currently reviewing open issues in preparation for our upcoming WebAPI 7.x release. Is the behavior that you're seeing still applicable? Please review the posts from Rob and Igor above. Thank you. |
I did not understand the reason, but when I try to return the count of the records does not come and not the error.
It seems duplicate: #1160 #1153
I tried several ways, I did not understand why it does not work, putting in and taking out the "? $ Count = true" return is always the same.
Package
Microsoft.AspNetCore.All 2.0.3
Microsoft.AspNetCore.OData 7.0.0-beta1
The text was updated successfully, but these errors were encountered: