I want to get and pass data from my HTTPWebRequest to my sync page. I am new to MVVM so I don't know if I am correct but so far I can check if there is a result. I tried to watch a lots of video tutorials and forums still I can't find what I need. I was hoping you guys have a answer for me. I have been struggling with this for a week now.
The flow of my project:
- The user will login when it is correct, The user will be redirected to my sync page where it will get all the users data from my server and insert the data into SQLite Database.
My questions are:
1. How can I get the data and specific data from my HTTPWebRequest?
2. Is my HTTPWebRequest service correct?
3. Is my MVVM correct?
4. Is my navigation to my next page is correct?
My code:
LoginPage.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TBSMobileApplication.Data;
using TBSMobileApplication.ViewModel;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace TBSMobileApplication.View
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class LoginPage : ContentPage
{
public LoginPageViewModel loginModel;
public LoginPage ()
{
InitializeComponent ();
BindingContext = new LoginPageViewModel();
MessagingCenter.Subscribe<LoginPageViewModel,string>(this, "Login Alert", (sender,Username) =>
{
DisplayAlert("Login Error", "Please fill-up the form", "Ok");
});
MessagingCenter.Subscribe<LoginPageViewModel, string>(this, "Connected", (sender, Username) =>
{
DisplayAlert("Connection Info", "Connected", "Ok");
});
MessagingCenter.Subscribe<LoginPageViewModel, string>(this, "Not Connected", (sender, Username) =>
{
DisplayAlert("Connection Info", "Not Connected", "Ok");
});
MessagingCenter.Subscribe<LoginPageViewModel, string>(this, "Http", (sender, Username) =>
{
DisplayAlert("Login Error", "Username or Password is incorrect", "Ok");
});
entUsername.Completed += (object sender, EventArgs e) =>
{
entPassword.Focus();
};
}
protected async Task OnAppearingAsync()
{
var db = DependencyService.Get<ISQLiteDB>();
var conn = db.GetConnection();
if (conn != null)
{
await conn.CreateTableAsync<UserTable>();
await conn.CreateTableAsync<RetailerTable>();
await conn.CreateTableAsync<ContactsTable>();
}
base.OnAppearing();
}
}
}
LoginPageViewModel.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Net;
using System.Text;
using System.Windows.Input;
using TBSMobileApplication.View;
using Xamarin.Essentials;
using Xamarin.Forms;
namespace TBSMobileApplication.ViewModel
{
public class LoginPageViewModel : INotifyPropertyChanged
{
void OnProperyChanged(string PropertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
}
public string username;
public string password;
public string Username
{
get { return username; }
set
{
username = value;
OnProperyChanged(nameof(Username));
}
}
public string Password
{
get { return password; }
set
{
password = value;
OnProperyChanged(nameof(Password));
}
}
public ICommand LoginCommand { get; set; }
public LoginPageViewModel()
{
LoginCommand = new Command(OnLogin);
}
public void OnLogin()
{
if (string.IsNullOrEmpty(Username) || string.IsNullOrEmpty(Password))
{
MessagingCenter.Send(this, "Login Alert", Username);
}
else
{
var current = Connectivity.NetworkAccess;
if (current == NetworkAccess.Internet)
{
var link = "http://192.168.1.25:7777/TBS/test.php?User=" + Username + "&Password=" + Password;
var request = HttpWebRequest.Create(string.Format(#link));
request.ContentType = "application/json";
request.Method = "GET";
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
if (response.StatusCode != HttpStatusCode.OK)
{
Console.Out.WriteLine("Error fetching data. Server returned status code: {0}", response.StatusCode);
}
else
{
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
var content = reader.ReadToEnd();
if (content.Equals("[]") || string.IsNullOrWhiteSpace(content) || string.IsNullOrEmpty(content))
{
MessagingCenter.Send(this, "Http", Username);
}
else
{
App.Current.MainPage.Navigation.PushAsync(new DatabaseSyncPage(), true);
}
}
}
}
}
else
{
MessagingCenter.Send(this, "Not Connected", Username);
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
Related
I have an error and i tired to look up it and i still not find it.
Scenario: I made my Backend in C# with VS, the FrontEnd is with Flutter.
all my backend works perfectly with all my Flutter App, but there is one problem with only one Action.
When i try to "Save, Update or Delete " a record, it appears the next message in my app, when i put a revision point in my Flutter code in the action to save/delete or update, apperars the CodeError 500 and "Internal Server Error".
The DB is in Azure, and i allready erase and create it twice, and the error persist.
the error is only with this code, the other Ssave/update or delete action.
attach the Images of my code:
Code of The Backend
using System.ComponentModel.DataAnnotations;
namespace Vehicles.API.Models.Request
{
public class HistoryRequest
{
public int Id { get; set; }
[Required(ErrorMessage = "El campo {0} es obligatorio")]
public int VehicleId { get; set; }
[Required(ErrorMessage = "El campo {0} es obligatorio")]
public int Mileage { get; set; }
[Required(ErrorMessage = "El campo {0} es obligatorio")]
public string Remarks { get; set; }
}
}
Other Page, the API Contoller
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Vehicles.API.Data;
using Vehicles.API.Data.Entities;
using Vehicles.API.Models.Request;
namespace Vehicles.API.Controllers.API
{
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[Route("api/[controller]")]
[ApiController]
public class HistoriesController : ControllerBase
{
private readonly DataContext _context;
private readonly IUserhelper _userhelper;
public HistoriesController(DataContext context, IUserhelper userhelper)
{
_context = context;
_userhelper = userhelper;
}
[HttpGet("{id}")]
public async Task<ActionResult<History>> GetHistory(int id)
{
History history = await _context.Histories
.Include(x => x.Details)
.ThenInclude(x => x.Procedure)
.FirstOrDefaultAsync(x => x.Id == id);
if (history == null)
{
return NotFound();
}
return history;
}
[HttpPost]
public async Task<ActionResult<History>> PostHistory(HistoryRequest request)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
Vehicle vehicle = await _context.Vehicles.FindAsync(request.VehicleId);
if (vehicle == null)
{
return BadRequest("El Vehiculo no existe");
}
string email = User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier).Value;
User user = await _userhelper.GetUserAsync(email);
if (user == null)
{
return BadRequest("La usuario no existe");
}
History history = new()
{
Date = DateTime.UtcNow,
Details= new List<Detail>(),
Mileage=request.Mileage,
Remarks=request.Remarks,
User=user,
Vehicle=vehicle,
};
_context.Histories.Add(history);
await _context.SaveChangesAsync();
return Ok(history);
}
[HttpPut("{id}")]
public async Task<IActionResult> PutHistory(int id, HistoryRequest request)
{
if (id != request.Id)
{
return BadRequest();
}
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
History history = await _context.Histories.FindAsync(request.Id);
if (history == null)
{
return BadRequest("La historia no existe");
}
history.Mileage = request.Mileage;
history.Remarks= request.Remarks;
_context.Histories.Update(history);
await _context.SaveChangesAsync();
return NoContent();
}
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteHistory(int id)
{
History history = await _context.Histories
.Include(x => x.Details)
.FirstOrDefaultAsync(x => x.Id == id);
if (history == null)
{
return NotFound();
}
_context.Histories.Remove(history);
await _context.SaveChangesAsync();
return NoContent();
}
}
}
Code in VC Flutter:
Response response = await ApiHelper.delete(
'/api/Histories/', widget.history.id.toString(), widget.token);
setState(() {
_showLoader = false;
});
if (!response.isSuccess) {
await showAlertDialog(
context: context,
title: 'Error',
message: response.message,
actions: <AlertDialogAction>[
AlertDialogAction(key: null, label: 'Aceptar'),
]);
return;
}
Navigator.pop(context, 'yes');
}
And the Code of the API code to conect with the backend:
static Future<Response> delete(
String controller, String id, Token token) async {
if (!_validToken(token)) {
return Response(
isSuccess: false,
message:
'Sus Credenciales se han vencido, favor cierre sesion y vuelva a ingresar nuevamente al sistema.');
}
var url = Uri.parse('${Constans.apiUrl}$controller$id');
var response = await http.delete(
url,
headers: {
'content-type': 'application/json',
'accept': 'application/json',
'authorization': 'bearer ${token.token}',
},
);
if (response.statusCode >= 400) {
return Response(isSuccess: false, message: response.body);
}
return Response(isSuccess: true);
}
I am trying to get a single item from my list of elements and it is not working. I am using FromODataUri correctly, I am not seeing what is wrong.
I am able to hit this resource:
https://localhost:44314/odata/Inventories
but I can't get a single item: https://localhost:44314/odata/Inventories(1)
This is how my Startup.cs looks
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.OData.Builder;
using Microsoft.AspNet.OData.Extensions;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.OData.Edm;
using OdataTest.Models;
using Swashbuckle.AspNetCore.Swagger;
namespace OdataTest
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddOData();
services.AddODataQueryFilter();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info { Title = "Inventory Api", Version = "v1" });
});
services.AddMvc(options => options.EnableEndpointRouting = false).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseSwagger();
app.UseSwaggerUI(c =>
{
// force to add another /swagger to fix issue
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Physical Inventory API");
});
app.UseHttpsRedirection();
app.UseMvc(b =>
b.MapODataServiceRoute("odata", "odata", GetEdmModel(app.ApplicationServices))
);
}
private static IEdmModel GetEdmModel(IServiceProvider serviceProvider)
{
ODataModelBuilder builder = new ODataConventionModelBuilder(serviceProvider);
builder.Namespace = "PartsInventory";
builder.ContainerName = "PartsInventoryContainer";
builder.EntitySet<InventoryOutputDto>("Inventories").EntityType
.Filter()
.Count()
.Expand()
.OrderBy()
.Page()
.Select();
return builder.GetEdmModel();
}
}
}
And my Controller
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.OData;
using Microsoft.AspNet.OData.Routing;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using OdataTest.Models;
namespace OdataTest.Controllers
{
public class PartsInventoryController : ODataController
{
List<InventoryOutputDto> list = new List<InventoryOutputDto>() {
new InventoryOutputDto {
Description = "Description1",
UserName = "user1",
EndDate = DateTime.Now,
ErrorId = "Error",
InventoryId = 1,
LocationCode = 1,
StartDate = DateTime.Now,
StatusCode = 1
},
new InventoryOutputDto {
Description = "Description1",
UserName = "user1",
EndDate = DateTime.Now,
ErrorId = "Error2",
InventoryId = 2,
LocationCode = 2,
StartDate = DateTime.Now,
StatusCode = 2
} };
[HttpGet]
[ODataRoute("Inventories")]
/// public IActionResult Inventory(InventoryInputDto inputDto)
public IActionResult GetInventories(InventoryInputDto inputDto)
{
return StatusCode(StatusCodes.Status200OK, list);
}
[HttpGet]
[ODataRoute("Inventories({key})")]
public IActionResult GetInventoryById([FromODataUri] int key)
{
var invRecord = list.FirstOrDefault(i => i.InventoryId == key);
if(invRecord == null)
{
return NotFound();
}
return Ok(invRecord);
}
}
}
I am trying to implement Npgsql in our DAL and running into issues under heavy load. the following sample application is a decent representation of just a simple query that under heavy load, throws a 'A command is already in progress' exception. I am assuming this is due to the lack of MARS support so I also tried creating a connection each time with a using statement around each command only to have the performance become unusable. I checked that the username is indexed so that shouldn't be an issue.
Not sure what I am doing wrong here but I need some advice on how to get this performing well.
OS: Docker Container: microsoft/dotnet:2.1.301-sdk
using Npgsql;
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Threading.Tasks;
namespace npgsqlTest
{
class Program
{
static async Task Main(string[] args)
{
DAL dal = new DAL();
dal.Prepare();
var tasks = dal.Users.Select(async user =>
{
Console.WriteLine(await dal.RunTest(user));
});
await Task.WhenAll(tasks);
}
}
public class DAL
{
private static string _ConnectionString;
private NpgsqlConnection _Connection;
public List<string> Users { get; set; } = new List<string>();
public DAL()
{
_ConnectionString = $"Host=192.168.1.1;Username=admin;Port=5432;Password=password;Database=BigDB;";
_Connection = new NpgsqlConnection(_ConnectionString);
_Connection.Open();
}
public void Prepare()
{
string query = "SELECT username FROM usertable;";
using (var cmd = new NpgsqlCommand(query, _Connection))
{
var reader = cmd.ExecuteReader();
using (reader)
{
while (reader.Read())
{
Users.Add(reader[0].ToString());
}
}
}
}
public async Task<string> RunTest(string user)
{
var parameters = new Dictionary<string, Object> { { "username", user } };
var query = $"SELECT name FROM usertable WHERE username = (#username);";
var reader = await QueryAsync(query, parameters);
using (reader)
{
if (reader.HasRows)
{
while (await reader.ReadAsync())
{
var name = reader["name"];
if (!(hash is DBNull))
return (string)name;
}
}
}
return String.Empty;
}
public async Task<DbDataReader> QueryAsync(string query, Dictionary<string, Object> parameters)
{
using (var cmd = new NpgsqlCommand(query, _Connection))
{
foreach (var parameter in parameters)
{
cmd.Parameters.AddWithValue(parameter.Key, parameter.Value == null ? DBNull.Value : parameter.Value);
}
cmd.Prepare();
return await cmd.ExecuteReaderAsync();
}
}
}
}
I've used OData with entity framework to create a simple web service. Everything works fine. I just need to know how to add additional GET commands such as "GetByCategory" or "GetByDate". These will use different SQL views to return complex filtered results.
** WebApiConfig.cs *
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Web.Http;
using Microsoft.Owin.Security.OAuth;
using Newtonsoft.Json.Serialization;
using System.Web.Http.OData.Builder;
using System.Web.Http.OData.Extensions;
using NRHSData.Models;
namespace NRHSData
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Configure Web API to use only bearer token authentication.
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
config.AddODataQueryFilter();
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
ODataConventionModelBuilder builder = new ODataConventionModelBuilder();
builder.Namespace = "NRHSData";
builder.EntitySet<employee>("employees");
builder.EntitySet<vw_employee_listing>("vw_employee_listings");
builder.EntitySet<physician>("physicians");
builder.EntitySet<vw_physician_listing>("vw_physician_listings");
builder.EntitySet<employeedepartment>("employeedepartments");
builder.EntitySet<vw_employeedepartment_listing>("vw_employeedepartment_listings");
builder.EntitySet<news>("news");
builder.EntitySet<vw_news_listing>("vw_news_listings");
builder.EntitySet<vw_news_current>("vw_news_current");
config.Routes.MapODataServiceRoute("odata", "odata", builder.GetEdmModel());
}
}
}
** newsController.cs *
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.ModelBinding;
using System.Web.Http.OData;
using System.Web.Http.OData.Routing;
using NRHSData.Models;
namespace NRHSData.Controllers
{
[Authorize]
public class newsController : ODataController
{
private ENTDATAEntities db = new ENTDATAEntities();
// GET: odata/news
[EnableQuery]
public IQueryable<vw_news_listing> Getnews()
{
return db.vw_news_listings;
}
// GET: odata/news(5)
[EnableQuery]
public SingleResult<vw_news_listing> Getnews([FromODataUri] int key)
{
return SingleResult.Create(db.vw_news_listings.Where(news => news.newsid == key));
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
private bool newsExists(int key)
{
return db.news.Count(e => e.newsid == key) > 0;
}
// PUT: odata/news(5)
public async Task<IHttpActionResult> Put([FromODataUri] int key, Delta<news> patch)
{
Validate(patch.GetEntity());
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
news news = await db.news.FindAsync(key);
if (news == null)
{
return NotFound();
}
patch.Put(news);
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!newsExists(key))
{
return NotFound();
}
else
{
throw;
}
}
return Updated(news);
}
// POST: odata/news
public async Task<IHttpActionResult> Post(news news)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.news.Add(news);
await db.SaveChangesAsync();
return Created(news);
}
// PATCH: odata/news(5)
[AcceptVerbs("PATCH", "MERGE")]
public async Task<IHttpActionResult> Patch([FromODataUri] int key, Delta<news> patch)
{
Validate(patch.GetEntity());
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
news news = await db.news.FindAsync(key);
if (news == null)
{
return NotFound();
}
patch.Patch(news);
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!newsExists(key))
{
return NotFound();
}
else
{
throw;
}
}
return Updated(news);
}
// DELETE: odata/news(5)
public async Task<IHttpActionResult> Delete([FromODataUri] int key)
{
news news = await db.news.FindAsync(key);
if (news == null)
{
return NotFound();
}
db.news.Remove(news);
await db.SaveChangesAsync();
return StatusCode(HttpStatusCode.NoContent);
}
}
}
I assume you are trying to address such requests:
~/News/GetByDate()
There are 2 options:
Using OData Actions. Add an action named GetByDate and issue the Request through 'POST ~/News/GetByDate()' But it does NOT align with the protocol. An Action should have some side effect. So the second option comes.
Using OData Functions, which must not have side effect. To implement this, you need to upgrade to the new bits: System.Web.OData.dll. here is a sample about functions: https://aspnet.codeplex.com/SourceControl/latest#Samples/WebApi/OData/v4/ODataFunctionSample/ .
I use custom HttpClientHandler to authorize test if it is not authorized.
Windows Store Unit Test App project type is used
using Microsoft.WindowsAzure.MobileServices;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Windows.Security.Credentials;
using Windows.UI.Popups;
using XperiAndri.Efficiency.Common.Http.Handlers;
namespace XperiAndri.Efficiency.Common.Tests.Online
{
public class AuthenticationHandler : DelegatingHandler
{
private const string user = "";
private const string password = "";
private const string MobileServiceAuthenticationTokenHeader = "X-ZUMO-AUTH";
private readonly PasswordVault vault = new PasswordVault();
public IMobileServiceClient Client { get; set; }
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
var headers = request.Headers;
PasswordCredential credential = FindCredential();
if (credential != null)
{
Client.CurrentUser = new MobileServiceUser(user) { MobileServiceAuthenticationToken = credential.Password };
headers.Remove(MobileServiceAuthenticationTokenHeader);
credential.RetrievePassword();
headers.Add(MobileServiceAuthenticationTokenHeader, credential.Password);
response = await base.SendAsync(request, cancellationToken);
}
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
JObject content = new JObject { { "user", user }, { "password", password } };
var result = await Client.InvokeApiAsync("UnitTestLogin", content, HttpMethod.Post, null);
string token = result["token"].Value<string>();
if (Client.CurrentUser != null)
Client.CurrentUser.MobileServiceAuthenticationToken = token;
else
Client.CurrentUser = new MobileServiceUser(user) { MobileServiceAuthenticationToken = token };
headers.Remove(MobileServiceAuthenticationTokenHeader);
headers.Add(MobileServiceAuthenticationTokenHeader, token); // After execution of this line cancellationToken.IsCancellationRequested changes to true
// try again!
response = await base.SendAsync(request, cancellationToken);
if (response.StatusCode != HttpStatusCode.Unauthorized)
{
if (credential != null)
credential.Password = token;
else
{
credential = new PasswordCredential(Client.ApplicationUri.ToString(), user, token);
vault.Add(credential);
}
}
}
}
return response;
}
private PasswordCredential FindCredential()
{
try
{
return vault.FindAllByResource(Client.ApplicationUri.ToString()).FirstOrDefault();
}
catch
{
return null;
}
}
}
}
Look at the comment at the line
headers.Add(MobileServiceAuthenticationTokenHeader, token);
the second time it is executed.
Can somebody explain why does it happened? Why request becomes cancelled?
You're trying to add header to request after actually receiving response (since you already awaited SendAsync) which logically is not possible and causes cancellation of original request.