I have a simple api method to insert data to db, the method in postman man is showing success, but the data is not inserted in database, here is my code
private Utilities uti = new Utilities();
private readonly ApplicationDBContext db;
public AppraisalController(ApplicationDBContext context)
{
db = context;
}
//INSERT API FOR AppraisalIdentity table
[AllowAnonymous]
[Route("api/appraiseinsert")]
[HttpPost]
public IActionResult Create([FromBody] AppraisalIdentity cre)
{
if (cre == null)
{
return BadRequest();
}
using (var transaction = db.Database.BeginTransaction())
{
try
{
#region Appraisal Insert
var apprais = new AppraisalIdentity
{
AppraisalName = cre.AppraisalName,
IsCurrent = cre.IsCurrent,
CompanyID = cre.CompanyID,
DateAdded = cre.DateAdded
};
db.AppraisalIdentity.Add(apprais);
db.SaveChanges();
#endregion
}
catch (Exception ex)
{
transaction.Rollback();
return Json(new
{
statusCode = ex.Message
});
}
}
return Json(new
{
statusCode = "Success"
});
}
I don't know, maybe there's an error somewhere in my code that I don't know about, but the fact is that in postman, the api is returning success, but its not inserting anything in db. Thanks
Since you are using a transaction i guess you need to commit the transaction after calling db.SaveChanges() like this:
db.SaveChanges();
transaction.Commit();
Related
I am using .net core C#, WebApi & AngularJs.
For saving data my Angularjs code makes a $http call to my WebApi. I can return single data from my api fine but not sure whats the best method to return multiple values here. I can make it comma separated and then return as well, but wanted to know if there is a better approach to this.
So basically when the API saves data to my db, I want to return a variable, boolean value if the save was successful and an exception message in case the save was not successfully. Below is my code.
AngularJs Code:
service.saveData(data).then(function (res) {
//get someDataToReturn, dataSaved & exception raised if any from db save here.
}, function (err) {
});
WebApi Code:
[HttpPost("data/save")]
public async Task<IActionResult> SaveData([FromBody] List<UserData> data)
{
bool dataSaved = true;
string someDataToReturn = string.Empty;
//do some processing and updating someDataToReturn here
//Saving data to DB
dataSaved = SaveData(data);
//I want to return someDataToReturn, dataSaved(true or false) and exception raised from SaveData if any
return Ok(someDataToReturn);
}
//DB Call to save data
public bool SaveData(List<UserData> data)
{
try
{
foreach (var set in data)
{
//creating query etc
_db.Execute(query);
}
return true;
}
catch (SqlException ex)
{
}
return false;
}
Let me know the best approach for this.
First of, you should check if the values in your request body is correctly populated.
Take a look at DataAnnotations.
You can use annotations to specify which properties in your model that are Required, Min and Maxlength etc.
Here's an example on how to define a Name property to be required on the UserData class
public class UserData
{
[Required]
public string Name { get; set; }
}
If the request model do not fulfill the specified rules set on the UserData class DataAnnotations, the context ModelState will be set to false and contain the DataAnnotations errors.
This can be used to determind if the current request is a bad request and return a proper http status code from that.
[HttpPost("data/save")]
public async Task<IActionResult> SaveData([FromBody] List<UserData> data)
{
if (!ModelState.IsValid)
return BadRequest(ModelState); //will return a 400 code
...
Then regarding the SaveData method. Capture the exception in the controller and return a proper status code from there
[HttpPost("data/save")]
public async Task<IActionResult> SaveData([FromBody] List<UserData> data)
{
if (!ModelState.IsValid)
return BadRequest(ModelState); //400 status code
try
{
SaveData(data);
}
catch(Exception e)
{
return InternalServerError(e); //500 status code
}
string someDataToReturn = string.Empty;
return Ok(someDataToReturn ); //200 status code
}
public void SaveData(List<UserData> data)
{
foreach (var set in data)
{
//creating query etc
_db.Execute(query);
}
}
You can use the Controller class method Json(object data). Something like:
[HttpPost("data/save")]
public async Task<IActionResult> SaveData([FromBody] List<UserData> data)
{
return this.Json(SaveData(data));
}
See this.
you can create an entity and return it
public class BaseResult{
public bool Result{get;set;}
public string Errors{get;set;}
}
or only
return Ok( new { result = dataSaved , error= exception.Message});
the standard way is:
return 201 status code
https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/201
[HttpPost("data/save")]
public async Task<IHttpActionResult> SaveData([FromBody] List<UserData> data)
{
try
{
if (!ModelState.IsValid)
return BadRequest(ModelState);
// return response of 201 if you created the resource successfully
// typically return this with a uri to the new resource
return Created("location", saveData(data));
}
catch (Exception)
{
return InternalServerError();
}
}
I have a problem when calling API for update and savechanges() is not working (the data is not update).
However, when I add Thread.Sleep(1000); the data update correctly.
Working Methods
public async Task<ResponseBaseModel> AddOrderRemark2(AddOrderRemarkRequestModel model)
{
try
{
using (ChatEntities context = new ChatEntities(CurrentUsername))
{
List<string> statusList = getPendingStatus(context).Result;
OrderHeader orderHeader = getOrderHerderByOrderCode(context, model.OrderCode, model.SalesChannelId).Result;
if (statusList.Contains(orderHeader.Status))
{
if (orderHeader != null)
{
Thread.Sleep(1000);
orderHeader.Remark = model.Remark;
context.DBEntry(orderHeader, EntityState.Modified);
context.SaveChanges();
}
}
}
return new ResponseBaseModel(MessageCode.OK);
}
catch (Exception ex)
{
return new ResponseBaseModel(MessageCode.Fail, ex.InnerException.Message);
}
}
Fail Methods
public async Task<ResponseBaseModel> AddOrderRemark2(AddOrderRemarkRequestModel model)
{
try
{
using (ChatEntities context = new ChatEntities(CurrentUsername))
{
List<string> statusList = getPendingStatus(context).Result;
OrderHeader orderHeader = getOrderHerderByOrderCode(context, model.OrderCode, model.SalesChannelId).Result;
if (statusList.Contains(orderHeader.Status))
{
if (orderHeader != null)
{
orderHeader.Remark = model.Remark;
context.DBEntry(orderHeader, EntityState.Modified);
context.SaveChanges();
}
}
}
return new ResponseBaseModel(MessageCode.OK);
}
catch (Exception ex)
{
return new ResponseBaseModel(MessageCode.Fail, ex.InnerException.Message);
}
}
Edit
I have realise that there are two APIs call at the same times from client sides. Moreover, these two APIs update on the same table 'OrderHeader' which contain both receiver info and remark that why it causes this issue!!. How can I prevent this issue guys?
[HttpPost]
[ActionName("AddReceiverAddress")]
[ChatAuthentication]
public async Task<ResponseBaseModel> AddReceiverAddress(AddReceiverAddressRequestModel model)
{
return _orderService.Value.AddReceiverAddress(model).Result;
}
[HttpPost]
[ActionName("AddOrderRemark")]
[ChatAuthentication]
public async Task<ResponseBaseModel> AddOrderRemark(AddOrderRemarkRequestModel model)
{
return _orderService.Value.AddOrderRemark(model).Result;
}
You are not using async properly. Try this instead
public async Task<ResponseBaseModel> AddOrderRemark2(AddOrderRemarkRequestModel model)
{
try
{
using (ChatEntities context = new ChatEntities(CurrentUsername))
{
List<string> statusList = await getPendingStatus(context);
OrderHeader orderHeader = await getOrderHerderByOrderCode(context, model.OrderCode, model.SalesChannelId);
When you call this method, did you await or Wait() for a result?
When you call the method you have to do either one of them as below sample.
await AddOrderRemark2(model);
Or
AddOrderRemark2(model).Wait();
public async Task<ActionResult> Index()
{
var service = new CoreServiceFactory().GetImpersonatingService();
try
{
var data = new Impersonation()
{
ImpersonatingId = "dac733c3-01ad-447b-b0df-3a7c21fef90b",
UserId = "dac733c3-01ad-447b-b0df-3a7c21fef90b"
};
var imp = await service.Add(data);
}catch(Exception ex) { throw ex; }
return View();
}
Above is one of my controllers action method. And this works fine when the insertion is successful. This should fail if the data already exists in database(unique constraints). So when i intentionally try to make it fail(i manually add the same record in the db and then try to add it again via this action method) the action method goes into a loop or something, the exception is never thrown , chrome keeps me showing me the loading icon , looks like it went into some deadlock state. Can someone please help me understand why it goes into deadlock state when exception is thrown and how can i handle it?
Below are the reference methods
service.Add(data)
public async Task<Impersonation> Add(Impersonation t)
{
if (ValidateData(t))
{
using (var uow = GetUnitOfWork())
{
var r = GetRepository(uow);
var item = r.Add(t);
try
{
var ret = await uow.Save();
if (ret > 0)
{
return item;
}
else
{
return null;
}
}
catch (Exception ex)
{
throw ex;
}
}
}
else
{
throw new ValidationException(null, "error");
}
}
uow.Save()
public class BaseUnitOfWork : IUnitOfWork
{
public DbContext _Context { get; private set; }
public BaseUnitOfWork(DbContext context)
{
this._Context = context;
}
public async Task<int> Save()
{
try
{
var ret = await this._Context.SaveChangesAsync();
return ret;
}catch(Exception ex)
{
throw ex;
}
}
}
Here is my suggestion: in uow.Save, log the error in the catch block and return zero (do not throw any exceptions).
public class BaseUnitOfWork : IUnitOfWork
{
public DbContext _Context { get; private set; }
public BaseUnitOfWork(DbContext context)
{
this._Context = context;
}
public async Task<int> Save()
{
try
{
var ret = await this._Context.SaveChangesAsync();
return ret;
}catch(Exception ex)
{
// log the error here
return 0;
}
}
}
I'm not sure if returning the null in the Add service is a good idea or not, you might need to handle that differently too.
I am creating an API that will enable CRUD functionality to Create, Read, Update, and Delete record in database.
I am running into an issue that is not allowing me to update entry inside a table due to an instance is shared and I get below error.
cannot be tracked because another instance of this type with the same key is already being tracked. For new entities consider using an IIdentityGenerator to generate unique key values."
Here is my code:
[HttpPut("{id}")]
public JsonResult Put(int Id, [FromBody]PackageVersion updatePackage)
{
try
{
if (ModelState.IsValid)
{
if (updatePackage == null || updatePackage.Id != Id)
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json(new { status = "Bad Request" });
}
var package = _respository.GetPackageById(Id);
if (package == null)
{
Response.StatusCode = (int)HttpStatusCode.NotFound;
return Json(new { status = "Not Found", Message = package });
}
_respository.UpdatePackage(updatePackage);
if (_respository.SaveAll())
{
Response.StatusCode = (int)HttpStatusCode.Accepted;
return Json(updatePackage);
}
}
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json(new { status = "Failed", ModelState = ModelState });
}
catch (Exception ex)
{
Response.StatusCode = (int)HttpStatusCode.BadRequest;
return Json(new { status = "Failed", Message = ex.Message });
}
}
In the above code you will notice I am first getting a record by using repository _repository.GetPackageById(Id), which allows me to validate that a record is in database and I can continue to update by using _repository.UpdatePackage(updatePackage) repository. If I comment out below code in my controller, I am able to save the data in the database.
//var package = _respository.GetPackageById(Id);
//if (package == null)
//{
// Response.StatusCode = (int)HttpStatusCode.NotFound;
// return Json(new { status = "Not Found", Message = package });
//}
I also made sure that I was using AddScoped in startup configuration as mentioned in this thread.
services.AddScoped<IAutomationRepository, AutomationRepository>();
I am not sure why I cant use multiple DBContext instances for same ID when it is called.
Any suggestion is really appreciated. :)
UPDATE 1:
public class AutomationRepository : IAutomationRepository
{
private AutomationDBContext _context;
public AutomationRepository(AutomationDBContext context)
{
_context = context;
}
public void AddPackage(PackageVersion newPackage)
{
_context.Add(newPackage);
}
public void DeletePackage(int id)
{
var package = _context.PackageVersions.SingleOrDefault(p => p.Id == id);
_context.PackageVersions.Remove(package);
}
public IEnumerable<PackageVersion> GetAllPackages()
{
return _context.PackageVersions.OrderBy(p => p.PackageName).ToList();
}
public object GetPackageById(int id)
{
return _context.PackageVersions.SingleOrDefault(p => p.Id == id);
}
public bool SaveAll()
{
return _context.SaveChanges() > 0;
}
public void UpdatePackage(PackageVersion updatePackage)
{
_context.Update(updatePackage);
}
Change your repository method like below. This may help. Let us know what happens
public object GetPackageById(int id)
{
return _context.PackageVersions.AsNoTracking().SingleOrDefault(p => p.Id ==id);
}
I have created a documentDB on Azure and can successfully create and get documents.
However, whilst documents are successfully created in the DB, I am not able to use the response from CreateDocumentAsync. The code immediately returns to the calling method on the controller. So the debug line is never reached.
Moreover I am setting the id to a guid, but the Document that is returned to the controller has an Id of 1.
Controller
[HttpPost]
[Route("")]
public IHttpActionResult CreateNewApplication(dynamic data)
{
if (data == null)
{
return BadRequest("data was empty");
}
try
{
var doc = _applicationResource.Save(data);
return Ok(doc.Id); //code hits this point and 'returns'
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
Resource
public async Task<Document> Save(dynamic application)
{
Document created;
using (Client)
{
application.id = Guid.NewGuid();
var database = await RetrieveOrCreateDatabaseAsync(Database);
var collection = await RetrieveOrCreateCollectionAsync(database.SelfLink, CollectionName);
//persist the documents in DocumentDB
created = await Client.CreateDocumentAsync(collection.SelfLink, application);
}
Debug.WriteLine("Application saved with ID {0} resourceId {1}", created.Id, created.ResourceId);
return created;
}
Get requests return data as expected:
[HttpGet]
[Route("{id}")]
public IHttpActionResult GetApplication(string id)
{
var application = _applicationResource.GetById(id);
return Ok(application);
}
That's because you're not awaiting an asynchronous method:
This:
var doc = _applicationResource.Save(data);
Needs to be:
var doc = await _applicationResource.Save(data);
Your method should look as follows:
[HttpPost]
[Route("")]
public async Task<IHttpActionResult> CreateNewApplication(dynamic data)
{
if (data == null)
{
return BadRequest("data was empty");
}
try
{
var doc = await _applicationResource.Save(data);
return Ok(doc.Id); //code hits this point and 'returns'
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}