run EF6 async request in mvc controller - asp.net-mvc

I have mvc 5 and EF6 and never really use async in mvc before so want ask best way for run sql requests.
public ActionResult SearchTest(string id)
{
string searchtxt = UsefulClass.ConvertObjectToString(id).Replace(",", " ").Trim();
var model = new SearchResult();
if (!String.IsNullOrEmpty(searchtxt))
{
//get the data from 3 requests
var ArtistList = _db.Artists.SqlQuery("SELECT top 6 * FROM Artists WHERE CONTAINS(Name, N'Queen') and TopTracksStatus = 1 and GrabStatus > 1 order by playcount desc").ToList();
var tracks = _db.Database.SqlQuery<TrackInfo>("exec SearchTracks #SearchText=N'Queen', #TopCount=10,#LengthCount=100").ToList();
var TagsList = _db.Tags.Where(x => x.TagName.Contains(searchtxt)).Take(5).ToList();
//work with ArtistList and add to model
if (ArtistList.Any())
{
int i = 0;
foreach (var artist in ArtistList)
{
i++;
if (i == 1) //top artist
{
model.BestArtist = artist;
model.BestArtistTrackCount = _db.TopTracks.Count(x => x.Artist_Id == artist.Id);
}
else
{
model.ArtistList = ArtistList.Where(x => x.Id != model.BestArtist.Id);
break;
}
}
}
//work with tracks and add to model
if (tracks.Any())
{
model.TopTrackList = tracks;
}
//work with tags and add to model
if (TagsList.Any())
{
model.TagList = TagsList;
}
}
return View(model);
}
Here I have 3 requests which return ArtistList, tracks, TagsList and I need add them to model and then pass to view. How to do it in async way?

Try below example, hope it gives you a good idea.
public async Task<ActionResult> SearchTestAsync(string id)
{
string searchtxt = UsefulClass.ConvertObjectToString(id).Replace(",", " ").Trim();
var model = new SearchResult();
if (!String.IsNullOrEmpty(searchtxt))
{
var art = GetArtistListResulst(model);
var track = GetTracksResults(model);
var tag = GetTagsListResults(model,searchtxt);
await Task.WhenAll(art, track, tag);
}
return View(model);
}
private Task GetArtistListResulst(SearchResult model)
{
return Task.Run(() =>
{
var artistList = _db.Artists.SqlQuery("SELECT top 6 * FROM Artists WHERE CONTAINS(Name, N'Queen') and TopTracksStatus = 1 and GrabStatus > 1 order by playcount desc").ToList();
// You don't need to use foreach because the artistList is ordered by playcount already.
model.BestArtist = artistList.Take(1);
model.BestArtistTrackCount = _db.TopTracks.Count(x => x.Artist_Id == model.BestArtist.Id);
model.ArtistList = artistList.Where(x => x.Id != model.BestArtist.Id);
});
}
private Task GetTracksResults(SearchResult model)
{
return Task.Run(() =>
{
model.TopTrackList = _db.Database.SqlQuery<TrackInfo>("exec SearchTracks #SearchText=N'Queen', #TopCount=10,#LengthCount=100").ToList();
});
}
private Task GetTagsListResults(SearchResult model, string searchtxt)
{
return Task.Run(() =>
{
model.TagsList = _db.Tags.Where(x => x.TagName.Contains(searchtxt)).Take(5).ToList();
});
}

Related

asp.net mvc core A second operation was started on the context before the first operation is completed

