Sending custom class via SignalR to Blazor WebAssembly - signalr

I have issue with sending custom class via SignalR hub to client (Blazor WebAssembly).
I have worker process, that is periodically sending data to all connected clients. If I try to send standard data, string, List of strings, Date etc. Client can process it without any problems.
But when I try to send my own "data class" I am receiving this error
Microsoft.AspNetCore.SignalR.Client.HubConnection[57]
Failed to bind arguments received in invocation '(null)' of 'ReceiveProject'.
System.IO.InvalidDataException: Error binding arguments. Make sure that the types of the provided values match the types of the hub method being invoked. ---> System.NotSupportedException: Deserialization of reference types without parameterless constructor is not supported. Type 'SekvenceReport.Shared.Project'
Here is my custom class
Custom class
public class Project
{
public Project(string name, string[] dbtable)
{
this.Name = name;
this.DbTable = dbtable;
this.Slbs = new List<Slb>();
this.Duplicity = true;
this.EdiError = true;
}
public string Name { get; set; }
public string[] DbTable { get; set; }
public List<Slb> Slbs { get; set; }
public bool Duplicity { get; set; }
public bool EdiError { get; set; }
}
public class Slb
{
public Slb(string slbid)
{
this.SlbId = slbid;
this.Sekvences = new List<Sequence>();
this.Status = "Připraveno";
}
public Slb()
{
}
public string SlbId { get; set; }
public List<Sequence> Sekvences { get; set; }
public string Status { get; set; }
public string StatusAsn { get; set; }
public string StatusEvaluation()
{
string eval = this.Status;
if (this.Sekvences.Any(i => i.Status == "D") && this.Sekvences.Any(i => i.Status == "N"))
{
eval = "Sekvencování";
this.Status = eval;
return this.Status;
}
else if (this.Sekvences.Any(i => i.Status == "D"))
{
eval = "Uvolněno";
this.Status = eval;
return this.Status;
}
else if (this.Sekvences.Any(i => i.Status == "N"))
{
if (this.Sekvences.Any(i => i.LoadingStatus == true) && this.Sekvences.Any(i => i.LoadingStatus == false))
{
eval = "Nakládání";
this.Status = eval;
return this.Status;
}
else if (this.Sekvences.Any(i => i.LoadingStatus == false))
{
eval = "Nasekvencováno";
this.Status = eval;
return this.Status;
}
else if (this.Sekvences.Any(i => i.LoadingStatus == true))
{
eval = "Naloženo";
this.Status = eval;
return this.Status;
}
}
eval = "Neznámý stav";
this.Status = eval;
return eval;
}
}
How server is sending data from Worker
await _projectHubContext.Clients.All.SendAsync("ReceiveProject", Data.projects);
How client is reading data
hubConnection = new HubConnectionBuilder()
.WithUrl(NavigationManager.ToAbsoluteUri("/projecthub"))
.ConfigureLogging(logging => logging.AddProvider(LoggerProvider))
.Build();
//Processing custom class (List of Projects)
hubConnection.On<List<Project>>("ReceiveProject", (listProjects) =>
{
var vars = listProjects;
//projects.AddRange(listProjects);
projects.AddRange(vars);
StateHasChanged();
});

Try adding a constructor to your Project class without parameters, for example:
public class Project
{
public Project()
{
}
// Rest of class
}

Related

How do I prevent a duplicate entry for a Create/Edit Functionality in ASP.NET Core w/ EF?

