Cosmos DB update fails Unable to cast Guid to string - asp.net-core

Tried to update a Cosmos DB record in ASP.NET core 3.1. But the update fails with the following message: "Unable to cast object of type 'System.Func'2[Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry,System.Guid]' to type 'System.Func'2[Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry,System.String]'
The error occurs on the saveCangesAsync();
Simplified, the code looks like:
// The service
public async Task<Todo> UpdateAsync(Todo entity)
{
var response = ctx.Todos.Update(entity);
await ctx.SaveChangesAsync(); // Error here
return response.Entity;
}
// The entity Todo
public class Todo
{
public Guid id { get; set; }
[Required(ErrorMessage = "Description is required")]
public string description { get; set; }
...
}
// The context
public class TodoDbContext : DbContext
{
public DbSet<Todo> Todos { get; set; }
public TodoDbContext(DbContextOptions<TodoDbContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasDefaultContainer("Todos");
}
}
// The controller
[HttpPut]
public async Task<IActionResult> Put(Todo todo)
{
try
{
if (ModelState.IsValid)
{
Todo td = await service.GetAsync(todo.id.ToString());
if (td != null)
{
td.description = todo.description;
var response = await service.UpdateAsync(td);
return Ok(response);
}
return BadRequest("Not found.");
}
else
{
return BadRequest(ModelState);
}
}
catch (Exception ex)
{
return BadRequest(ex.Message); // Exception here
}
}
I can insert, read, but not update, so following code runs fine (with a Guid as well)
public async Task<Todo> CreateAsync(Todo entity)
{
entity.id = Guid.NewGuid();
var response = await ctx.Todos.AddAsync(entity);
await ctx.SaveChangesAsync();
return response.Entity;
}
Thanks for any help!

Don't use "id", that will collide with the automatically generated id. Use another property or use "Id" (with a capital I) in stead, to solve the problem.

Is there any reason you're using Guid as your id type? In your Todo class you could write it like so:
public class Todo
{
[JsonProperty("id")
public string Id { get; set; }
[JsonProperty("description")
[Required(ErrorMessage = "Description is required")]
public string description { get; set; }
}
I'm assuming that you're using v3 of the Cosmos DB SDK. You would then just use Guid.NewGuid().ToString() to set the id as a Guid (but as a string type).

Related

Xamarin.Forms: Cannot connect to database

I have this database structure:
public class QRDatabase
{
readonly SQLiteAsyncConnection _database;
public QRDatabase(string dbPath)
{
_database = new SQLiteAsyncConnection(dbPath);
_database.CreateTableAsync<db_QRCODE_Type>().Wait();
}
public Task<List<db_QRCODE_Type>> GetQRCode()
{
return _database.Table<db_QRCODE_Type>().ToListAsync();
}
public Task<int> SaveQRCode(db_QRCODE_Type note)
{
if (note.ID != 0)
{
return _database.UpdateAsync(note);
}
else
{
return _database.InsertAsync(note);
}
}
public Task<int> DelteQRCode(db_QRCODE_Type note)
{
return _database.DeleteAsync(note);
}
}
This uses this type:
public class db_QRCODE_Type
{
[PrimaryKey, AutoIncrement]
public int ID { get; set; } // Identificator of column
public string firstName { get; set; } // firstname
public string lastName { get; set; } // firstname
public byte[] qrBytes { get; set; } //qr code in bytes
}
Then, in the class where I need the DB I am doing this from the tutorial here:
https://learn.microsoft.com/de-de/xamarin/get-started/quickstarts/database?pivots=windows
static QRDatabase database;
static string nameOfDB = "01db_qrs_q2go.db3";
public static QRDatabase Database
{
get
{
if (database == null)
{
database = new QRDatabase(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), nameOfDB));
}
return database;
}
}
However, I am not quite sure how this works since I am never calling "Database" even though this is where the database is supposed to be initiliased.
Now, when I try to write to the database:
db_QRCODE_Type entry = new db_QRCODE_Type();
entry.firstName = entry_firstname.Text;
entry.lastName = entry_lastname.Text;
entry.qrBytes = qrCodeBytes;
try
{
await database.SaveQRCode(entry);
}
catch
{
DependencyService.Get<IMessage>().LongAlert("Etwas hat nicht funktioniert, bitte versuche es noch einmal. Fehlercode: DB_665h");
}
It fails saying it is not set reference to an instance and goes into the catch block. I am doing everything as in the tutorial. Why is this happening?
Thank you!

