OData Web API Put Request with DTO - odata

I have a similar question to the one here:
OData update controller throwing 415 Unsupported media type error.
I have an OData Web API controller that has a put request to update a Product but so that I don't expose my database entity I would like the put method to accept a ProductDTO instead. Is this possible?
At the moment, I have the following in my ProductsController:
public IHttpActionResult Put([FromODataUri] int key, ProductDTO product)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (key != product.ID)
{
return BadRequest();
}
Product DBProduct = AsProduct(product);
db.Entry(DBProduct).State = EntityState.Modified;
try
{
db.SaveChanges();
}
catch (DbUpdateConcurrencyException)
{
if (!ProductExists(key))
{
return NotFound();
}
else
{
throw;
}
}
return Updated(DBProduct);
}
And this is my WebApiConfig.cs file:
builder.EntitySet<Product>("Products");
builder.EntitySet<ProductDTO>("ProductDTO");
Where Product is:
public class Product
{
public int ID { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public string Category { get; set; }
public DateTime Timestamp { get; set; }
}
And ProductDTO:
public class ProductDTO
{
public int ID { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public string Category { get; set; }
}
But I get the following message:
The entity type 'ProductService.DTOs.ProductDTO' is not compatible with the base type 'ProductService.Models.Product' of the provided entity set 'Container.Products'. When an entity type is specified for an OData feed or entry reader, it has to be the same or a subtype of the base type of the specified entity set.
There must be a way of doing this...any ideas?
Cheers

You need to
return Updated(product);
instead of
return Updated(DBProduct);

The result needs to not only accept the ProductDTO type, but also return that same type of ProductDTO.
I do this by converting the entity back into the DTO type after it has been processed. This way you can do what Tan Junfu said in their answer:
return Updated(product);
This product could be the result of converting DBproduct back into the DTO type.

Related

ASP.NET Web API and MongoDB Atlas - Method to Retrieve Specific Data from Database

I have created an ASP.NET Web API project (using .NET Framework 4.6.1) in Visual Studio Community Edition 2017 that utilizes MongoDb Atlas via the MongoDB .NET Driver. The project stores "Patients" with a few different attributes.
I have successfully implemented a Get() method to return a "Patient". I now want to implement a GetMedications() method to return only the medications of a particular "Patient". Below are the pertinent methods in my "PatientsController" file:
public async Task<Patient> Get(string id)
{
try
{
ObjectId internalId = GetInternalId(id);
return await _patients.Find(p => p.Id == id || p.InternalId == internalId).FirstOrDefaultAsync();
}
catch (Exception ex)
{
throw ex;
}
}
[Route("api/patients/{id}/medications")]
public async Task<Medication> GetMedications(string id)
{
try
{
ObjectId internalId = GetInternalId(id);
var patient = await _patients.Find(p => p.Id == id || p.InternalId == internalId).FirstOrDefaultAsync();
return patient.Medications;
}
catch (Exception ex)
{
throw ex;
}
}
private ObjectId GetInternalId(string id)
{
ObjectId internalId;
if (!ObjectId.TryParse(id, out internalId))
internalId = ObjectId.Empty;
return internalId;
}
Visual studio displays this error for return patient.Medications:
Cannot implicitly convert type 'Systems.Collections.Generic.ICollection<WebAPIDemo.Models.Medication>' to 'WebAPIDemo.Models.Medication'
Here is my Patient class (and other applicable classes):
public class Patient
{
[BsonId]
public ObjectId InternalId { get; set; }
public string Id { get; set; }
public string Name { get; set; }
public ICollection<Ailment> Ailments { get; set; }
public ICollection<Medication> Medications { get; set; }
}
public class Medication
{
public string Name { get; set; }
public int Doses { get; set; }
}
public class Ailment
{
public string Name { get; set; }
}
How could I properly write the GetMedications() method?
Your problem is that you are returning a collection when your method only returns one item, so you need to make sure the types match.
Change the return type of your method:
public async Task<ICollection<Medication>> GetMedications(string id)

MVC APi with Entity framework, best way to update?

I am working on a project that uses an MVC API with Entity Framework 6.1.3. We need to implement Get, GET/ID, Insert, Update and Delete API's.
for Insert and Update we have a parameter class that is not the same one as the DB, i am not sure what will be the best solution for that.
For an Update (the one that i have more questions) i can find a record and then update all properties manually, this is something i want to avoid. If i use the currentvalues from the entity then i will have to set the ExtraValues properties in all the apis that i am going to write, that kind of looks weird.
Note: i want to have a child class since most of the entities uses the same fields (Created/Updated) so instead of having those in all the classes i rather have them as inheritance.
There has to be a better solution for this problem, could someone help with ideas or best ways to do this.
public class DBClassA : ExtraValues
{
public int ID { get; set; }
public string Name { get; set; }
}
public class DBClassB : ExtraValues
{
public int ID { get; set; }
public string Name { get; set; }
}
//This will be use in all the classes
public class ExtraValues
{
public string SameValueInOtherClasses { get; set; }
public string SameValueInOtherClasses2 { get; set; }
public string SameValueInOtherClasses3 { get; set; }
}
[HttpGet]
public List<DBClassA> Get()
{
return new List<DBClassA>();
}
[HttpGet]
public DBClassA Get(int ID)
{
return new DBClassA();
}
[HttpPost]
public HttpResponseMessage Insert(DBClassA obj)
{
using (var context = new DBEntities())
{
context.Entity.Attach(DBClassA);
context.SaveChanges();
}
return Request.CreateResponse(HttpStatusCode.OK);
}
[HttpPut]
public HttpResponseMessage Update(int ID, DBClassA obj)
{
using (var context = new DBEntities())
{
var entity = context.Entity.Find(ID);
//I will have to put the ExtraValues here
obj.ExtraValues = "";
_context.Entry(entity).CurrentValues.SetValues(obj);
context.SaveChanges();
}
return Request.CreateResponse(HttpStatusCode.OK);
}
using (var db = new DBEntities())
{
db.Entry(obj).State = EntityState.Modified;
db.SaveChanges();
}
more info: http://www.entityframeworktutorial.net/EntityFramework4.3/update-entity-using-dbcontext.aspx

Getting error The 'proprrty1' property on 'table1' could not be set to a 'System.Int64' value. in my mvc application

I am getting an error in my mvc application. I am trying to display record from my sql table using entity framework. here is the code which i am trying. But I dnt know why this code giving me error
This is my model
[Table("tbInsertMobile")]
public class tbInsertMobile
{
[Key]
public int MobileID { get; set; }
public string MobileName { get; set; }
public string MobileIMEno { get; set; }
public string mobileprice { get; set; }
public string mobileManufacured { get; set; }
}
my another class in model
public class EmployeeContext:DbContext
{
public DbSet<tbInsertMobile> usermobiles { get; set; }
}
This the code of my contoller
public ActionResult Index(int id)
{
EmployeeContext employeeContext = new EmployeeContext();
tbInsertMobile employee = employeeContext.usermobiles.Single(x => x.MobileID == id);
return View(employee);
}
and now the error which i am getting is this
The 'MobileID' property on 'tbInsertMobile' could not be set to a 'System.Int64' value. You must set this property to a non-null value of type 'System.Int32
Experts Tell me where I am getting this error
Thanks
The answer is there already in the comment.
It's trying to assign a long to an int. Change MobileID to be long. – CodeCaster

Value cannot be null

I have written ASP.NET MVC Code for CRUD but while Updating,Deleting and Details the page is throwing the error as value can not be null.my code is as follows:
public ActionResult Edit(int id=0)
{
var alb=db.Albums.FirstOrDefault(x=>x.AlbumId==id);
return View(alb);
}
public ActionResult Detail(int id=0)
{
var alb=db.Albums.Find(id);
return View(alb);
}
public ActionResult Delete(int id=0)
{
var album = db.Albums.Find(id);
return View(album);
}
and my Model is
public class Album
{
public int AlbumId { get; set; }
public int GenreId { get; set; }
public int ArtistId { get; set; }
public string AlbumTitle { get; set; }
public String Price { get; set; }
public String AlbumArtUrl { get; set; }
public Genre Genre { get; set; }
public Artist Artist { get; set; }
}
This is because the variable alb and album is showing me the null value its not getting any ID.
so suggest me on this.
This is because the variable alb and album is showing me the null
value its not getting any ID
If there is no record in your database then you should handle it gracefully in code. You should have something like:
var alb=db.Albums.FirstOrDefault(x=>x.AlbumId==id);
if (alb==null) {
// TempData, simplest way to give you an example
TempData["error_msg"] = "the record does not exists";
return RedirectToAction("index", "error");
}
return View(alb);
in your ErrorController
public ActionResult Index() {
ViewBag.ErrorMsg = TempData["error_msg"];
return View();
}
in the view (Index.cshtml) for your error controller
<h1>You have been, or trying to be, a naughty boy</h1>
<p>#ViewBag.ErrorMsg</p>
This is not the most elegant solution though but good enough to get you started. I think the most important part of my answer is not the code I gave you but the suggestion to handle that kind of situation (null values / missing or invalid records) in code.

ASP.NET EditorTemplate DropdownList

Every time I add a new App It creates a new AppCategory. I am seriously screwing this up somehow
code first entity framework objects
public class AppCategory
{
public int ID { get; set; }
public string Name { get; set; }
public ICollection<App> apps { get; set; }
}
public class App
{
public int ID { get; set; }
public string Name { get; set; }
public AppCategory Category { get; set; }
}
Editor Template (I would love to just make just one Foreign Key EditorTemplate)
#inherits System.Web.Mvc.WebViewPage
#Html.DropDownList("Category", LIG2010RedesignMVC3.Models.Repo.GetAppCategoriesSelect())
and of course the repository
public static IEnumerable<SelectListItem> GetAppCategoriesSelect()
{
return (from p in GetAppCategories()
select new SelectListItem
{
Text = p.Name,
Value = p.ID.ToString(),
});
}
public static ICollection<AppCategory> GetAppCategories()
{
var context = new LIGDataContext();
return context.AppCategories.ToList();
}
Every time I add a new App It creates a new AppCategory I am seriously screwing this up somehow
Adding more debug info
#inherits System.Web.Mvc.WebViewPage
#Html.DropDownList("", LIG2010RedesignMVC3.Models.Repo.GetAppCategoriesSelect())
gives me a validation message on the post
Parameters application/x-www-form-urlencoded
Category 1
Name 8
Validation error The value '1' is invalid.
This makes sense because Category should be an object not an integer.
Controller Code as asked for
pretty sure this isnt the problem as it came from MVCScaffold
[HttpPost]
public ActionResult Create(App d)
{
if (ModelState.IsValid)
{
context.Apps.Add(d);
context.SaveChanges();
return RedirectToAction("Index");
}
return View();
}
My model was incorrectly set up ... virtual ICollection and just the foreign key id for the sub and everything worked... changes below
Model
public class AppCategory
{
public int ID { get; set; }
public string Name { get; set; }
public **virtual** ICollection<App> Apps { get; set; }
}
public class App
{
public int ID { get; set; }
********************************************
[UIHint("AppCategory")]
public int AppCategoryID { get; set; }
********************************************
public string Name { get; set; }
}
public class LIGDataContext : DbContext
{
public DbSet<AppCategory> AppCategories { get; set; }
public DbSet<App> Apps { get; set; }
}
/Views/Shared/EditorTemplates/AppCategory.cshtml
#inherits System.Web.Mvc.WebViewPage
#Html.DropDownList("", LIG2010RedesignMVC3.Models.Repo.GetAppCategoriesSelect())
AppController
[HttpPost]
public ActionResult Create(App d)
{
if (ModelState.IsValid)
{
this.repository.Add(d);
this.repository.Save();
return RedirectToAction("Index");
}
return View();
}
If you bind your dropDownList to Category.Id, you'll at least get the selected value into that filed, but nothing else in your Category Object.
The model binder cannot create the AppCategory object from the form collection in your Create action because the form only has an ID for that object (the other properties of AppCategory are not there).
The quickest solution would be setting the Category property of your App object manually, like this :
[HttpPost]
public ActionResult Create(App d) {
int categoryId = 0;
if (!int.TryParse(Request.Form["Category"] ?? String.Empty, out categoryId) {
// the posted category ID is not valid
ModelState.AddModelError("Category",
"Please select a valid app category.")
} else {
// I'm assuming there's a method to get an AppCategory by ID.
AppCategory c = context.GetAppCategory(categoryID);
if (c == null) {
// couldn't find the AppCategory with the given ID.
ModelState.AddModelError("Category",
"The selected app category does not exist.")
} else {
// set the category of the new App.
d.Category = c;
}
}
if (ModelState.IsValid)
{
context.Apps.Add(d);
context.SaveChanges();
return RedirectToAction("Index");
}
return View();
}

Resources