I am trying to prevent a user from creating a form with a FirstName, LastName, and DOB that match an entry in the database and editing a form to match an existing entry. If you could also lead me to how I can show an error when this happens, that would be awesome.
My Model:
public class MRegForm
{
public int MRegFormId { get; set; }
[Display(Name = "First Name")]
public string FirstName { get; set; } = string.Empty;
[Display(Name = "Last Name")]
public string LastName { get; set; } = string.Empty;
public DateTime DOB { get; set; }
[I tried Index attribute. It did not work for me. I was able to create new duplicate forms with no issues.
[Index(nameof(FirstName), nameof(LastName), nameof(DOB), IsUnique = true)]
public class MRegForm
{
I also tried this. Same thing.
protected override void OnModelCreating(ModelBuilder modelbuilder)
{
base.OnModelCreating(modelbuilder);
modelbuilder.Entity<MRegForm>()
.HasIndex(x => new { x.FirstName, x.LastName, x.DOB})
.IsUnique();
}
public DbSet<MRegForm> MRegForm { get; set; } = default!;
I think that there is maybe a way to prevent this in the OnPostAsync()
This is my create OnPostAsync():
public async Task<IActionResult> OnPostAsync()
{
MRegForm.CreatorId = UserManager.GetUserId(User);
var isAuthorized = await AuthorizationService.AuthorizeAsync(User, MRegForm, RegFormOperations.Create);
if (isAuthorized.Succeeded == false)
return Forbid();
Context.MRegForm.Add(MRegForm);
await Context.SaveChangesAsync();
return RedirectToPage("./Index");
}
This is my Edit OnPostAsync():
public async Task<IActionResult> OnPostAsync(int id)
{
var mRegForm = await Context.MRegForm.AsNoTracking().SingleOrDefaultAsync(m => m.MRegFormId == id);
if (mRegForm == null)
return NotFound();
MRegForm.CreatorId = mRegForm.CreatorId;
var isAuthorized = await AuthorizationService.AuthorizeAsync(User, MRegForm, RegFormOperations.Update);
if (isAuthorized.Succeeded == false)
return Forbid();
MRegForm.Status = mRegForm.Status; // the Status is the current Status - Do Not Reset
Context.Attach(MRegForm).State = EntityState.Modified;
try
{
await Context.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (!MRegFormExists(MRegForm.MRegFormId))
{
return NotFound();
}
else
{
throw;
}
}
return RedirectToPage("./Index");
}
private bool MRegFormExists(int id)
{
return (Context.MRegForm?.Any(e => e.MRegFormId == id)).GetValueOrDefault();
}
}
You can try to download the entity from the database if exists and make changes to it or creating new one if not exist.
Your clients can always make new MRegForm in the form, but you add or update in the back and. Bether option will be to pass the existing MRegForm to the form and the client see and change all props he need.
public async Task AddOrUpdate(MRegForm input)
{
var mRegForm = await Context.MRegForm
.FirstOrDefaltAsync(x => x.FirstName == input.FirstName && x.LastName == input.LastName && x.DOB == input.YourDate);
if(mRegForm != null)
{
//Make changes on mRegForm
mRegForm.SomeProp = input.SomeProp,
...
}
else
{
var newMRegForm = new MRegForm
{
//Set all props you need
}
await this.Context.AddAsync(newMRegForm );
}
await this.Context.SaveCangesAsync();
}

How to update entity desipite the row is loaded in EF change tracker or not in entity framework?

I allready look out everything of that problem but none of them worked for me.Everyone is suggestion to use AsNoTracking() for that problem but its has no sense with my problem because im not updating the data which i call from my database.
I have company profile update modal, this company can have profile photo or not but either way i need to update those informations. So that's why i need to control is comapny creating a photo or updating a photo. Let me show u to my code in the bellow :
#region /*UpdateCompanyProfile*/
[HttpPost]
public IActionResult UpdateCompanyProfile(Company company, List<IFormFile> files, int FileID)
{
try
{
if (ModelState.IsValid)
{
company.ModifiedDate = DateTime.Now;
_unitOfWorkC.RepositoryCompany.Update(company);
int firstRequest = HttpContext.Response.StatusCode;
if (firstRequest == 200)
{
_unitOfWorkC.Complete();
if (files.Count != 0)
{
var File = _fileUploader.FileUploadToDatabase(files);
var FileResult = File.Result;
FileResult.CompanyID = company.CompanyID;
if (FileID == 0)//That's the point where i control that file, is it gonna be update or create.
{
_unitOfWorkFR.RepositoryFileRepo.Create(FileResult);
int secondRequest1 = HttpContext.Response.StatusCode;
if (secondRequest1 == 200)
{
int tryCatch = _unitOfWorkFR.Complete();
if (tryCatch != 15)
{
TempData["JS"] = "showSuccess();";
}
else
{
TempData["JS"] = "showError();";
}
}
}
else
{
FileResult.FileID = FileID;
_unitOfWorkFR.RepositoryFileRepo.Update(FileResult); //That's the point where i get the error.
int secondRequest2 = HttpContext.Response.StatusCode;
if (secondRequest2 == 200)
{
int tryCatch2 = _unitOfWorkFR.Complete();
if (tryCatch2 != 15)
{
TempData["JS"] = "showSuccess();";
}
else
{
TempData["JS"] = "showError();";
}
}
else
{
TempData["JS"] = "showError();";
}
}
}
}
else
{
TempData["Message"] = "?irket g?ncelleme i?leminiz ba?ar?s?z!";
TempData["JS"] = "showError();";
return RedirectToAction("CompanyProfile");
}
}
else
{
TempData["Message"] = "G??ncellemek istedi?iniz veri hatal?!";
TempData["JS"] = "showError();";
return RedirectToAction("CompanyProfile");
}
}
catch (Exception ex)
{
var log = _logging.Logging(ex.Message, "Exception/Hata", company.CompanyID.ToString(),
"CompanyProfile/UpdateCompanyProfile", getCurrentUser(), getCurrentUserClaimRole());
_unitOfWorkLog.RepositoryLog.Create(log);
_unitOfWorkLog.Complete();
//TempData["Message"] = ex.Message;
//TempData["JS"] = "showError();";
return RedirectToAction("CompanyProfile");
}
}
#endregion
As u can see, calling that data with AsNoTracking() has no sense in my stuation. I'm only getting that error in that action,so other FileRepo actions are working well.
That's my FileUploadToDatabase() method :
public async Task<FileRepo> FileUploadToDatabase(List<IFormFile> files)
{
foreach (var file in files)
{
var fileName = Path.GetFileNameWithoutExtension(file.FileName);
var fileExtension = Path.GetExtension(file.FileName);
_fileRepo = new FileRepo
{
FileName = fileName,
FileExtension = fileExtension,
FileType = file.ContentType,
CreatedDate= DateTime.Now
};
using (var dataStream = new MemoryStream())
{
await file.CopyToAsync(dataStream);
_fileRepo.FileData = dataStream.ToArray();
}
}
return _fileRepo;
}
And that's my FileRepo class :
public class FileRepo : Base
{
[Key]
public int FileID { get; set; }
[Required(ErrorMessage = "Required Field !")]
public string FileName { get; set; }
[Required(ErrorMessage = "Required Field !")]
public string FileType { get; set; }
[Required(ErrorMessage = "Required Field !")]
public string FileExtension { get; set; }
public string FilePath { get; set; }
public bool FilePhotoIsDefault { get; set; }
public byte[] FileData { get; set; }
public int? CompanyID { get; set; }
public Company Company { get; set; }
#endregion
}
That's my UnitOfWork :
and This is my Repository :
This is the query for my Update Modal:
public IEnumerable<Company> GetByIDForCompanyProfileCompany(int ID)
{
return TradeTurkDBContext.Companies.Where(x => x.CompanyID == ID)
.Include(x => x.Addresses.Where(x => x.IsDeleted == null || x.IsDeleted == false))
//
.Include(x => x.Products.Where(x => x.IsDeleted == null || x.IsDeleted == false))
.ThenInclude(x => x.FileRepos.Where(x => x.IsDeleted == null || x.IsDeleted == false)).AsSplitQuery()
//
.AsNoTrackingWithIdentityResolution().ToList();
}
For updating FileResult you are using DbSet.Update - it is trying to attach entity to ChangeTracker. Attaching will fail if there is already attached object with the same key.
Change your repository to the following. It will update all fields if entity is not in ChangeTracker, otherwise it will correct only needed properties:
public void Update(T model)
{
if (model == null)
throw new ArgumentNullException(nameof(model));
// I hope your generic repository knows Model Id property
var entry = _context.ChangeTracker.Entries<T>().FirstOrDefault(e => e.Entity.Id == model.Id);
if (entry == null)
{
// entity not tracked, so attach it
_dbSet.Update(model);
}
else
{
// setting values from not tracked object
if (!ReferenceEquals(model, entry.Entity))
entry.CurrentValues.SetValues(model);
}
}
UPDATE
If generic repository don't know about Id property, you can define interface for that:
public interface IEntityWithId
{
int Id {get;}
}
Make sure that your classes is implementation of IEntityWithId. Then correct Repository definition:
public interface IRepository<T> where T: class, IEntityWithId
{
...
}

Issue getting NullReferenceException using Http with jwt Xamarin Forms Android

So my code works in PostMan querying the api to populate a listview locally in my Android app. But when I run it from within the app, I get NullReferenceException on the line "Items.Clear() in ShipViewModel.cs
I tried hardcoding the address rather than using my APIQueriable path, I tried generating new JWT, and I tried manually cleaning my /bin /obj folders to rule out code not compiling correctly.
ShipsViewModel.cs Xamarin.Forms/ViewModels
{
private GallogClient _gallogClient;
public ObservableCollection<ShipCatalog> Items { get; set; }
public Command LoadItemsCommand { get; }
public ShipsViewModel()
{
Title = "Ships";
_gallogClient = new GallogClient("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC9hcGkuZ2FsbG9nLmNvIiwiYXVkIjoiaHR0cDpcL1wvYXBpLmdhbGxvZy5jbyIsImlhdCI6MTM1Njk5OTUyNCwibmJmIjoxMzU3MDAwMDAwLCJkYXRhIjp7ImlkIjo1NywidXNlcm5hbWUiOiJQYXJhIiwiaGFuZGxlIjoiUGFyYSIsImVtYWlsIjoicGFyYWJvbGE5NDlAZ21haWwuY29tIn19.bRpI9hVy-Spky5pbZhJCkyN-MT9RA6ap_yD9ezRxCxo");
LoadItemsCommand = new Command(async () => await ExecuteLoadItemsCommand(), () => !IsBusy);
}
async Task ExecuteLoadItemsCommand()
{
if (IsBusy)
return;
IsBusy = true;
try
{
LoadItemsCommand.ChangeCanExecute();
Items.Clear();
var items = await _gallogClient.GetItemsAsync<ShipList>();
foreach (var item in items.ships.ToList())
{
Items.Add(item);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
finally
{
IsBusy = false;
}
}
}
}
GallogClient.cs Gallog.API
{
internal readonly HttpClient Client = new HttpClient { BaseAddress = new Uri("https://api.gallog.co/api/") };
public string Jwt { get; set; }
public GallogClient()
{
}
public GallogClient(string jwt)
{
Jwt = jwt;
}
private StringContent JwtContent
{
get {
return new StringContent(JsonConvert.SerializeObject(new
{
jwt = Jwt
}), Encoding.UTF8, "application/json");
}
}
//...
public async Task<T> GetItemAsync<T>(string name) where T : ApiQueryable
{
return await PostAsync<T>($"{GetPath<T>()}/{name}");
}
public async Task<T> GetItemsAsync<T>() where T : ApiQueryable
{
return await PostAsync<T>($"{GetPath<T>()}");
}
internal string GetPath<T>()
{
if (typeof(T).GetCustomAttributes(
typeof(ApiPathAttribute), true
).FirstOrDefault() is ApiPathAttribute at)
{
return at.Path;
}
return null;
}
public async Task<T> PostAsync<T>(string path) where T : ApiQueryable
{
var response = await Client.PostAsync(path, JwtContent);
return JsonConvert.DeserializeObject<T>(await response.Content.ReadAsStringAsync());
}
public async Task<T> PostAsync<T>(object body, string path) where T : ApiQueryable
{
var response = await Client.PostAsync(path,
new StringContent(JsonConvert.SerializeObject(body), Encoding.UTF8, "application/json"));
return JsonConvert.DeserializeObject<T>(await response.Content.ReadAsStringAsync());
}
}
}
ShipList.cs Xamarin.Forms/Models
{
[ApiPath("ships")]
public class ShipList : ApiQueryable
{
public ShipCatalog[] ships { get; set; }
}
public class ShipCatalog
{
public int id { get; set; }
public string name { get; set; }
// ...etc etc
}
}
Items is null because you declared it but have never initialized it
public ShipsViewModel()
{
...
Items = new ObservableCollection<ShipCatalog>();
...
}

Unit tests fails after upgrading to .net core 2

Can someone maybe explain to me what this means and why am i getting it.
System.InvalidOperationException : When called from 'VisitLambda',
rewriting a node of type 'System.Linq.Expressions.ParameterExpression'
must return a non-null value of the same type. Alternatively, override
'VisitLambda' and change it to not visit children of this type.
I am getting it from my unit tests I am running the latest .net core 2 with EF core. all my tests were fine till i upgraded then i started getting the error.
The funny thing is, is that when i run the project the line were it fails in the the tests is ok.
This is my Test
[Fact]
public async Task GetUserProfileAsync_Where_Employee_Exist_Test()
{
// Given
var user = TestPrincipal.CreatePrincipalForEmployeeUser();
using (var factory = new TestContextFactory())
using (var context = factory.CreateInMemoryDatabase<ApplicationContext>())
{
this.SetDependencies(context);
var data = EmployeeValueHelper.GetEmployeeValues();
context.AddRange(data);
context.SaveChanges();
var sut = new ProfileService(new DbContextRepository<Data.Models.Employees.Employee>(context), this.userService, this.moqEmploymentStatusService.Object);
// When
// -> this method goes to a service and calls the below FindByIdAsync
var actual = await sut.GetProfileForUserAsync(user);
// Then
Assert.Equal(10, actual.EmployeeId);
}
}
public async Task<Employee> FindByIdAsync(long id)
{
var profile = await this.repository.Set
.Include(_ => _.Address) --> IT FAILS ON THIS LINE, IF I REMOVE THE INCLUDE THEN IT WORKS
.Include(_ => _.EmployeeImage)
.SingleOrDefaultAsync(_ => _.EmployeeId == id);
if (profile == null)
{
return null;
}
return profile;
}
UPDATE
Service Layer
public class ProfileService : GenericService<Employee>, IProfileService
{
private readonly DbContextRepository<Employee> repository;
private readonly IUserService userService;
public ProfileService(DbContextRepository<Employee> repository, IUserService userService)
: base(repository)
{
this.repository = repository;
this.userService = userService;
}
public Task<Employee> GetProfileForUserAsync(ClaimsPrincipal user)
{
var id = this.userService.GetEmployeeId(user);
return id.HasValue ? this.FindByIdAsync(id.Value) : null;
}
public async Task<Employee> FindByIdAsync(long id)
{
var profile = await this.repository.Set
.Include(_ => _.Address)
.Include(_ => _.EmployeeImage)
.SingleOrDefaultAsync(_ => _.EmployeeId == id);
if (profile == null)
{
return null;
}
return profile;
}
}
Employee Model
public class Employee : IValidatableObject
{
[Key]
[Column("pkEmpID")]
public long EmployeeId { get; set; }
[Column("fkCompanyID")]
public long CompanyId { get; set; }
public virtual Company Company { get; set; }
[Display(Name = "lblEmpNumber")]
public string EmpNumber { get; set; }
public virtual IList<Address> Address { get; set; } = new List<Address>();
// WITH SOME EXTRA STUFF NOT NEEDED FOR THIS
}
Repository
public class DbContextRepository<TEntity> : IGenericRepository<TEntity>, IDisposable
where TEntity : class
{
public DbContextRepository(ApplicationContext context)
{
this.Context = context;
this.Set = context.Set<TEntity>();
this.SetWithNoTracking = this.Set.AsNoTracking();
}
public ApplicationContext Context { get; }
public DbSet<TEntity> Set { get; }
public IQueryable<TEntity> SetWithNoTracking { get; }
// WITH SOME EXTRA STUFF NOT NEEDED FOR THIS
}
Hope this will shed more light

How to consume this web service?

This is my first time creating a web service. I am not sure if my implementation is incorrect, but I am trying to use much like a class. The problem is that when I am trying to consume I am getting confused and not being able to set the values of the properties.
here is the web service.
public class Service1 : System.Web.Services.WebService
{
private bool _isUserActive { get; set; }
private bool _isCredentialValid { get; set; }
public string email { get; set; }
public string pass { get; set; }
public int customerID { get; set; }
[WebMethod]
public bool VerifyUserCredential()
{
bool result = false;
PURLDataContext purl = new PURLDataContext();
try
{
var res = purl.Sel_User(email.ToLower(), pass);
if (res != null)
result = true;
_isUserActive = true;
_isCredentialValid = true;
}
catch (Exception ex)
{
if (ex.Message == "Account is inactive, please contact your administrator!")
{
_isUserActive = false;
_isCredentialValid = false;
}
else
_isCredentialValid = false;
//Invalid credentials.
}
return result;
}
[WebMethod]
public ArrayList retrieveCustomerInfo()
{
ArrayList customerInfo = new ArrayList();
string validate = "Please Validate";
if (_isCredentialValid)
{
PURLDataContext purl = new PURLDataContext();
var customer = purl.Sel_Recipient(customerID);
foreach (var c in customer)
{
customerInfo.Add(c);
}
}
else
customerInfo.Add(validate);
return customerInfo;
}
}
Here is what I am trying to do to consume.
PURLServices.Service1SoapClient webserv = new Service1SoapClient();
bool result;
ArrayOfAnyType array = new ArrayOfAnyType();
webserv.email = "email#email.com";
webserv.pass = "pass";
webserv.customerID = 12345;
result = webserv.VerifyUserCredential();
array = webserv.retrieveCustomerInfo();
Thank you for any help/
You do not want to try to use properties like this. Your method should look more like this:
public bool VerifyUserCredential(string userName, string password)
{
// method body here
}
Probably you would want to return an access token of some sort that the server will cache. This can then be passed into other methods to show that the user is valid.

Resources