I am using this code for authorization on controllers.
with [Authorize(Policy = "CustomRole")]
The thing happened that after 3 or 4 request it fails with
A second operation started on this context before a previous operation completed
public class CustomRoleRequirement : AuthorizationHandler<CustomRoleRequirement>, IAuthorizationRequirement
{
public CMSContext _context = new CMSContext();
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, CustomRoleRequirement requirement)
{
var routeobj = context.Resource as Microsoft.AspNetCore.Mvc.Filters.AuthorizationFilterContext;
var c = routeobj.RouteData.Values.Values.ToList();
var keys = routeobj.RouteData.Values.Keys.ToList();
string area = "";
string id = "";
string controller = "";
string action = "";
string module = "";
foreach (var item in keys)
{
if (item=="area")
{
int indexs = keys.FindIndex(cc => cc == "area");
area = c[indexs].ToString();
}
else if (item == "id")
{
int indexs = keys.FindIndex(cc => cc == "id");
id = c[indexs].ToString();
}
else if (item == "controller")
{
int indexs = keys.FindIndex(cc => cc == "controller");
controller = c[indexs].ToString();
}
else if (item == "module")
{
int indexs = keys.FindIndex(cc => cc == "module");
module = c[indexs].ToString();
}
else if (item == "action")
{
int indexs = keys.FindIndex(cc => cc == "action");
action = c[indexs].ToString();
}
}
string modulelink = controller;
if (!string.IsNullOrEmpty(module))
{
modulelink = modulelink + "/" + module;
}
List<string> Roles = new List<string>();
int UserId = Auth.UserID;
string UserName = Auth.UserName;
if (UserName == "superadmin")
{
context.Succeed(requirement);
return Task.CompletedTask;
}
else
{
// apparently the error occurred here
var moduleobj = _context.AppModules.FirstOrDefault(q => q.Link == modulelink);
if (moduleobj != null)
{ // 69 role is assessing news module
//60 role is accessing page module
var RolesModulesobj = _context.AppRolesModules.FirstOrDefault(q => q.ModuleId == moduleobj.ModuleId && q.RolesId == Auth.RoleId);
if (RolesModulesobj != null)
{
string permissionsobj = RolesModulesobj.Permissions;
List<string> PermissionsListobj = permissionsobj.Split(',').Select(x => x.Trim()).ToList();
var FindFullAccess = PermissionsListobj.FirstOrDefault(q => q.Contains("FullAccess:true"));
if (FindFullAccess != null)
{
context.Succeed(requirement);
return Task.CompletedTask;
}
else
{
var abc = PermissionsListobj.FirstOrDefault(q => q.Contains(action + ":true"));
if (abc != null)
{
context.Succeed(requirement);
return Task.CompletedTask;
}
else
{
context.Fail();
return Task.CompletedTask;
}
}
}
}
}
The error occurred at this line above
var moduleobj = _context.AppModules.FirstOrDefault(q => q.Link == modulelink);
How can I make task wait before the second operation started in the method above?
You can't use a singleton DB context. You either create one each time you need one or you pool them.

Check for but allow duplicates entries in the Db

My Db allows duplicate monuments, so the traditional method of checking for duplicates will not work. The user first inputs a monument name, then I run a check against the Db and if no duplicates found, great, allow the user to input the rest of the data. If one of more monuments with the same name are found, display a list of the monuments already in the Db. If this is truly a new monument with the same name, allow the user to input the new monument.
What I have so far:
[Authorize]
public ActionResult MonumentTitle(string battleRecordID, int callingRecordID)
{
ViewBag.monumentBattleRecID = battleRecordID; // ID of the battle
ViewBag.battleName = getBattleName(battleRecordID);
var vModel = new MonumentTitle();
vModel.BattleRecID = ViewBag.monumentBattleRecID;
vModel.BattleName = ViewBag.battleName;
vModel.CallingRecID = callingRecordID;
return View(vModel);
}
[HttpPost]
[Authorize]
public ActionResult MonumentTitle(MonumentTitle monumentTitle)
{
ViewBag.callingRecordID = monumentTitle.CallingRecID;
ViewBag.battleName = monumentTitle.BattleName;
ViewBag.MonumentName = monumentTitle.MonumentName;
var NmonumentTitle = monumentTitle;
ViewBag.battleName1 = NmonumentTitle.BattleName;
var List_monument = from s in db.Monuments
where (s.MonumentStatus == "A" &&
s.MonumentBattleRecID == monumentTitle.BattleRecID &&
s.MonumentName == monumentTitle.MonumentName
)
select s;
List_monument = List_monument.OrderBy(s => s.MonumentName);
var vModel = new MonumentTitleDuplicate();
vModel.MonumentTitle = NmonumentTitle;
vModel.Monument = List_monument.ToList();
if (vModel.Monument.Count == 0)
{
return RedirectToAction("MonumentCreate", new { battleRecord = vModel.MonumentTitle.BattleRecID, callingRecordID = vModel.MonumentTitle.CallingRecID, monumentName = vModel.MonumentTitle.MonumentName });
}
{
return RedirectToAction("MonumentTitleDuplicate", vModel );
}
}
Works for when there is not a duplicate. Where I run into a problem is trying to switch control to "MonumentTitleDuplicate" and pass the model.
[Authorize]
[HttpGet]
public ViewResult MonumentTitleDuplicate(MonumentTitleDuplicate monumentTitleDuplicate)
{
return View("MonumentTitleDuplicate", monumentTitleDuplicate);
}
[HttpPost]
[Authorize]
public ActionResult MonumentTitleDuplicate(MonumentTitle monumentTitle)
{
ViewBag.callingRecordID = monumentTitle.CallingRecID;
return RedirectToAction("MonumentCreate", new { battleRecord = monumentTitle.BattleRecID, callingRecordID = monumentTitle.CallingRecID, monumentName = monumentTitle.MonumentName });
}
[Authorize]
public ActionResult MonumentCreate(string battleRecord, int callingRecordID, string monumentName)
{
Got it -- needed to use TempData to pass the model to the new controller so:
[Authorize]
public ActionResult MonumentTitle(string battleRecordID, int callingRecordID)
{
ViewBag.monumentBattleRecID = battleRecordID; // ID of the battle
ViewBag.battleName = getBattleName(battleRecordID);
var vModel = new MonumentTitle();
vModel.BattleRecID = ViewBag.monumentBattleRecID;
vModel.BattleName = ViewBag.battleName;
vModel.CallingRecID = callingRecordID;
return View(vModel);
}
[HttpPost]
[Authorize]
public ActionResult MonumentTitle(MonumentTitle monumentTitle)
{
ViewBag.callingRecordID = monumentTitle.CallingRecID;
ViewBag.battleName = monumentTitle.BattleName;
ViewBag.MonumentName = monumentTitle.MonumentName;
var NmonumentTitle = monumentTitle;
ViewBag.battleName1 = NmonumentTitle.BattleName;
var List_monument = from s in db.Monuments
where (s.MonumentStatus == "A" &&
s.MonumentBattleRecID == monumentTitle.BattleRecID &&
s.MonumentName == monumentTitle.MonumentName
)
select s;
List_monument = List_monument.OrderBy(s => s.MonumentName);
var vModel = new MonumentTitleDuplicate();
vModel.MonumentTitle = NmonumentTitle;
vModel.Monument = List_monument.ToList();
if (vModel.Monument.Count == 0)
{
return RedirectToAction("MonumentCreate", new { battleRecord = vModel.MonumentTitle.BattleRecID, callingRecordID = vModel.MonumentTitle.CallingRecID, monumentName = vModel.MonumentTitle.MonumentName });
}
{
TempData["monumentTitleDuplicate"] = vModel;
return RedirectToAction("MonumentTitleDuplicate");
}
}
[Authorize]
public ActionResult MonumentTitleDuplicate()
{
MonumentTitleDuplicate monumentTitleDuplicate = TempData["MonumentTitleDuplicate"] as MonumentTitleDuplicate;
return View(monumentTitleDuplicate);
}
[HttpPost]
[Authorize]
public ActionResult MonumentTitleDuplicate(MonumentTitle monumentTitle)
{
ViewBag.callingRecordID = monumentTitle.CallingRecID;
return RedirectToAction("MonumentCreate", new { battleRecord = monumentTitle.BattleRecID, callingRecordID = monumentTitle.CallingRecID, monumentName = monumentTitle.MonumentName });
}
Probably have some cleanup work to do (like getting rid of the stuff where I was trying to use ViewBag), but does appear to be working.

Delete a context record from grid in kendo UI using LINQ

Hi Im using kendo ui grid in my project.
This is my code to insert records in database.
public static void Insert(StudentViewModel student)
{
student.StudentId = All().OrderByDescending(p => p.StudentId).First().StudentId + 1;
//All().Insert(0, student);
UniRegEntities uniRegEntities = new UniRegEntities();
Student stu =new Student();
stu.FName = student.FirstName;
stu.LName = student.LastName;
stu.Gender = uniRegEntities.Genders.Where(x => x.Title == student.Gender).FirstOrDefault();
stu.Id = student.StudentId;
uniRegEntities.Students.Add(stu);
uniRegEntities.SaveChanges();
}
And this is my update statement.
public static void Update(StudentViewModel student)
{
UniRegEntities context = new UniRegEntities();
var studentToUpdate = context.Students.Where(x => x.Id == student.StudentId).FirstOrDefault();
studentToUpdate.FName = student.FirstName;
studentToUpdate.LName = student.LastName;
studentToUpdate.Gender = context.Genders.Where(x => x.Title == student.Gender).FirstOrDefault();
context.SaveChanges();
}
Anyone can suggest me the delete method?
You can either get an entity from the DB and then delete it or create one and then delete it.
So:
var e = // Get
ctx.DeleteObject(e);
ctx.SaveChanges();
or
var e = new Foo() { FooId = id };
ctx.Entity.Attach(e);
ctx.DeleteObject(e);
ctx.SaveChanges();
Applied to your situation:
You are getting a record so you want to use DeleteObject()
public static void Update(StudentViewModel student)
{
UniRegEntities context = new UniRegEntities();
var studentToDelete = context.Students.Where(x => x.Id == student.StudentId).FirstOrDefault();
context.Students.DeleteObject(studentToUpdate);
context.SaveChanges();
}
context.Students.Remove(context.students.Single(x=>x.Id==student.Id));
Can you please try with below code snippet?
using (var db= new AppContext(ConnectionStr))
{
try
{
con.Configuration.AutoDetectChangesEnabled = false;
var o = new Student { StudentId = student.StudentId };
db.Students.Attach(o);
db.Students.Remove(o);
db.SaveChanges();
}
catch (Exception ex)
{
throw new Exception(ex.InnerException.Message);
}
finally
{
con.Configuration.AutoDetectChangesEnabled = true;
}
}

How can I update related tables?

My project involves creating a new hotel room and 2 tables in my database will update. My tables are called RoomType and RoomFacility.
I can successfully update RoomType, but when I try to update RoomFacility and use RoomTypeID to make a new room facility, it fails. I always get 1 for my RoomFacilityID.
How can I update data for both tables, roomType and RoomFacility?
This is the code for my service to update my database
public void UpdateFacilityInRooms(List<int> FacilityIDs, int RoomTypeID)
{
List<HotelRoomFacility> hotelRoomFacilities =
_HotelRoomFacilityRopository.AsQueryable()
.Where(f => f.RoomTypeID == RoomTypeID).ToList();
foreach (int newRoomFacility in FacilityIDs)
{
if (hotelRoomFacilities.Where(h => h.RoomFacilityID == newRoomFacility).Count() == 0)
{
HotelRoomFacility facility = new HotelRoomFacility
{
RoomFacilityID = newRoomFacility,
RoomTypeID = RoomTypeID
};
_HotelRoomFacilityRopository.Add(facility);
}
}
_HotelRoomFacilityRopository.CommitChanges();
}
public RoomType NewRoom(int HotelID,int? RoomTypeID,
string RoomTypeName, string RoomTypeDescription)
{
RoomType room = new RoomType();
room.HotelID = HotelID;
room.RoomTypeID = RoomTypeID ?? 0;
room.RoomtypeName = RoomTypeName;
room.RoomTypeDescripton = RoomTypeDescription;
_RoomTypeRepository.Add(room);
_RoomTypeRepository.CommitChanges();
return room;
}
public RoomType UpdateRoom(int RoomTypeID, string RoomTypeName, string RoomTypeDescription, List<int> RoomFacilityIDs)
{
RoomType roomType = (from rt in _RoomTypeRepository.AsQueryable().Include(r => r.HotelRoomFacilities)
where rt.RoomTypeID == RoomTypeID
select rt).FirstOrDefault();
if (roomType == null)
return null;
roomType.RoomTypeName = RoomTypeName;
roomType.RoomTypeDescripton = RoomTypeDescription;
//Add New Room facilities
List<HotelRoomFacility> hotelRoomFacilities = _HotelRoomFacilityRopository.AsQueryable().Where(f => f.RoomTypeID == RoomTypeID).ToList();
foreach (int newRoomFacilityID in RoomFacilityIDs)
{
if (roomType.HotelRoomFacilities.Where(h => h.RoomFacilityID == newRoomFacilityID).Count() == 0)
{
roomType.HotelRoomFacilities.Add(new HotelRoomFacility
{
RoomFacilityID = newRoomFacilityID
});
}
}
foreach (HotelRoomFacility roomFacility in hotelRoomFacilities)
{
if (RoomFacilityIDs.Contains(roomFacility.RoomFacilityID) == false)
_HotelRoomFacilityRopository.Delete(roomFacility);
}
_RoomTypeRepository.Attach(roomType);
_RoomTypeRepository.CommitChanges();
return roomType;
}

ASP.NET MVC AsyncController Exception Handling

On ASP.NET MVC, I try to write an async Controller action with the old asynchronous programming model (actually, it is the current one, new one is still a CTP).
Here, I am trying to run 4 operations in parallel and it worked great. Here is the complete code:
public class SampleController : AsyncController {
public void IndexAsync() {
AsyncManager.OutstandingOperations.Increment(4);
var task1 = Task<string>.Factory.StartNew(() => {
return GetReponse1();
});
var task2 = Task<string>.Factory.StartNew(() => {
return GetResponse2();
});
var task3 = Task<string>.Factory.StartNew(() => {
return GetResponse3();
});
var task4 = Task<string>.Factory.StartNew(() => {
return GetResponse4();
});
task1.ContinueWith(t => {
AsyncManager.Parameters["headers1"] = t.Result;
AsyncManager.OutstandingOperations.Decrement();
});
task2.ContinueWith(t => {
AsyncManager.Parameters["headers2"] = t.Result;
AsyncManager.OutstandingOperations.Decrement();
});
task3.ContinueWith(t => {
AsyncManager.Parameters["headers3"] = t.Result;
AsyncManager.OutstandingOperations.Decrement();
});
task4.ContinueWith(t => {
AsyncManager.Parameters["headers4"] = t.Result;
AsyncManager.OutstandingOperations.Decrement();
});
task3.ContinueWith(t => {
AsyncManager.OutstandingOperations.Decrement();
}, TaskContinuationOptions.OnlyOnFaulted);
}
public ActionResult IndexCompleted(string headers1, string headers2, string headers3, string headers4) {
ViewBag.Headers = string.Join("<br/><br/>", headers1, headers2, headers3, headers4);
return View();
}
public ActionResult Index2() {
ViewBag.Headers = string.Join("<br/><br/>", GetReponse1(), GetResponse2(), GetResponse3(), GetResponse4());
return View();
}
}
And these below ones are the methods that the async operations are running:
string GetReponse1() {
var req = (HttpWebRequest)WebRequest.Create("http://www.twitter.com");
req.Method = "HEAD";
var resp = (HttpWebResponse)req.GetResponse();
return FormatHeaders(resp.Headers);
}
string GetResponse2() {
var req2 = (HttpWebRequest)WebRequest.Create("http://stackoverflow.com");
req2.Method = "HEAD";
var resp2 = (HttpWebResponse)req2.GetResponse();
return FormatHeaders(resp2.Headers);
}
string GetResponse3() {
var req = (HttpWebRequest)WebRequest.Create("http://google.com");
req.Method = "HEAD";
var resp = (HttpWebResponse)req.GetResponse();
return FormatHeaders(resp.Headers);
}
string GetResponse4() {
var req = (HttpWebRequest)WebRequest.Create("http://github.com");
req.Method = "HEAD";
var resp = (HttpWebResponse)req.GetResponse();
return FormatHeaders(resp.Headers);
}
private static string FormatHeaders(WebHeaderCollection headers) {
var headerStrings = from header in headers.Keys.Cast<string>()
select string.Format("{0}: {1}", header, headers[header]);
return string.Join("<br />", headerStrings.ToArray());
}
I have also Index2 method here which is synchronous and does the same thing.
I compare two operation execution times and there is major difference (approx. 2 seconds)
But I think I am missing lots of things here (exception handling, timeouts, etc). I only implement the exception handling on task3 but I don't think it is the right way of doing that.
What is the healthiest way of handling exceptions for this kind of operations?
Before accessing the result of a task you should test whether it completed successfully. If there was an error you should not try to access the results but instead do something with the exception:
task1.ContinueWith(t =>
{
if (!t.IsFaulted)
{
AsyncManager.Parameters["headers1"] = t.Result;
}
else if (t.IsFaulted && t.Exception != null)
{
AsyncManager.Parameters["error"] = t.Exception;
}
AsyncManager.OutstandingOperations.Decrement();
});

Resources