I'm creating web-client for my REST API, and I want to add a field to my table containing result of async function.
#foreach(Product item in products)
{
<tr>
<th>#item.Name</th>
<th>#item.Amount</th>
<th>#GetUnit(item.UnitID).Result</th>
<th>#item.PriceNetto</th>
</tr>
}
async Task<string> GetUnit(Guid id)
{
string a = "https://localhost:5001/api/Units/";
a += id.ToString();
var temp = await Http.GetJsonAsync<Unit>(a); //it fails here
return temp.Name;
}
In short I have a list of products and items on the list have "UnitID" property which I use to make a GET request. When I put anywhere in code .Result after my async function result Visual Studio's debugger just skip the line responsible for calling the API and 'bricks' whole app without any error nor exception. Then I have to restart the project.
I tried to create second function only for returning GetUnit(id).Result but it gave nothing. I tried to return whole Unit object and then in the table GetUnit(item.UnitID).Name but it was just representing object (I guess...). I seems like all I need is do it with .Result but when I do it doesn't work.
My API is made with .Net Core 2.2 and my client is made with .Net Core 3.0 (Blazor template). Is this a bug or I just can't do it that way? Thanks.
you shouldn't need to do it.i recommand to call it in async action,like below :
razor focus on view,controller/model focus on data.
public async Task<IActionResult> SomeAction(Guid id)
{
var products = ..;
foreach (var item in products)
{
p.UnitID = await GetUnit(item.UnitID);
}
return View(products);
}
private async Task<string> GetUnit(Guid id)
{
string a = "https://localhost:5001/api/Units/";
a += id.ToString();
var temp = await Http.GetJsonAsync<Unit>(a); //it fails here
return temp.Name;
}
public class Product
{
public string Name { get; set; }
public decimal Amount { get; set; }
public string UnitID { get; set; }
public string PriceNetto { get; set; }
}
IMO, you can't do that way.In blazor,you could get all data in OnInitializedAsync instead.Store all Name in a string List and display the list data in view based index.For example:
#code {
private List<string> listItems = new List<string>();
protected override async Task OnInitializedAsync()
{
//get products
foreach (var product in products)
{
string a = "https://localhost:5001/api/Units/";
a += product.UnitID.ToString();
var temp = await Http.GetJsonAsync<Unit>(a);
listItems.Add(temp.Name);
}
}
}
Razor
#{ int i = 0;}
#foreach(Product item in products)
{
<tr>
<th>#item.Name</th>
<th>#item.Amount</th>
<th> #listItems[i] </th>
<th>#item.PriceNetto</th>
</tr>
i++;
}
Related
How to select values inside MultiSelect? I just want to select some items inside a dropdownlist from URL Variables
Issue: following line is not selecting items Colors.add(TempArray[i]);
debug: If I remove this line than it works ok and selects values. if (!string.IsNullOrEmpty(result)) ... foreach (var item2 in TempArray) but I need this line to check for null values. Code will get error otherwise
Also If I remove Post Method and it goes to Get directly than it works fine (items get selected). how can I make it work by using post method
Form
<form asp-page="./Index" method="post">
<select asp-for="Colors" asp-items="#Model.Colors_SELECT " class="MultiSelect" multiple>
....
</select>
...
back-end variables
[BindProperty(SupportsGet = true)]
public List<string>? Colors { get; set; }
public SelectList? Colors_SELECT { get; set; }
OnPost() Method - here I am change the URL format. for ex: localhost/index?Colors=red&Colors=Black to localhost/index?Colors=red,Black
public async Task<IActionResult> OnPostAsync()
{
var CurrentFilters = new Dictionary<string, string>();
var ColorsTemp = string.Join(",", Colors);
CurrentFilters.Add("Colors", ColorsTemp);
string query = "";
foreach (var p in CurrentFilters)
{
query += $"{p.Key}={p.Value}&";
}
query = query.TrimEnd(query[query.Length - 1]); //remove last '&'
var url = $"{HttpContext.Request.Path}?{query}"; // create URL
return Redirect(url); // Send new url - call get Method()
}
on OnGet() method
public async Task OnGetAsync()
{
// here i want to get URL values and select those items
string result = Request.Query["Colors"];
if (!string.IsNullOrEmpty(result))
{
string[] TempArray = result.Split(",");
foreach (var item2 in TempArray)
{
Colors.add(TempArray[i]);
}
}
}
I have this method in my components called ClearFiles(string PathName) which I currently call by clicking one of the buttons on the component, but now I'm trying to call of them at once but I cannot figure out how to do this. Calling a single ClearFiles method from the component works perfectly, but I'm not quite sure how to call all these methods at once using the parent class. I have made a button in the parent class with the name of ForceClean which I'm hoping can be pressed to call all of the components clear files function.
I create the components in the parent class using a for loop in my PageFileCleaner.razor file like this:
#foreach (var d in filters)
{
<CompFileFilter FileFilter="d" OnDelete="Delete" ></CompFileFilter>
}
Where filters is a List of type FileFilter which is in the namespace Common.DB. This is how it is declared in the parent class:
private List<FileFilter> filters { get; set; }
And is populated with data from my database using:
using var context = _db.CreateDbContext();
filters = await context.FileFilters.AsNoTracking().ToListAsync();
foreach( var filter in filters)
{
FileFilter f = new FileFilter();
}
And this Parameter (in the child class) is what i use to get the values from that row in the database using FileFilter.Pathname or FileFilter.ID for example:
[Parameter]
public FileFilter FileFilter { get; set; }
Database / FileFilter object class:
namespace Common.DB
{
public class FileFilter
{
[Key]
public int ID { get; set; }
public string TimerChoice { get; set; }
public bool IsLoading;
public string PathName { get; set; } = "";
public string Extension { get; set; }
//public string[] extensionList { get; set; }
...
}
}
The clear files function is in my component/child CompFileFilter.razor file (took the code out for this example because its over a hundred lines of code). I'm creating the components in the parent class and each one will have this ClearFiles() method.
public async Task ClearFiles(string PathName)
{
filtering code...
}
How can I loop through my components from the parent class and call all of there ClearFiles() method?
You can use a dictionary and store the reference of each CompFileFilter component. Then use the references to call the ClearFiles method. You can use the ID property of FileFilter for the dictionary keys.
PageFileCleaner.razor
#foreach (var d in filters)
{
<CompFileFilter #key="d.ID" #ref="_fileFilterRefs[d.ID]" FileFilter="d" OnDelete="Delete"></CompFileFilter>
}
#code {
private Dictionary<int, CompFileFilter> _fileFilterRefs = new Dictionary<int, CompFileFilter>();
private async Task ForceClean()
{
// Execute ClearFiles one at a time.
foreach (var item in _fileFilterRefs)
{
await item.Value.ClearFiles();
}
// Or execute them in parallel.
//var tasks = _fileFilterRefs.Select(item => Task.Run(item.Value.ClearFiles));
//await Task.WhenAll(tasks);
}
}
ClearFiles method doesn't need PathName parameter. You can get the PathName from the FileFilter parameter.
CompFileFilter.razor
#code {
[Parameter]
public FileFilter FileFilter { get; set; }
public async Task ClearFiles()
{
var pathName = FileFilter.PathName;
// filtering code...
}
}
Edit:
You should manually update the _fileFilterRefs dictionary whenever you remove an item from filters list to keep them synchronized. E.g.
private void Delete(FileFilter fileFilter)
{
_filters.Remove(fileFilter);
_fileFilterRefs.Remove(fileFilter.ID);
}
You should clear the _fileFilterRefs dictionary whenever you set the filters list to a new list. E.g.
private async Task RefreshFilters()
{
_fileFilterRefs.Clear();
_filters = await filterService.GetFileFilters();
}
But you don't have to do the same thing when you add a new item to the filters list (_filters.Add(...)). In that case it's handled automatically.
In summary:
filters.Add(new FileFilter()) -> do nothing.
filters.Remove(fileFilter) -> remove fileFilter.ID key from
_fileFilterRefs
filters = new List() -> clear _fileFilterRefs before setting the list
https://github.com/dotnet/aspnetcore/issues/17361#issuecomment-558138782
I'm using dapper to try and send multiple querys at the same time. I have 2 different querys that I want to use. However, no matter what I try, I can only get the method to find 1 of the querys.
Firstly, here's the controller:
[HttpGet]
public async Task<IActionResult> Test(RequestOverview model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
try
{
await _request.Test(model);
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
return Ok();
}
Interface:
Task<bool> Test(RequestOverview model);
Model:
public class TestT
{
public string vehicleRegNr { get; set; } = "";
}
public class TestE
{
public string dealerName { get; set; } = "";
}
Sidenote: The name of the entire class is RequestOverview
Now, here are some of my attempts in the method that uses dapper:
// attempt 1
public async Task<bool> Test(RequestOverview model)
{
string query = #"SELECT TOP 5 RegNumber as vehicleRegNr FROM [dbo].[Vehicle];
SELECT TOP 5 Retailer.Name as dealerName FROM [dbo].[Retailer]";
using (var multi = _sqlconnection.QueryMultiple(query, null))
{
List<TestT> list1 = multi.Read<TestT>().AsList();
List<TestE> list2 = multi.Read<TestE>().AsList();
}
return true;
}
attempt 2//
public async Task<bool> Test(TestT model)
{
string query = #"SELECT TOP 5 RegNumber as vehicleRegNr FROM [dbo].[Vehicle];
SELECT TOP 5 Retailer.Name as dealerName FROM [dbo].[Retailer]";
using (var multi = _sqlconnection.QueryMultiple(query, null))
{
List<dynamic> list1 = multi.Read<dynamic>().AsList();
List<dynamic> list2 = multi.Read<dynamic>().AsList();
}
return true;
}
(In attempt 2 I use only one model class TestT that has vehicleRegNr and dealerName as parameters)
However, no matter what I get this output when debugging:
List 2 always ends up null, anyone knows why?
Thankful for help.
(Sidenote: List 1 contains the vehicleRegNr)
I could not reproduce that problem, created an example https://dotnetfiddle.net/j0L0kd and QueryMultiple works as expected.
And you can see the results:
So I suspect either you check list2 values before multi.Read<dynamic>().AsList(); executed or second query returns nothing :)
So I'm new to ASP and EF and I am wondering how to do this incredibly basic operation, as well as a few questions to go along with doing it.
Currently I have a table we will call Resource;
class Resource
{
int current;
int min;
int max;
};
Right now I have the default CRUD options for this. What I would like is a + / - button on the main list that will manipulate the current value of each resource and update the value in the DB and on screen.
There are also certain operations I'd like to run such as "AddFive" to a selected group of resources.
So my questions;
How do I do this?
Is this scalable? If someone is constantly hitting the buttons this is obviously going to send a lot of requests to my DB. Is there any way to limit the impact of this?
What are my alternatives?
Thanks.
Edit:
To clarify the question; here are my post functions. How / where do I add these in my view to get a button showing for each resource. I just want the action to fire and refresh the value rather than redirect to a new page.
#Html.ActionLink("+", "Increment", new { id = item.ID })
public void Increment(int? id)
{
if (id != null)
{
Movie movie = db.Movies.Find(id);
if (movie != null)
{
Increment(movie);
}
}
}
[HttpPost, ActionName("Increment")]
[ValidateAntiForgeryToken]
public ActionResult Increment([Bind(Include = "ID,Title,ReleaseDate,Genre,Price")] Resource resource)
{
if ((resource.Current + 1) < (resource.Max))
resource.Current++;
return View(resource);
}
It sounds like the main issue you are having is creating a list of movies on the front end and updating the details for a specific one.
The key here is that you will need to either wrap a form around each item and have that posting to your update controller or use ajax / jquery to call the controller instead.
I have given you an example of the first one. Once the update controller is hit it will redirect to the listing page which will then present the updated list of movies.
Below is a minimal working example of how to wire this up. I've not included any data access code for brevity but have included psuedo code in the comments to show you where to place it.
Please let me know if you have any futher questions.
Controller
public class MoviesController : Controller
{
public ViewResult Index()
{
// Data access and mapping of domain to vm entities here.
var movieListModel = new MovieListModel();
return View(movieListModel);
}
public ActionResult Increment(IncrementMovieCountModel model)
{
// Put breakpoint here and you can check the value are correct
var incrementValue = model.IncrementValue;
var movieId = model.MovieId;
// Update movie using entity framework here
// var movie = db.Movies.Find(id);
// movie.Number = movie.Number + model.IncrementValue;
// db.Movies.Save(movie);
// Now we have updated the movie we can go back to the index to list them out with incremented number
return RedirectToAction("Index");
}
}
View
#model WebApplication1.Models.MovieListModel
#{
ViewBag.Title = "Index";
}
<h2>Some Movies</h2>
<table>
<tr>
<td>Id</td>
<td>Name</td>
<td>Increment Value</td>
<td></td>
</tr>
#foreach (var movie in Model.MovieList)
{
using (Html.BeginForm("Increment", "Movies", FormMethod.Post))
{
<tr>
<td>#movie.Id #Html.Hidden("MovieId", movie.Id)</td>
<td>#movie.Name</td>
<td>#Html.TextBox("IncrementValue", movie.IncrementValue)</td>
<td><input type="submit" name="Update Movie"/></td>
</tr>
}
}
</table>
Models
public class MovieListModel
{
public MovieListModel()
{
MovieList = new List<MovieModel> {new MovieModel{Id=1,Name = "Apocalypse Now",IncrementValue = 3}, new MovieModel {Id = 2,Name = "Three Lions", IncrementValue = 7} };
}
public List<MovieModel> MovieList { get; set; }
}
public class MovieModel
{
public int Id { get; set; }
public string Name { get; set; }
public int IncrementValue { get; set; }
}
public class IncrementMovieCountModel
{
public int IncrementValue { get; set; }
public int MovieId { get; set; }
}
I have this method
Meeting is a class
Attendees is an ICollection in Meeting
Class
public partial class Meeting
{
public Meeting()
{
this.Attendees = new List<Attendees>();
}
public virtual ICollection<Attendees> Attendees{ get; set; }
[...]
Method Controller
private void RemoveRowsDuplicated(Meeting model)
{
if (model.Attendees != null)
{
foreach (var item in model.Attendees.GroupBy(x => x.UserName).Select(y => y.Last()))
{
context.Attendees.Remove(item);
}
}
}
The objective is remove duplicate Attendees with the same username in the table.
But the current method it deletes all records and keeps the duplicate
Where am I going wrong?
Correct version of your method will look like this:
private static void RemoveRowsDuplicated(Meeting model)
{
if (model.Attendees != null)
{
var duplicates = new List<Attendees>();
foreach (var item in model.Attendees.GroupBy(x => x.UserName).Where(x=>x.Count()>1))
{
duplicates.AddRange(item.Skip(1));
}
duplicates.ForEach(x=>context.Attendees.Remove(x));
}
}
You can try writing raw SQL and invoking via EF and return Attendees objects in a list.
var query = "Select * from Attendees group by username";
var attendeesList = dbContext.Database.SqlQuery<Attendees>(query).ToList<Attendees>();
As I can see you grouped elements by name and remove last item. So you remove unique elements.
Like this
private void RemoveRowsDuplicated(Meeting model)
{
if (model.Attendees != null)
{
var temporaryAtendees = new List<Attendees>();
foreach(var item in model.Attendees)
{
if (temporaryAtendees.Contains(item))
{
context.Attendees.Remove(item);
}
else
{
temporaryAtendees.Add(item);
}
}
}
}