I'm working on rebuilding an API by converting to aspnetcore from aspnet. An unfortunate roadblck for me has been around Odata in general, one of the concessions I was forced to make was moving a few routes to OData, as a mixture of OData and standard API wasnt compatible with swagger.
Anyhow, One such method (The last one remaining) has the following Signature
[HttpPost]
public IActionResult PostChanges([FromBody] ICollection<BulkEditDTO> changeSet)
{
if (!ModelState.IsValid)
{
var error = ModelState.Values.Select(x => x.Errors).FirstOrDefault();
return BadRequest(ModelState);
}
//TODO:Get data?
}
Object
public class BulkEditDTO
{
[Key]
[DataMember]
public int Id { get; set; }
[DataMember]
public SomeDTO Changes { get; set; }
}
Now I feel like I've tried every combination of Functions, complex types, actions, ODataActionParamaers that i can think of - but i cannot seem to solve this. I even tried a simple DTO that has the ienumerable as a property, but that ran into problems with 'needing a key'.
A lot of the research i've tried leads me down to this path:
https://github.com/OData/WebApi/issues/1873
That isnt quite the same, becuase thats looking for a header and Im trying to use a body.
Is there an actual workaround to this?
Related
I am building a Web API using Dapper for .NET Core and trying to adhere to Clean Architecture principles. The API is consumed by an external Angular front-end.
I have repositories that use Dapper to retrieve data from the database, and this data then passes through a service to be mapped into a DTO for display to the user.
It is my understanding that an entity should be an exact representation of the database object, with no extra properties, and that I should use DTOs if I require some additional properties to show the user (or if I wish to obscure certain properties from the user too).
Suppose I have a DTO:
public class StudentDTO
{
public Guid Id { get; set; }
public string Name { get; set; }
public List<Assignment> Assignments { get; set;}
}
and its corresponding Entity:
public class Student
{
public Guid Id { get; set; }
public string Name { get; set; }
}
With this model, should I want to get a student with all of their assignments, I'd need to have two repository calls, and do something like this in the service:
public StudentDTO GetById(Guid id)
{
var student = this.studentRepository.GetById(id);
var assignments = this.assignmentRepository.GetByStudentId(id);
return SomeMapperClass.Map(student, assignments);
}
But this seems inefficient and unnecessary. My question is, should I not just retrieve the Assignments when I get the student entity in the repository, using a JOIN? Or would this violate what an entity is supposed to be?
I apologise, I do realise this is a rather simple question, but I'd really like to know which method is the best approach, or if they both have their use cases
I think it would be more efficient, since map uses reflections, that is slower tens times
public StudentDTO GetById(Guid id)
{
var student = this.studentRepository.GetById(id);
student.Assignments = this.assignmentRepository.GetByStudentId(id);
return student;
}
but the common way is
return _context.Students.Include(i=>i.Assignments).FirstOrDefault(i=> i.Id==id);
This is why the generic repository is a bad idea in the most casses, since it is hard to guess what set of data you will need.
When can we use Mapper.ProjectTo over Mapper.Map in Asp.Net core application? Use of ProjectTo method in Entity Framework Core, will it reduce the number of fields I query from the database to match the model if we add it in the beginning of the _mapper.ProjectTo statement? How will it reduces the query number of fields?
Instead of using this
_mapper.ProjectTo<CompareVarValdto>(_compareRepo.GetCompareDetailsByid(id))
we can use this right?
_mapper.Map<CompareVarValdto>(result)
ComapreVarValdto.cs
public class CompareVarValdto
{
public int CompareVarValId { get; set; }
public int CURRENT_CATEGORY_ID { get; set; }
public int Current_LaunguageId { get; set; }
public int Current_compareDimId { get; set; }
public string CONTENT { get; set; }
}
Controller get method code snippet
[Route("api/[controller]")]
[ApiController]
public class ComapareController : ControllerBase
{
private readonly ICompare _compareRepo;
private readonly IMapper _mapper;
public ComapareController(ICompare compare, IMapper mapper)
{
_compareRepo = compare;
_mapper = mapper;
}
[HttpGet("{id:int}")]
public IActionResult GetComapare(int id)
{
try
{
return Ok(_mapper.ProjectTo<CompareVarValdto>(_compareRepo.GetCompareDetailsByid(id)));
//VS
//var result = _compareRepo.GetCompareDetailsByid(id);
//return Ok(_mapper.Map<CompareVarValdto>(result));
}
catch (Exception ex)
{
return StatusCode(StatusCodes.Status500InternalServerError,
"Error retrieving data from the database");
}
}
compareRepo Database method
public IQueryable<CompareDim> GetCompareDetailsByid(int compareId)
{
return _context.CompareDim.Include("Launguage");
}
reference: automapper LINQ deep dive
I think the article explains your question.
If I understand the article right Mapper.Map works in-memory, Project to creates a expression-tree that needs to be parsed and understood by the underlying query provider.
I'm not sure if it will reduce the number of queried fields. My understanding/guess is, and I hope people will correct me, that ProjectTo is more efficient because the query in only executed after constructing the complete expression tree, and you use the query provider to execute the query (in SQL).
While working in memory: "(...)
The main problem people run into here is that typically that source object is some object filled in from a data source, whether it's a
relational or non-relational source. This implies that the original
fetch pulled a lot more information back out than we needed to."
this does not seem to be the case.
I have a big database existing database to comunicate with, and I'm using EF 5.0 database first, the problem I'm having is that if I create any data decoration like [stringlength(50)] on the class and then the databases is uploaded, when I "upload from database" all data annotations are gone. How can I do to keep them?
It's very simple: You Can't! Because those codes are auto-generated and will be over written on each model update or change.
However you can achieve what you need through extending models. Suppose that EF generated the following entity class for you:
namespace YourSolution
{
using System;
using System.Collections.Generic;
public partial class News
{
public int ID { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public int UserID { get; set; }
public virtual UserProfile User{ get; set; }
}
}
and you want do some work arounds to preserve your you data annotations and attributes. So, follow these steps:
First, add two classes some where (wherever you want, but it's better to be in Models) like the following:
namespace YourSolution
{
[MetadataType(typeof(NewsAttribs))]
public partial class News
{
// leave it empty.
}
public class NewsAttribs
{
// Your attribs will come here.
}
}
then add what properties and attributes you want to the second class - NewsAttribs here. :
public class NewsAttrib
{
[Display(Name = "News title")]
[Required(ErrorMessage = "Please enter the news title.")]
public string Title { get; set; }
// and other properties you want...
}
Notes:
1) The namespace of the generated entity class and your classes must be the same - here YourSolution.
2) your first class must be partial and its name must be the same as EF generated class.
Go through this and your attribs never been lost again ...
The accepted answer may work for standard data operations, but I am trying to validate the model prior to the call to DbSet.Add using TryValidateObject. With the accepted answer, it is still not picking up on the data annotations.
What did work for me I found in a .NET Runtime GitHub thread, as proposed by what I'm inferring is one of the .NET developers.
Basically, this is a bug, and you have to force the model to recognize the metadata decorations using TypeDescriptor.AddProviderTransparent . . .
TypeDescriptor.AddProviderTransparent(new AssociatedMetadataTypeTypeDescriptionProvider(typeof(News), typeof(NewsAttrib)), typeof(News));
Once I make this call, TryValidateObject recognizes the data annotations and returns false when any of the constraints are not met.
Here's the link. I little more than half-way down, there's a working code sample in a .zip file.
https://github.com/dotnet/runtime/issues/46678
The parameter request is always null using Web API. Am I missing something with using a strongly typed object as a parameter instead of simple types as the parameters.
Url
http://localhost:2222/api/v1/divisions?EventId=30
Controller Action
public virtual ApiDivisionsResponse Get(ApiDivisionsRequest request)
{
return _apiDivisionsService.GetDivisions(request);
}
Object
public class ApiDivisionsRequest : ApiAuthorizedRequest
{
public ApiDivisionsRequest()
{
Page = 1;
PageSize = 10;
}
public int EventId { get; set; }
public int PageSize { get; set; }
public int Page { get; set; }
public string[] Includes { get; set; }
}
I very strongly invite you to read the following article to better understand how parameter binding works in the Web API. After reading it you will understand that by default the Web API binds query string parameters to primitive types and request body content to complex types.
So if you need to bind query string parameters to complex types you will need to override this default behavior by decorating your parameter with the [FromUri] parameter:
public virtual ApiDivisionsResponse Get([FromUri] ApiDivisionsRequest request)
{
...
}
And yeah, I agree with you - that's a hell of a mess - model binding was so easy in plain ASP.NET MVC and they created a nightmare in the Web API. But once you know how it works you will avoid the gotchas.
I am currently using the WCF Web API and it is working great with synchronous tasks.
However, when I try and implement asynchronous operations using Task/Task like so:
[WebGet(UriTemplate = "landmark")]
Task<IEnumerable<Closest>> GetLandmarkAsync(float latitude, float longtitude)
{
return Task.Factory.StartNew(() => CalculateClosestThing(latitude, longtitude));
}
I am getting this error:
System.Threading.Tasks.Task`1[System.Collections.Generic.IEnumerable`1[ContentRepository.Data.Entities.Closest]] cannot be serialized because it does not have a parameterless constructor.
Now, the first thing I did was check the Closest Class and add a parameterless constructor like so:
public class Closest
{
public Closest() { }
public double Distance { get; set; }
public string Name { get; set; }
}
But I'm still getting the same error. I've tried everything and clearly there is a parameterless constructor on the class. Has anyone ever experienced this? Any ideas?
Ensure you new up a WebApiConfiguration obj (derived from HttpConfiguration) and set it as the default Http configuration with a call to routes.SetDefaultHttpConfiguration() in the host (I'm using an MVC host).
I had the same problem as you before I switched from HttpConfiguration to WebApiConfiguration.
For more details see here.
Give these a read:
http://arbel.net/2011/06/04/wcf-tasks/
http://msdn.microsoft.com/en-us/library/ms730059.aspx
http://www.dotnetcurry.com/ShowArticle.aspx?ID=237