I have an Observable<WebResponse> (WebResponse implements IDisposable)
responseObservable
.Where(webResponse => webResponse.ContentType.StartsWith("text/html"))
.Select(webResponse => webResponse.ContentLength)
.Run()
(Ignore the pointlessness of the query!)
so, I'm discarding WebResponse instances without calling Dispose on them. This seems bad.
More abstractly: If I have an Observable<IDisposable>, how do I deal with the disposal of generated items?
Assuming that you have a method WebResponse CreateMyWebResponse() use Observable.Using like this:
var responseObservable = Observable.Using(() => CreateMyWebResponse(), Observable.Return);
Change the Where and Do bits to something like
.Do(webResponse => {
if (webResponse.ContentType.StartsWith("text/html"))
ProcessAndDispose(webResponse);
else
webResponse.Dispose(); })
perhaps?
EDIT
Based on your edit, how about
.Select(webResponse => {
int r = -1;
if (webResponse.ContentType.StartsWith("text/html"))
r = webResponse.ContentLength;
webResponse.Dispose();
return r; })
.Where(i => i != -1)
now? This would generalize into something like
FilterProjectDispose(e, pred, proj) {
e.Select(x => {
using(x) {
if (pred(x)) return Some(proj(x));
else return None; }})
.Where(x => x.IsSome)
.Select(x => x.Value)
}
assuming Some/None as in F# (I am apparently starting to forget my C#).
Related
I have the following controller:
[HttpGet("idfull/{id}")]
public async Task<IActionResult> GetAccountByIdFull(int id)
{
try
{
var response = await _accountFacade.GetAccountByIdAsync(id, full: true).ConfigureAwait(false);
if (response == null)
return NoContent();
return Ok(response);
}
catch (KeyNotFoundException kEx)
{
return NotFound();
}
catch (Exception ex)
{
return StatusCode((int)HttpStatusCode.InternalServerError);
}
}
The Facade layer:
public async Task<AccountViewModel> GetAccountByIdAsync(int accountId, bool full = false)
{
try
{
var unmappedResponse = await _accountService.GetAccountByIdAsync(accountId, full);
var mappedResponse = _mapper.Map<AccountViewModel>(unmappedResponse);
return mappedResponse;
}
catch
{
throw;
}
}
The service layer:
public async Task<Account> GetAccountByIdAsync(int accountId, bool full = false)
{
try
{
Account account;
if (full)
{
account = await _repo.GetOneAsync<Account>(x => x.AccountId == accountId);
account.Company = await _repo.GetOneAsync<Company>(filter: x => x.CompanyId == account.CompanyId,
includes: source => source
.Include(c => c.CompanyTransferType)
.ThenInclude(ctt => ctt.PartnerCompanyAccountType)
.Include(c => c.CompanyTransferType).ThenInclude(ctt => ctt.TransferType)
.Include(c => c.CompanyEquipment).ThenInclude(ce => ce.Equipment)
.Include(c => c.CompanyAccountGroup)
.Include(c => c.CompanyAccountType));
account.AccountContact = await _repo.GetAsync<AccountContact>(filter: x => x.AccountId == accountId);
account.AccountEquipment = await _repo.GetAsync<AccountEquipment>(filter: x => x.AccountId == accountId,
includes: source => source
.Include(ae => ae.AccountEquipmentFee).Include(ae => ae.CompanyEquipment).ThenInclude(ce => ce.Equipment));
account.AccountPickVolumeDefaultAccount = await _repo.GetAsync<AccountPickVolumeDefault>(filter: x => x.AccountId == accountId,
includes: source => source
.Include(a => a.Equipment).Include(a => a.PartnerAccount));
}
else
{
account = await _repo.GetByIdAsync<Account>(accountId);
}
if (account == null)
throw new KeyNotFoundException($"Could not find Account with ID: {accountId}");
return account;
}
catch
{
throw;
}
}
What I do not understand is, the controller returns OK status and all of my fields are populated. However, the API hangs and does not return my data, in other words, the Swagger API (including front-end application) does not receive the response and keeps on showing the loading button.
I have a funny feeling it has something to do with Serialization, but not sure how to fix it.
I have made sure to turn off SelfRefenceLooping, as can be seen here:
services.AddControllers().AddNewtonsoftJson(setup =>
{
setup.SerializerSettings.ContractResolver = new DefaultContractResolver();
setup.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
});
Why is the API not returning the JSON object?
Can you try without the .ConfigureAwait(false)?
Except for that, you code seems ok and the API should work fine.
If you don't have it, I would recommend adding Swagger to your API, it's just a matter of 5-10 lines and it helps a lot in the development phase (and also avoid manual mistakes while testing). Here is the official documentation: ASP.NET Core web API help pages with Swagger / OpenAPI
You can also try to add a very dumb action in your controller, to try to identify where the issue comes from (Controller layer? Service layer? Facade layer?)
The below code i use for doing multiple HTTP calls depending on the studentList.
It works well; however, I think the axios spread is not necessary
export default {
getFee (studentList: { studentId: string }[]) {
if (studentList.length < 1) {
Promise.resolve()
}
let promises = []
for (const student of studentList) {
if (!student.studentId) {
Promise.resolve()
}
var url = `${API_URL}/${student.studentId}`
promises.push(Axios.get(url))
}
return Axios.all(promises)
.then(Axios.spread((...args) => {
// customise the response here
return args
.map(response => response.data)
.map(data => {
// #ts-ignore
data.totalMark = data.markinPhysics + data.markinMaths + data.markinChemistry // total mark sum of marks in differnet discplines
return data
})
}))
.catch(error => {
switch (error.response.status) {
case 400:
console.log('student not found')
break
case 500:
console.log('error invoking')
break
default:
console.log('unknown error')
I have to do multiple network calls in Vue and I am using Axios.
I got it working by axios, all and axios.spread, but I think the code can be improved.
The logic is to do multiple calls for the student list and get the outputs back
Can anyone help?
Axios.all
as well as Promise.all accepts array of promises and returns a new Promise which is resolved whenever all of the given promises are resolved with an array with the result of each promise
e.g.
const promise1 = Promise.resolve('data1');
const promise2 = Promise.resolve('data2');
Promise.all([
promise1,
promise2,
]).then(results => {
// results is an array with 2 elements
console.log(results[0]); // data1
console.log(results[1]); // data2
});
you can use Axios.spread to to assign each result to a variable like this:
Promise.all([
promise1,
promise2,
]).then(Axios.spread(( result1, result2 ) => {
// args is an array with 2 elements
console.log(result1); // data1
console.log(result2); // data2
});
alternatively you can use ES6 Destructuring assignment:
Promise.all([
promise1,
promise2,
]).then(([ result1, result2 ]) => {
// args is an array with 2 elements
console.log(result1); // data1
console.log(result2); // data2
});
Unnecessary Promise.resolve()
Your Promise.resolve() function calls have no effect on the getFee method since you're not returning them
What would my implementation be
async function getFee(studentList) {
try {
const promises = studentList.reduce((acc, student) =>
student.studentId
? acc.concat(Axios.get(`${API_URL}/${student.studentId}`))
: acc
, []);
const responses = await Axios.all(promises);
return responses
.map(response => response.data)
.map(data => ({
// return new object
// with data's properties
// instead of assinging the new ones directly to the data
...data,
// total mark sum of marks in differnet discplines
totalMark: data.markinPhysics + data.markinMaths + data.markinChemistry,
}));
} catch (error) {
switch (error.response.status) {
case 400:
console.log("student not found");
break;
case 500:
console.log("error invoking");
break;
default:
console.log("unknown error");
}
}
}
export default {
getFee
}
Since you're only using args as an array, you could remove axios.spread.
axios.spread() might only be useful in older browsers now that ES2015 introduced its own spread operator. The main purpose of axios.spread() is to expand the result of axios.all() into an argument list, such that you could do:
axios.all(promiseArray).then(axios.spread(function(arg1, arg2, arg3) {
/*...*/
}))
instead of:
axios.all(promiseArray).then(function(args) {
var arg1 = args[0]
var arg2 = args[1]
var arg3 = args[2]
/*...*/
})
ES2015's rest operator does the inverse of axios.spread(), so when you combine them (as seen below), you end up with the result above, as if axios.spread() and the rest operator weren't even used:
axios.all(promiseArray).then(axios.spread(function(...args) {
var arg1 = args[0]
var arg2 = args[1]
var arg3 = args[2]
/*...*/
}))
// or newer syntax:
axios.all(promiseArray).then(axios.spread((...args) => {
const arg1 = args[0]
const arg2 = args[1]
const arg3 = args[2]
/*...*/
}))
To avoid promise chaining and improve readability, I think below can be used.
const [arg1, arg2] = await Promise.all(promises)
I have been looking around the Telerik forums & Stackoverflow for an answer for this and I am completely stuck and unable to figure out the issue.
I am using the Kendo UI for Asp.Net Core Scheduler Control. I have it reading the data from my controller fine. However, I cannot get it call the HttpPut handler correctly.
When checking the traffic I get the following response, and therefor my breakpoint inside my HttpPut handler will never be hit.
400 - Bad Request
{"":["The input was not valid."]}
My code in my view is:
#(Html.Kendo().Scheduler<MeetingViewModel>()
.Name("SchedulerView")
.Height(500)
.Date(DateTime.Now.ToUniversalTime())
.StartTime(new DateTime(2018, 11, 28, 0, 00, 00).ToUniversalTime())
.MajorTick(30)
.ShowWorkHours(false)
.Footer(false)
.Editable(edit =>
{
//edit.Resize(false);
edit.Create(false);
})
.Views(views =>
{
views.TimelineView(timeline => timeline.EventHeight(50));
//views.TimelineWeekView(timeline => timeline.EventHeight(50));
//views.TimelineWorkWeekView(timeline => timeline.EventHeight(50));
//views.TimelineMonthView(timeline =>
//{
// timeline.StartTime(DateTime.Now);
// timeline.EndTime(DateTime.Now.AddMonths(1));
// timeline.MajorTick(1440);
// timeline.EventHeight(50);
//});
})
.Timezone("Etc/UTC")
.Group(group => group.Resources("WorkCenters" /*,"Attendees"*/).Orientation(SchedulerGroupOrientation.Vertical))
.Resources(resource =>
{
resource.Add(m => m.ScheduleRowID)
.Title("Work Center")
.Name("WorkCenters")
.DataTextField("Text")
.DataValueField("Value")
.DataColorField("Color")
.BindTo(#Model.AvailableWorkCenters);
})
.DataSource(d => d
.ServerOperation(true)
.WebApi()
.Model(m =>
{
m.Id(f => f.ActivityID);
m.Field(f => f.Title).DefaultValue("No title");
//m.RecurrenceId(f => f.RecurrenceID);
m.Field(f => f.Description).DefaultValue("No Description");
})
.Events(events => events.Error("error_handler"))
.Read(read => read.Action("GetActivities", "Scheduler").Data("setRequestDateTimes"))
//.Create(create => create.Action("Post", "Scheduler"))
.Update(update => update.Action("PutActivity", "Scheduler", new { id = "{0}" }).Type(HttpVerbs.Put))
//.Destroy(destroy => destroy.Action("Delete", "Scheduler", new { id = "{0}" }))
)))
And my API Controller is as follows:
[Route("Api/[controller]")]
[ApiController]
public class SchedulerController : DawnController
{
public SchedulerController(DatabaseContext context) : base(context)
{
}
[HttpGet]
public DataSourceResult GetActivities([DataSourceRequest] DataSourceRequest request, DateTime requestStartDateTime, DateTime requestEndDateTime)
{
//Kendo doesnt seem to send the full date range. so + 1 day to end
requestEndDateTime = requestEndDateTime.AddDays(1);
List<MeetingViewModel> test = new List<MeetingViewModel>();
foreach (JobTask jobTask in Context.JobTask)
{
if (JobTask.HasActivityInDateRange(jobTask, requestStartDateTime, requestEndDateTime))
{
foreach (Activites jobTaskAct in jobTask.Activites)
{
test.Add(new MeetingViewModel()
{
JobTaskID = jobTask.JobTaskId,
ActivityID = jobTaskAct.ActivityId,
Title = jobTaskAct.Name,
Description = jobTaskAct.Description,
Start = jobTaskAct.StartTime.ToUniversalTime(),
End = jobTaskAct.EndTime.ToUniversalTime(),
IsAllDay = false,
ScheduleRowID = jobTaskAct.Workcenter.WorkCenterId,
});
}
}
}
return test.ToDataSourceResult(request);
}
[HttpPut("{id}")]
public IActionResult PutActivity(int id, MeetingViewModel task)
{
if (ModelState.IsValid && id == task.ActivityID)
{
try
{
//breakpoint here
bool a = true;
//update the db here
}
catch (DbUpdateConcurrencyException)
{
return new NotFoundResult();
}
return new StatusCodeResult(200);
}
else
{
return BadRequest(ModelState.Values.SelectMany(v => v.Errors).Select(error => error.ErrorMessage));
}
}
}
Thanks
The URL exposing your controller method PutActivity in your controller example is PUT api/scheduler/{id}
To access that URL use this Update method.
.Update(update => update.Action("Put", "Scheduler", new { id = "{0}" }))
See this demo as example
Alternatively
If you want to implment the URL api/Scheduler/PutActivity/{id} (similar pattern to your GET) then you will need to modify the attribute over the put method as follows.
[HttpPut("PutActivity/{id}")]
public IActionResult PutActivity(int id, MeetingViewModel task)
Then you can call api/Scheduler/PutActivity/{id} with this asp.net action call.
.Update(update => update.Action("PutActivity", "Scheduler", new { id = "{0}" }).Type(HttpVerbs.Put))
I am having a bit of a headache with a thing (I know something like has been asked before, but I'm pretty sure it's not quite the same thing).
To the point:
I have a View with a Telerik grid. On that grid I show some stuff from the model that I pass to the View BUT I want in the final column to put a CheckBox that is checked/unchecked based on some things in the Controller (the checks have nothing to do with the model that is being passed). In my ActionResult function that takes care of the View I store some Boolean values in the ViewData, and then I set the isChecked value in the CheckBox based on the values stored in the ViewData.
The code for the ActionResult is as follows:
[SecureThis(Roles = "User")]
public ActionResult Index()
{
//get bucket ids
var buckets = db.Buckets.ToList();
int i=1;
string cb = "checkbox" + i.ToString();
foreach (Bucket b in buckets)
{
var payByInvoice = db.PaymentOptions.Where(p => p.BucketId == b.Id).Select(p => p.CanPayByInvoice).SingleOrDefault();
if (payByInvoice == (int)PayByInvoiceState.Accepted)
ViewData["checkbox" + i.ToString()] = true;
else ViewData["checkbox" + i.ToString()] = false;
i++;
cb = "checkbox" + i.ToString();
}
return View(db.Buckets);
}
And the grid that should show all the stuff is this:
#{
int i=1;
string cb = "checkbox" + i.ToString();
}
#(Html.Telerik().Grid(Model)
.Name("BucketsGrid")
.DataKeys(keys => keys.Add(bucket => bucket.Id))
.Columns(
columns =>
{
columns.Template(model => ViewData[model.Id.ToString()])
.HeaderTemplate(
#<b>#Strings.Title_Customer</b>
);
columns.Bound(model => model.CreditFacility);
columns.Bound(model => model.Minimum);
columns.Bound(model => model.RefillLevel);
columns.Bound(model => model.NotificationEmail);
columns.Bound(model => model.NotificationSms);
columns.Template(model => Html.ActionLink(Strings.Edit, "Edit", new { id = model.Id }));
columns.Template(model => Html.ActionLink(Strings.NotificationOptions, "Bucket", "NotificationOptions", new { id = model.Id }, null));
columns.Template(model => Html.ActionLink("Refill", "Refill", "Payment", new { id = model.Id }, null));
columns.Template(model => Html.ActionLink(Strings.Details, "Details", new { id = model.Id }));
columns.Template(model => Html.ActionLink(Strings.Delete, "Delete", new { id = model.Id }));
columns.Template(model => Html.CheckBox("invoice", (Boolean)ViewData[#cb])).HeaderTemplate("Invoice Option");
#i++;
#cb = "checkbox" + i.ToString();
}
)
.Pageable(paging =>
paging.Enabled(true)
.PageSize(UserSettings.GridPageSize)
.Style(GridPagerStyles.NextPrevious)
.Position(GridPagerPosition.Bottom)
)
.Sortable()
.Scrollable()
.Resizable(resize=> resize.Columns(true))
)
The problem with this whole thing is that the checkboxes remain unchecked, no matter the data stored in the ViewData. I went with the debugger and the values are se accordingly in the ViewData, but for some reason (that I cannot yet tell) the checkboxes still remain unchcked.
Any ideas on this matter would be much appreciated.
I have found out the problem of all this. As expected, it was my own doing (or so to say). The problem was that I incremented the #i variable inside the Telerik grid declaration, thinking that it would happen for all the rows in the grid, but that thing is only triggered once. Hence, the ViewData[#cb] value would always have the 2nd value set in the Controller (which in my case was false) and all the checkboxes would then be unchecked.
The fix:
I used the ViewBag and set it up with a Dictionary<Guid, bool> to hold my values, and iterate through it using the model.Id property. For anyone who might be interested I'll post the code below.
Controller:
ViewBag.Dict = new Dictionary<Guid, bool>();
Dictionary<Guid, bool> dict = new Dictionary<Guid, bool>();
foreach (Bucket b in buckets)
{
var payByInvoice = db.PaymentOptions.Where(p => p.BucketId == b.Id).Select(p => p.CanPayByInvoice).SingleOrDefault();
if (payByInvoice != (int)PayByInvoiceState.Accepted)
{
dict.Add(b.Id, false);
}
if (payByInvoice == (int)PayByInvoiceState.Accepted)
{
dict.Add(b.Id, true);
}
}
ViewBag.Dict = dict;
View:
columns.Template(model => Html.CheckBox("invoice", (bool)ViewBag.Dict[model.Id])).HeaderTemplate("Invoice option");
I have a question about the websites argument in the magento api.
Nowhere can I find an explanation of what this variable is.
Does this variable represent a storeview? a store? a website?
Where in the api can I retrieve a list of available options?
If I cannot retrieve a list from the API, where in the backend menu can I find the static variable that I can use?
Do I need the website ID or storeID?
I use soap v1
function call($which,$vars=null)
{
// retourneer de output soap client api call
if($vars !== null)
{
return $this->soapclient->call($this->sessiontoken,$which,$vars);
}
else
{
return $this->soapclient->call($this->sessiontoken,$which);
}
}
function createProduct($productname,
$websites,
$shortdescription,
$description,
$status,
$weight,
$tax_class_id,
$categories,
$price,
$attributesetid,
$producttype,
$sku)
{
$attributeSets = $this->call('product_attribute_set.list');
$set = current($attributeSets);
try
{
$x = $this->call('product.create', array($producttype, $set['set_id'], $sku, array(
'name' => $productname,
// websites - Array of website ids to which you want to assign a new product
'websites' => $websites, // array(1,2,3,...)
'short_description' => $shortdescription,
'description' => $description,
'status' => $status,
'weight' => $weight,
'tax_class_id' => $tax_class_id,
'categories' => $categories, //3 is the category id(array(3))
'price' => $price
);));
}
catch(Exception $e)
{
$x = 0xABED + 0xCAFE + 0xBAD + 0xBED * 0xFACE;// abed went to a cafe... the alcohol went bad.... he stumbled into bed and fell face down...
}
return $x;
}
Looking at Mage_Catalog_Model_Product_Api::create():
public function create($type, $set, $sku, $productData, $store = null)
{
//[...]
$product = Mage::getModel('catalog/product');
$product->setStoreId($this->_getStoreId($store))
->setAttributeSetId($set)
->setTypeId($type)
->setSku($sku);
//[...]
$this->_prepareDataForSave($product, $productData);//this does some processing
Now, looking at Mage_Catalog_Model_Product_Api::_prepareDataForSave():
protected function _prepareDataForSave($product, $productData)
{
if (isset($productData['website_ids']) && is_array($productData['website_ids'])) {
$product->setWebsiteIds($productData['website_ids']);
}
//....
we see that website_ids (numeric array) are expected