getting 400 error on webapi call blazorserver

i am trying to setup a blazor server app, calling a webapi.
I keep getting a 400 error returned, when I call the API.
I have 3 Projects, projectserver and projectapi. projectserver is where the Blazor app sits and Project API is where the API sits.
I don't know if the apicall can find the API as it does not hit any breakpoints in the API section, I am totally confused, as if it cannot find the API then it should return a 404 or other error and not 400 ?
thank you for your efforts.
this is my code,
Projectserver, this is where I post the Register Model to the API
public string message { get; set; }
public RegisterModel r = new RegisterModel();
private async Task Create(MouseEventArgs e)
{
var json = Newtonsoft.Json.JsonConvert.SerializeObject(r);
var client = clientfactory.CreateClient("ServerApi");
var result = await client.PostAsJsonAsync("/Account/Register",json); // check the Startup file and check base address for the Full route.
message = result.StatusCode.ToString();
}
}
the ClientFactory returns the base address of what is defined in startup.cs
services.AddHttpClient("ServerApi", client => client.BaseAddress = new Uri("https://localhost:44302/"));
the API is Projectserver and defined as follows.
[Route("[controller]")]
[ApiController]
public class AccountContoller : ControllerBase
{
private readonly ApplicationDbContext _context;
private readonly SecurityOptions _securityOptions;
private readonly JwtIssuerOptions _jwtOptions;
// GET: api/<Account>
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/<Account>/5
[HttpGet("{id}")]
public string Get(int id)
{
return "value";
}
// POST api/<Account>
[HttpPost]
public void Post([FromBody] string value)
{
}
// POST api/<Account>
[HttpPost("Register")]
public async Task<ActionResult<RegisterResult>> Register(RegisterModel model)
{
RegisterResult r = new RegisterResult();
var Exisits = await _context.Users.Where(r => r.EmailAddress == model.Email).FirstOrDefaultAsync();
if(Exisits != null)
{
r.Sucsess = false;
r.ErrorMessage = "Email - Already Exisits";
return r;
}
else
{
try
{
User newuser = new User();
newuser.CreatedDateTime = DateTime.UtcNow;
newuser.UserID = Guid.NewGuid();
newuser.MobileNumber = model.MobileNumber;
newuser.Password = model.Password;
newuser.FirstName = model.FirstName;
newuser.Surname = model.LastName;
_context.Users.Add(newuser);
await _context.SaveChangesAsync();
r.Sucsess = true;
return r;
}
catch(Exception e)
{
r.Sucsess = false;
r.ErrorMessage = e.ToString();
return r;
}
}
}
the Model classes are defined as Serializable
[Serializable]
public class RegisterResult
{
public bool Sucsess { get; set; }
public string ErrorMessage { get; set; }
}
[Serializable]
public class RegisterModel
{
public string UserName { get; set; }
public string Password { get; set; }
public string Email { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string RoleID { get; set; }
public string EntityID { get; set; }
public string MobileNumber { get; set; }
}
Can you please modify your code as below and give it a try:-
var serializedBody = JsonConvert.SerializeObject(r);
var jsonRequestBodyContent = new StringContent(serializedBody, Encoding.UTF8,"application/json");
var client = clientfactory.CreateClient("ServerApi");
var result = await client.PostAsync("/Account/Register",jsonRequestBodyContent);

Uploading File and Employee Model in POST Web API

I have an Employee Model and Profile Picture. I need to Upload Profile Picture and Model Date both in one POST method. I just need to save Image File Name in Database and Uploading image in a WebRoot Directory.
Here is my Model:
public partial class Employers
{
public int EmployerId { get; set; }
public string Companyname { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Mobile { get; set; }
public string Password { get; set; }
public string DisplayImage { get; set; }
public bool? IsActive { get; set; }
public DateTime StateDate { get; set; }
}
Here is my Controller Code:
[HttpPost]
public async Task<ActionResult<Employees>> PostEmployees([FromForm] FileUploadAPI Image, [FromBody]Employees employees)
{
try
{
_context.Employees.Add(employees);
await _context.SaveChangesAsync();
await UploadImage(Image, 2, employees.EmployeeId);
var returnInfo = CreatedAtAction("GetEmployees", new { id = employees.EmployeeId }, employees);
return returnInfo;
}
catch(Exception ex)
{
return NoContent();
}
}
public class FileUploadAPI
{
public IFormFile files { get; set; }
}
public async Task<string> UploadImage(FileUploadAPI files, int UserType, int UserId)
{
if (files.files.Length > 0)
{
try
{
if (!Directory.Exists(_hostingEnvironment.WebRootPath + "\\Employees\\"))
{
Directory.CreateDirectory(_hostingEnvironment.WebRootPath + "\\Employees\\");
}
Guid guid = Guid.NewGuid();
string filename = _hostingEnvironment.WebRootPath + "\\Employees\\" + $"EM-{UserType}-UserId-{guid}";
using (FileStream filestream = System.IO.File.Create(filename))
{
await files.files.CopyToAsync(filestream);
filestream.Flush();
return filename;
}
}
catch (Exception ex)
{
return ex.ToString();
}
}
else
{
return "Not Found";
}
}
If i just upload File in POSTMAN without Employee Model, its working fine. But when i pass both File EMployee Data both then FILE is returning null.
Any Suggestion, Solution ?
Thanks
It's impossible to use [FromForm] and [FromBody] simultaneously as is mentioned here. But I think You have 2 choices:
You can either put your JSON body into a form and send Employee data besides the File or use 2 separate endpoints for form upload. An endpoint for uploading user picture using [FromFile] and obtaining a pictureId and another for sending Employee in the body with populated pictureId key.
Firstly change FromBody to FromForm.Then,because you want to save filename to the database,change your code like below:
[HttpPost]
public async Task<ActionResult<Employers>> PostEmployees([FromForm] FileUploadAPI Image, [FromForm]Employers employees)
{
try
{
var filename = await UploadImage(Image, 2, employees.EmployerId);
employees.DisplayImage = filename;
_context.Employers.Add(employees);
await _context.SaveChangesAsync();
var returnInfo = CreatedAtAction("GetEmployees", new { id = employees.EmployerId }, employees);
return returnInfo;
}
catch (Exception ex)
{
return NoContent();
}
}
Your postman should be like below:
Result:

Serializing generic c# model to xml in .net core 3.0

I am using .net core 3.0 to create Web APIs. I want to serialize a generic model to XML. But by default, it serializes the generic model to JSON only.
When I tried to serialize a simple model to XML, it gets serialized to XML properly.
Depending upon the headers passed, I want to serialize the model. Here is what I have done till now:
In startup.cs file:
services.AddControllers(options =>{
options.RespectBrowserAcceptHeader = true; // false by default
}).
AddXmlSerializerFormatters().
Below is the example of code:
My Generic Model:
public class ApiResponseModel<T>
{
public int ErrorCode { get; set; }
public string ErrorMessage { get; set; }
public T Data { get; set; }
public ApiResponseModel(int errorCode, string errorMessage, T data)
{
this.ErrorCode = errorCode;
this.ErrorMessage = errorMessage;
this.Data = data;
}
}
Action to return data:
[HttpGet]
public async Task<IActionResult> Get()
{
IEnumerable<RoleDTO> roleList = null;
roleList = await _repository.RoleRepository.GetAllRolesAsync();
var data = new ApiResponseModel<IEnumerable<RoleDTO>>(200, "Success", roleList);
return Ok(data);
}
This return JSON response though Accept header is specified as application/xml.
But if I simply return roleList it gives the response in XML format.
[HttpGet]
public async Task<IActionResult> Get()
{
IEnumerable<RoleDTO> roleList = null;
roleList = await _repository.RoleRepository.GetAllRolesAsync();
return Ok(roleList);
}
How can I get the XML response for the generic models also?
There're two reasons :
Your ApiResponseModel<T> has no parameterless constructor.
You're using XmlSerializer by AddXmlSerializerFormatters()(instead of XML DataContractSerializer), which will not take care of IEnumerable<T> serialization by default.
To fix this issue, add a parameterless constructor:
public class ApiResponseModel<T>
{
public int ErrorCode { get; set; }
public string ErrorMessage { get; set; }
public T Data { get; set; }
public ApiResponseModel() { } // add a parameterless constructor
public ApiResponseModel(int errorCode, string errorMessage, T data)
{
this.ErrorCode = errorCode;
this.ErrorMessage = errorMessage;
this.Data = data;
}
}
And consider adding a XML DataContractSerializer formatters to take care of IEnumerable<T> serialization:
services.AddControllersWithViews(opts =>{
opts.RespectBrowserAcceptHeader = true;
})
.AddXmlDataContractSerializerFormatters()
.AddXmlSerializerFormatters()

ASP.Net core correct way of implementing http methods for related models

I have tow models Context and Connection as following:
public class Context
{
[Key]
public long ContextId { get; set; }
[Required]
public string Role { get; set; }
public ICollection<Connection> Connections { get; set; }
public Context()
{
}
}
And
public class Connection
{
[Key]
public long ConnectionId { get; set; }
[Required]
public string Name { get; set; }
public long ContextId { get; set; }
public Context Context { get; set; }
public Connection()
{
}
}
So far, I did not create any controller or repository for Connection. ContextRepositiry looks like following:
public class ContextRepository: IContextRepository
{
private readonly WebAPIDataContext _db;
public ContextRepository(WebAPIDataContext db)
{
_db = db;
}
public Context CreateContext(Context context)
{
_db.Contexts.Add(context);
_db.SaveChanges();
return context;
}
public void DeleteContext(long id)
{
Context context = GetContext(id);
if (context != null)
{
_db.Contexts.Remove(context);
_db.SaveChanges();
}
}
public List<Context> GetAllContexts()
{
return _db.Contexts.AsNoTracking().ToList();
}
public Context GetContext(long id)
{
return _db.Contexts.FirstOrDefault(o => o.ContextId == id);
}
public void UpdateContext(long id, Context context)
{
}
}
public interface IContextRepository
{
List<Context> GetAllContexts();
Context GetContext(long id);
Context CreateContext(Context context);
void UpdateContext(long id, Context context);
void DeleteContext(long id);
}
And it's controller:
[Route("api/[controller]")]
public class ContextController : Controller
{
private readonly IContextRepository _contexts;
public ContextController(IContextRepository contexts)
{
_contexts = contexts;
}
[HttpGet("")]
public IActionResult GetAllContexts()
{
try
{
List<Context> contexts = _contexts.GetAllContexts();
return Ok(contexts);
}
catch (EntityNotFoundException<Context>)
{
return NotFound();
}
}
[HttpGet("{id}")]
public IActionResult GetContext(long id)
{
Context context= _contexts.GetContext(id);
if (context == null)
{
return NotFound();
}
return Ok(context);
}
[HttpPost]
public IActionResult CreateContext([FromBody] Context context)
{
if (ModelState.IsValid == false)
{
return BadRequest(ModelState);
}
Context createdContext= _contexts.CreateContext(context);
if (createdContext== null)
{
return NotFound();
}
return CreatedAtAction(
nameof(GetContext), new { id = createdContext.ContextId}, createdContext);
}
[HttpPut("{id}")]
public IActionResult UpdateContext(long id, [FromBody] Context context)
{
if (ModelState.IsValid == false)
{
return BadRequest(ModelState);
}
try
{
_contexts.UpdateContext(id, context);
return Ok();
}
catch (EntityNotFoundException<Context>)
{
return NotFound();
}
}
[HttpDelete("{id}")]
public IActionResult DeleteCOntext(long id)
{
_contexts.DeleteContext(id);
return Ok();
}
}
Question: While creating a context I shouldn't have to enter any connection data i.e. it should be optional (look ta the swagger request bellow). However, on updating a specific context there could be connection data, and corresponding context should be updated accordingly.
Right now, in Swagger for POST if I enter something like:
{
"contextId": 0,
"role": "Employee",
"connections": [
{
"connectionId": 0,
"name": "",
"contextId": 0,
"context": {}
}
]
}
then it says, The Name field is required and The Role field is required (I am trying to send just context data like role and leaving blank connection data- which should be possible). If I remove "connections":[] then it posts with connections set to null, but don't want to remove it from there.