In an ASP.NET Webforms application on .NET 4.0 and a Web API, I attempt to add an order and orderRow to the database, but I get this error:
Error: new transactions are not allowed because there are other running threads in the session
From the client, I run an Ajax post to the controller, which has the following code for inserting a value into the database:
private readonly MyDatabaseEntities _ctx;
public ComponibileController()
{
_ctx = new MyDatabaseEntities(#"xxx");
}
[HttpPost]
public void Post([FromBody] ComponibileCreate model)
{
if (!ModelState.IsValid) return;
var taskWork = System.Threading.Tasks.Task.Run(() => SaveOnDatabase(model, utente));
...query
SendMailToUser(...);
taskWork.Wait();
}
public void SaveOnDatabase(ComponibileCreate model, string utente)
{
try
{
using (_ctx)
{
var ordine = new COM_ORDINI
{
..,
};
foreach (var item in model.Righe.ToList())
{
var righe = new COM_RIGHE
{
...
};
ordine.COM_RIGHE.Add(righe);
}
_ctx.COM_ORDINI.Add(ordine);
_ctx.SaveChanges();
}
}
catch (Exception e)
{
}
}
instead System.Threading.Tasks.Task.Run(() => SaveOnDatabase(model, utente)); , use
async Task SaveOnDatabase(ComponibileCreate model, string utente) {
...
await _ctx.SaveChangesAsync();
}
and call await SaveOnDatabase(model, utente) in action
Related
I'm using Simple.OData.Client to query and update in our crm dynamics system.
But each query, insertion or update takes up to 10 seconds. It works like a charm on postman. That means that the server is not the problem.
Here is my Code:
Base Class
public abstract class CrmBaseDao<T> where T : class
{
protected ODataClient GetClient()
{
return new ODataClient(new ODataClientSettings(BuildServiceUrl())
{
Credentials = new NetworkCredential(Settings.Default.CrmUsername, Settings.Default.CrmPassword),
IgnoreUnmappedProperties = true
});
}
public async Task<IEnumerable<T>> GetAll()
{
var client = GetClient();
return await client.For<T>().FindEntriesAsync();
}
private string BuildServiceUrl()
{
return Settings.Default.CrmBaseUrl + "/api/data/v8.2/";
}
}
Derived class:
public void Insert(Account entity)
{
var task = GetClient()
.For<Account>()
.Set(ConvertToAnonymousType(entity))
.InsertEntryAsync();
task.Wait();
entity.accountid = task.Result.accountid;
}
public void Update(Account entity)
{
var task = GetClient()
.For<Account>()
.Key(entity.accountid)
.Set(ConvertToAnonymousType(entity))
.UpdateEntryAsync();
task.Wait();
}
private object ConvertToAnonymousType(Account entity)
{
return new
{
entity.address1_city,
entity.address1_fax,
entity.address1_line1,
entity.address1_postalcode,
entity.address1_stateorprovince,
entity.he_accountnumber,
entity.name,
entity.telephone1,
entity.telephone2
};
}
public async Task<Account> GetByHeAccountNumber(string accountNumber)
{
return await GetClient().For<Account>()
.Filter(x => x.he_accountnumber == accountNumber)
.FindEntryAsync();
}
The call:
private void InsertIDocsToCrm()
{
foreach (var file in GetAllXmlFiles(Settings.Default.IDocPath))
{
var sapAccountEntity = GetSapAccountEntity(file);
var crmAccountEntity = AccountConverter.Convert(sapAccountEntity);
var existingAccount = crmAccountDao.GetByHeAccountNumber(crmAccountEntity.he_accountnumber);
existingAccount.Wait();
if (existingAccount.Result != null)
{
crmAccountEntity.accountid = existingAccount.Result.accountid;
crmAccountDao.Update(crmAccountEntity);
}
else
crmAccountDao.Insert(crmAccountEntity);
}
}
This whole function takes a very long time (30 sec+)
Is there any chance to speed that up?
Additionaly it does take a lot of memory?!
Thanks
I suspect this might have to do with a size of schema. I will follow the issue you opened on GitHub.
UPDATE. I ran some benchmarks mocking server responses, and processing inside Simple.OData.Client took just milliseconds. I suggest you run benchmarks on a server side. You can also try assigning metadata file references to MetadataDocument property of ODataClientSettings.
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();
I am working on a website created in .NET Core (using the full .NET Framework) that uses background tasks to get a devices list.
I want to display a loading "view" like this while the task is getting data from another PC (using GET requests) and then, when the task is completed I want to display the table with the devices. How can I do that?
Here is a little piece of my code:
public class DeviceController : Controller {
public IActionResult Index() {
if (DataSyncronizer.getDeviceListTask.Status == TaskStatus.Running) {
// TODO Show the loading screen here.
// return this.View("Loading");
}
if (DataSyncronizer.getDeviceListTask.Status == TaskStatus.Faulted) {
ViewData["ErrorTitle"] = "Errore di sincronizzazione";
ViewData["ErrorText"] = "Cannot get devices";
return this.View("Error");
}
if (DataSyncronizer.getDeviceListTask.Status == TaskStatus.Canceled) {
ViewData["ErrorTitle"] = "Errore di sincronizzazione";
ViewData["ErrorText"] = "";
return this.View("Error");
}
return this.View(DataSyncronizer.Devices);
}
And this is the function that gets the device list:
public static class DataSyncronizer {
public static Task<List<Device>> getDeviceListTask { get; private set; }
public static List<Device> Devices = new List<Device>();
public static Task UpdateDevices() {
getDeviceListTask = new Task<List<Device>>(() =>
Device.GetMyDevicesList(meUser));
getDeviceListTask.ContinueWith((result) => {
DataSyncronizer.Devices = result.Result;
}, TaskScheduler.Current);
getDeviceListTask.Start();
return getDeviceListTask;
}
}
You could display the loader right before you call UpdateDevices().
add this to the end of your TASK
.ContinueWith(t => "Function to hide loader");
Example
var webTask = Task.Run(() =>
{
try
{
wcf.UploadMotionDynamicRaw(bytes); //my web service
}
catch (Exception ex)
{
//deal with error
}
}).ContinueWith(t => "Function to hide loader");
Originally my ViewModel had the following:
public ObservableCollection<DataListItem> files { get; private set; }
private object _filesLock = new object();
public MainViewModel(IDataService dataService)
{
files = new ObservableCollection<DataListItem>();
BindingOperations.EnableCollectionSynchronization(files, _filesLock);
_dataService = dataService;
}
One of the commands ran this code
await Task.Run(() => {
Parallel.ForEach(files, new ParallelOptions() { MaxDegreeOfParallelism = 2 }, (file) =>
{
this.files.Add(new DataListItem(file));
});
});
But this doesn't work at design time. So I moved the code into the DataService.
public async void ScanFolder()
{
if (!CanScanFolder()) return;
files.Clear();
await Task.Run(() =>
{
_dataService.GetData(SelectedFolder, filter, IncludeSubs, (myfiles, error) =>
{
if (error != null)
{
return;
}
foreach (var file in myfiles.files)
{
files.Add(file);
}
}
);
});
}
The DataService code looks like this
public async void GetData(string folder, string filter, bool includeSubs, Action<DataItem, Exception> callback)
{
// Use this to connect to the actual data service
var item = new DataItem(folder, filter, includeSubs);
await item.ScanFolderAsync();
callback(item, null);
}
public async Task<bool> ScanFolderAsync()
{
try
{
var ret = new List<DataListItem>();
var files = Directory.EnumerateFiles(folder, filter, includeSubs ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly);
await Task.Run(() => {
Parallel.ForEach(files, new ParallelOptions() { MaxDegreeOfParallelism = 2 }, (file) =>
{
this.files.Add(new DataListItem(file));
});
});
return true;
}
catch (Exception)
{
return false;
}
}
But as far as I can tell there is no continuous communication channel between the DataService and the ViewModel. So with the new approach it reads all of the files and then displays it in the grid, but I want it to display each update as it goes.
My instinct is to add a Messenger within the foreach and subscribe to it in the ViewModel, but there does not seem to be a way to use a messenger within a data service.
How can the DataService send a message to the ViewModel each time it has scanned a file? Alternatively is there a better architecture for loading the data?
It turns out I was just missing the using statement for messenger and it is possible to update the ViewModel using it.
In the ViewModel constructor I added
Messenger.Default.Register<DataListItem>(this, (item) => { files.Add(item); });
and the ScanFolderAsync() method was updated to send the message
using GalaSoft.MvvmLight.Messaging;
public async Task<bool> ScanFolderAsync()
{
try
{
var ret = new List<DataListItem>();
var files = Directory.EnumerateFiles(folder, filter, includeSubs ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly);
await Task.Run(() => {
Parallel.ForEach(files, new ParallelOptions() { MaxDegreeOfParallelism = 1 }, (file) =>
{
var item = new DataListItem(file);
Messenger.Default.Send<DataListItem>(item);
this.files.Add(item);
});
});
return true;
}
catch (Exception)
{
return false;
}
}
I have lots of controllers methods in WebAPI similar to the following:
public IHttpActionResult Delete(int id)
{
var command = new DeleteItemCommand() { Id = id };
try
{
_deleteCommandHandler.Handle(command);
}
catch (CommandHandlerResourceNotFoundException)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
catch(CommandHandlerException)
{
throw new HttpResponseException(HttpStatusCode.InternalServerError);
}
// More catches etc...
return Ok();
}
The command handlers (in this instance _deleteCommandHandler) is injected earlier in the execution and the commands may be built in the method or using WebApi's automatic method.
What I would like to do is to encapsulate the try/catch error handling in a private method and end up with a controller similar to:
public IHttpActionResult Delete(int id)
{
var command = new DeleteItemCommand() { Id = id };
return ExecuteCommand(x => _deleteCommandHandler.Handle(command));
}
I'm not sure what the signature of the private ExecuteCommand method should be though.
I think you can Invoke your action in a method like this:
public IHttpActionResult Delete(int id)
{
return ExecuteCommand(() => {
var command = new DeleteItemCommand() { Id = id };
_deleteCommandHandler.Handle(command);
});
}
private IHttpActionResult ExecuteCommand(Action action)
{
try
{
action.Invoke();
//or: action();
}
catch (CommandHandlerResourceNotFoundException)
{
return HttpResponseException(HttpStatusCode.NotFound);
}
catch (CommandHandlerException)
{
return HttpResponseException(HttpStatusCode.InternalServerError);
}
return Ok();
}
A good reference for HttpResponseException.
I would create a custom error handler filter, and handle all possible errors there in a centralized form. That way you can just throw whatever exception from the action methods, and then they will be caught at the filter where you can handle them and change the response accordingly.
public class NotImplExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
if (context.Exception is NotImplementedException)
{
context.Response = new HttpResponseMessage(HttpStatusCode.NotImplemented);
}
}
}
The example is taken from this article where you can find the concept in more detail.
Here's a solution similar to shA.t's answer, but the exceptions are mapped in a dictionary and the try/catch logic is in an extension method:
public class TestController:ApiController
{
public IHttpActionResult Delete(int id)
{
return ExecuteCommand(() => {
var command = new DeleteItemCommand() { Id = id };
_deleteCommandHandler.Handle(command);
});
}
private IHttpActionResult ExecuteCommand(Action action)
{
return action.SafeInvoke();
}
}
public static class ActionExtensions
{
private static readonly Dictionary<Type, HttpStatusCode> _exceptionToStatusCodeLookup = new Dictionary<Type, HttpStatusCode>
{
{typeof(CommandHandlerResourceNotFoundException), HttpStatusCode.NotFound },
{typeof(CommandHandlerException), HttpStatusCode.InternalServerError },
};
public static IHttpActionResult SafeInvoke(this Action action)
{
try
{
action();
}
catch (Exception ex)
{
var statusCode = _exceptionToStatusCodeLookup.ContainsKey(ex.GetType()) ? _exceptionToStatusCodeLookup[ex.GetType()] : HttpStatusCode.InternalServerError;
return new HttpResponseException(statusCode);
}
return new OkResult();
}
}