I have the following API call to retrieve page data
List<VillageNewsItem> newsList = pageRetriever.RetrieveAsync<VillageNewsItem>(
query => query
.Path("/Home/Village-News", PathTypeEnum.Children)
.Published(true)
.OnSite(SiteContext.CurrentSiteName)
.OrderByDescending(x => x.DocumentCreatedWhen)
)?.Result?.ToList();
It works fine and return 2 records if I run the query on page load. Inside Index action of the controller.
public VillageNewsListController(IPageDataContextRetriever dataRetriever, VillageNewsListRepository villageNewsListRepository,
IPageRetriever pagesRetriever, IPageDataContextRetriever pageDataContextRetriever, IPageUrlRetriever pageUrlRetriever)
{
this._dataRetriever = dataRetriever;
this._villageNewsListRepository = villageNewsListRepository;
this._pagesRetriever = pagesRetriever;
this.pageDataContextRetriever = pageDataContextRetriever;
this.pageUrlRetriever = pageUrlRetriever;
}
public async Task<ActionResult> Index(CancellationToken cancellationToken)
{
try
{
List<VillageNewsItem> newsList = pagesRetriever.RetrieveAsync<VillageNewsItem>(
query => query
.Path("/Home/Village-News", PathTypeEnum.Children)
.Published(true)
.OnSite(SiteContext.CurrentSiteName)
.OrderByDescending(x => x.DocumentCreatedWhen)
)?.Result?.ToList();
newsItems.VillageNewsItems = newsList;
return View(newsItems);
}
catch (Exception ex)
{
ErrorHandler.EventLog.LogError(ex.Source, ex.Message, ex.StackTrace);
return RedirectToAction("ErrorPage", "Error");
}
}
However, if I try to make the same API call via a client side AJAX call, it doesn't work and return 0 records. Why it's not working with Ajax calls?
Ajax call
function loadMoreNews() {
$.ajax({
url: '/VillageNewsList/VillageNewsItemList',
//data: { "term": request.term },
type: "POST",
success: function (data) {
response($.map(data,
function (item) {
console.log(data);
}));
},
error: function (response) {
//alert(response.responseText);
},
failure: function (response) {
// alert(response.responseText);
}
});
}
Server side method.
[HttpPost]
[Route("VillageNewsList/VillageNewsItemList")]
public VillageNewsListViewModel VillageNewsItemList(string NodeAliasPath = "", int villageId = 0, string state = "", int page = 1, int pageSize = 4)
{
try
{
List<VillageNewsItem> newsList = pagesRetriever.RetrieveAsync<VillageNewsItem>(
query => query
.Path("/Home/Village-News", PathTypeEnum.Children)
.Published(true)
.OnSite(SiteContext.CurrentSiteName)
.OrderByDescending(x => x.DocumentCreatedWhen)
)?.Result?.ToList();
var model = new VillageNewsListViewModel
{
VillageNewsItems = newsList, // returns 0 records
};
return model;
}
catch (Exception ex)
{
ErrorHandler.EventLog.LogError(ex.Source, ex.Message, ex.StackTrace);
//return RedirectToAction("ErrorPage", "Error");
}
return null;
}
Couple things I see.
You're calling IPageRetriever.RetrieveAsync, but you aren't putting an await before it. There may be some odd behavior due to this. Get rid of the ?.Result?.ToList() and instead just put await before it, it will return an IEnumerable of the specified type.
You don't need ".Published" nor "OnSite" with IPageRetriever, this API automatically uses the Current Site Context, the current culture, and either Published or not / Latest Version or not based on if it's in edit/preview mode or not.
See if those things fix the issue!
I also asume it is caused by async context here...
You can try to use a document query instead.
Would be something like this:
var items = new DocumentQuery<VillageNewsItem>(
.Path("/Home/Village-News", PathTypeEnum.Children)
.PublishedVersion()
.Published()
.OnCurrentSite()
.OrderByDescending(x => x.DocumentCreatedWhen))
?.Result
?.ToList();
If you have multiple cultures, add the culture to your query, too.
.Culture(LocalizationContext.CurrentCulture.CultureCode)
Related
We have an issue that occurs at every method call for limited periods of time. Then it works as expected. The issue is that the code produces double WHERE clauses.
We're using Servicestack 4.5.14
The method we have:
protected static void InsertOrUpdate<T>(
IDbConnection connection,
T item,
Expression<Func<T, bool>> singleItemPredicate,
Expression<Func<T, object>> updateOnlyFields = null)
{
var type = item.GetType();
var idProperty = type.GetProperty("Id");
if (idProperty == null)
{
throw new Exception("Cannot insert or update on a class with no ID property");
}
var currentId = (int)idProperty.GetValue(item);
if (currentId != 0)
{
throw new Exception("Cannot insert or update with non-zero ID");
}
var query = connection.From<T>().Where(singleItemPredicate).WithSqlFilter(WithUpdateLock);
T existingItem;
try
{
existingItem = connection.Select(query).SingleOrDefault();
Log.Verbose(connection.GetLastSql);
}
catch (SqlException)
{
Log.Verbose(connection.GetLastSql);
throw;
}
if (existingItem == null)
{
Insert(connection, item);
return;
}
var existingId = (int)idProperty.GetValue(existingItem);
idProperty.SetValue(item, existingId);
try
{
var affectedRowCount = connection.UpdateOnly(item, onlyFields: updateOnlyFields, where: singleItemPredicate);
Log.Verbose(connection.GetLastSql);
if (affectedRowCount != 1)
{
throw new SwToolsException("Update failed");
}
}
catch (SqlException)
{
Log.Verbose(connection.GetLastSql);
throw;
}
}
When it all works, an example output from the logs could be:
SELECT "Id", "Application", "Hostname", "LastContact", "Version", "ToolState", "ServerState"
FROM "ca"."ExecutionHost"
WITH (UPDLOCK) WHERE ("Hostname" = #0)
UPDATE "ca"."ExecutionHost" SET "LastContact"=#LastContact, "Version"=#Version, "ToolState"=#ToolState, "ServerState"=#ServerState WHERE ("Hostname" = #0)
When it fails, the output (same session, only seconds later) was:
SELECT "Id", "Application", "Hostname", "LastContact", "Version", "ToolState", "ServerState"
FROM "ca"."ExecutionHost"
WITH (UPDLOCK) WHERE ("Hostname" = #0)
UPDATE "ca"."ExecutionHost" SET "LastContact"=#LastContact, "Version"=#Version, "ToolState"=#ToolState, "ServerState"=#ServerState WHERE "LastContact"=#LastContact, "Version"=#Version, "ToolState"=#ToolState, "ServerState"=#ServerState WHERE ("Hostname" = #0)
Marked in bold is the addition to the SQL that makes the call to fail. It seems that it adds an additional WHERE clause with the content from the SET clause.
We've been debugging this for a while and don't really know if the issue is on "our" side or in Servicestack.
Any ideas on where to continue?
I am getting an invalid Type exception when trying to utilize PromptDialog.Choice.
Here is my code from on of my dialogs:
public async Task StartAsync(IDialogContext context) {
await context.PostAsync(ConversationHelper.CreateReschedulePromptMessage());
context.Wait(MessageReceivedAsync);
}
public virtual async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> result) {
var message = await result;
var Options = new[] { "Location", "Date and Time", "Both" };
if (message.Text.ToUpper().CompareTo("PICKUP") == 0) {
_rescheduleType = "pickup";
string prompt = string.Format("Is the {0} location incorrect, is the date and time incorrect, or both?", _rescheduleType);
PromptDialog.Choice(context, OnResumeFromRescheduleChoice, Options, prompt, promptStyle: PromptStyle.Auto, descriptions: Options);
}
else if (message.Text.ToUpper().CompareTo("DROP") == 0) {
_rescheduleType = "drop-off";
string prompt = string.Format("Is the {0} location incorrect, is the date and time incorrect, or both?", _rescheduleType);
PromptDialog.Choice(context, OnResumeFromRescheduleChoice, Options, prompt, promptStyle: PromptStyle.Auto, descriptions: Options);
}
else {
await context.PostAsync(ConversationHelper.CreateGenericRescheduleMessage(SUPPORTNUMBER));
}
context.Done<object>(null);
}
private async Task OnResumeFromRescheduleChoice(IDialogContext context, IAwaitable<string> result) {
var choice = await result;
}
The OnResumeFromRescheduleChoice method is firing, but the result shows failed because the ResumeAfter delegate is expecting type string, but is receiving object. Is this incorrect usage of the PromptDialog? Also the user is not being prompted the choices. I am using Bot.Builder version 3.5.5.
Move the context.Done<object>(null); call inside the else clause. You cannot call to context.Done after firing a Prompt.
I am using asp,net core and have used the tutorial to create sorted, paged and search page (Index). Once I edit an item from this page the controller always dumps me back to the default index page. How do I return to the previous URL. Many thanks.
Here is a section of my controller file.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, Bind("id,UserPassword,user")] UserProfiles userProfiles)
{
var users = from u in _context.UserProfiles
select u;
if (id != userProfiles.id)
{
return NotFound();
}
if (ModelState.IsValid)
{
try
{
_context.Update(userProfiles);
await _context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!UserProfilesExists(userProfiles.id))
{
return NotFound();
}
else
{
throw;
}
}
// ***************
// Redirect to the previous URL,i.e. the Index
return Redirect(TempData["PreviousURL"].ToString()) ;
}
return View(userProfiles);
}
public async Task<IActionResult> Index(string sortOrder, string currentFilter, string searchString, int? page)
{
ViewData["CurrentSort"] = sortOrder;
ViewData["NameSortParm"] = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
// paging
if (searchString != null)
{
page = 1;
}
else
{
searchString = currentFilter;
}
// search
ViewData["CurrentFilter"] = searchString;
var users = from u in _context.UserProfiles
select u;
if (!String.IsNullOrEmpty(searchString))
{
users = users.Where(u => u.user.Contains(searchString)
);
}
//sort
switch (sortOrder)
{
case "name_desc":
users = users.OrderByDescending(u => u.user);
break;
default:
users = users.OrderBy(s => s.user);
break;
}
// ***************
// store the current path and query string in TempData["PreviousURL" session variable
TempData["PreviousURL"] = HttpContext.Request.Path.ToString() + HttpContext.Request.QueryString.ToString();
return View(await PaginatedList<UserProfiles>.CreateAsync(users.AsNoTracking(), page ?? 1, pageSize));
}
This is my first MVC project.
It depends on your logic where controller takes you after saving data.
You need to pass search, sort and paging related data to controller when saving data. You can send them as part of extra post data, as query string parameters or as part of the model itself which is being posted.
After saving data retrieve data based on those parameters and populater your view with that paged, filtred and sorted data.
I solved my problem with the use of session variables: ViewData, ViewBag and TempData. The following two pages were very useful:
https://www.codeproject.com/Articles/476967/What-is-ViewData-ViewBag-and-TempData-MVC-Option
http://andrewlock.net/an-introduction-to-session-storage-in-asp-net-core/
Please see edited question above for the solution.
I am developing a quartz.net job which runs every 1 hour. It executes the following method. I am calling a webapi inside a for loop. I want to make sure i return from the GetChangedScripts() method only after all thread is complete? How to do this or have i done it right?
Job
public void Execute(IJobExecutionContext context)
{
try
{
var scripts = _scriptService.GetScripts().GetAwaiter().GetResult();
}
catch (Exception ex)
{
_logProvider.Error("Error while executing Script Changed Notification job : " + ex);
}
}
Service method:
public async Task<IEnumerable<ChangedScriptsByChannel>> GetScripts()
{
var result = new List<ChangedScriptsByChannel>();
var currentTime = _systemClock.CurrentTime;
var channelsToProcess = _lastRunReader.GetChannelsToProcess().ToList();
if (!channelsToProcess.Any()) return result;
foreach (var channel in channelsToProcess)
{
var changedScripts = await _scriptRepository.GetChangedScriptAsync(queryString);
if (changedScriptsList.Any())
{
result.Add(new ChangedScriptsByChannel()
{
ChannelCode = channel.ChannelCode,
ChangedScripts = changedScriptsList
});
}
}
return result;
}
As of 8 days ago there was a formal announcement from the Quartz.NET team stating that the latest version, 3.0 Alpha 1 has full support for async and await. I would suggest upgrading to that if at all possible. This would help your approach in that you'd not have to do the .GetAwaiter().GetResult() -- which is typically a code smell.
How can I use await in a for loop?
Did you mean a foreach loop, if so you're already doing that. If not the change isn't anything earth-shattering.
for (int i = 0; i < channelsToProcess.Count; ++ i)
{
var changedScripts =
await _scriptRepository.GetChangedScriptAsync(queryString);
if (changedScriptsList.Any())
{
var channel = channelsToProcess[i];
result.Add(new ChangedScriptsByChannel()
{
ChannelCode = channel.ChannelCode,
ChangedScripts = changedScriptsList
});
}
}
Doing these in either a for or foreach loop though is doing so in a serialized fashion. Another approach would be to use Linq and .Select to map out the desired tasks -- and then utilize Task.WhenAll.
I have 2 methods:
private static async Task<T> GetInfoAsync<T>(MyClient service, Func<Task<T>> funcAsync, string resultText)
{
var result = default(T);
if (service != null) {
try {
service.Open();
result = await funcAsync();
service.Close();
Console.WriteLine(resultText);
} catch (Exception ex) {
service.Abort();
}
}
return result;
}
private static async Task<T> GetInfoAsync<T>(MyClient service, Func<string,Task<T>> funcAsync, string resultText, string param)
{
var result=default(T);
if (service != null) {
try {
service.Open();
result = await funcAsync(param);
service.Close();
Console.WriteLine(resultText);
} catch (Exception ex) {
service.Abort();
}
}
return result;
}
Is is possible to create 1 method for 2 functions?
Something like
"private static async Task GetInfoAsync(MyClient service, Something??, string resultText, string param)"
No, but in general you can do a partial function (see for example http://blogs.msdn.com/b/wesdyer/archive/2007/01/29/currying-and-partial-function-application.aspx) that, given a function with a parameter, calls it with a fixed value for that parameter.
// The original function
Func<string, Task<T>> fullFunction = x => ...;
// The partial function
string param = "...";
Func<Task<T>> partialFunction = () => fullFunction(param);
So it would be the caller of GetInfoAsync that would need to create this partialFunction and pass it to GetInfoAsync.