I am using web api with entity framework core. I have a Worker entity with relationships to Company (via CompanyId), Status (via StatusId) and Position (via PositionId). Using a request through the API I want to be able to update a worker.
I want to pass a request of WorkerId and PositionId only. In the data layer I check to find the PositiionId exists from the request, if not the return back to the controller with message of Position not found. Below is a sample of the code without the Company and Status checks.
Public void Update(WorkerEntity worker)
{
var workerRecord = _context.WorkerEntity.SingleOrDefault(w => w.Id == worker.Id);
if (workerRecord == null)
{
Log.Logger("Cannot find the worker with Id " + worker.Id);
return;
}
var positionRecord = _context.PositionEntity.SingleOrDefault(w => w.Id == worker.PositionId);
if (positionRecord == null)
{
Log.Logger("Cannot find the position with Id " + worker.PositionId);
return;
}
workerRecord.Position = positionRecord;
_context.SaveChanges();
}
I have also tried a simple approach using .Single to catch an error if the record does exist but the catch only output the generic "Sequence contains no elements" which isn't helpful in knowing with entity failed.
Is there an easier way to check the relationship Ids the user has added in the request are not bogus without having lines and code checking the Id exists? This table may have 3 relationships but I have another table that has 5 which would have 5 if null checks.
To get a meaningful message you would have to check each field manually. It can be simplified little bit using Any() method.
if (!context.PositionEntity.Any(w => w.Id == worker.PositionId)
{
Log.Logger("Cannot find the position with Id " + worker.PositionId);
return;
}
From the EF exception you wont be able to get a descriptive error message.
But you do you really want to do this? I assume you have these lookup values (Positions, statuses etc) as dropdowns in front-end where you load them with values from your database. So in the normal scenario an incorrect value cannot be selected and send to the API. However somehow if it does, you have the EF core default error to avoid screwing up things.
Cheers,
Related
In this moment I´m try to get a List of users and checks if the user is in the BD or not
I´m using Web API Net 6 and Sql Server
This is the code
[HttpPost("login")]
public async Task<ActionResult<string>> Login(LoginDto request)
{
//In this line I´m try to get the list of users (this bottom line doesn't work)
await _context.Users.ToListAsync();
if(user.UserName != request.UserName)
{
return BadRequest("User Not Found");
}
// ...
Here the problem is that the program has been running for 1 time until it works normally but when I end the session and come back again there is an application on the 2nd time it can no longer find the user in the database. My idea then is to add that line of code that just doesn't work (I don't know if it's due to await or if it's wrong to get through ListAsync() or if it's due to the user inside the if not being connected with the _context of the database )
By the way, that user is static having declared it like this
-> public static User user = new User();
Can anyone help me with this problem or tell me better solutions on how to get data from a table
If you just want to search your Users table for a user record with the name passed in the LoginDTO instance, then you just ask it to the database context to search for that name.
var userInDb = await _context.Users.FirstOrDefaultAsync(x => x.UserName == request.UserName);
if(userInDb == null)
... not found ....
But let me understand better your problem. If you are implementing your custom authorization and verification infrastructure for users, then think twice becase is not as simple as it looks. (For example, how do you store passwords in that table?) There is a dedicated library for that from Microsoft and is called ASP.NET Identity
I am using ASP.NET Entity Framework and I am trying to update a single column with the following code:
[HttpGet]
public void MarkOffline(string online)
{
Users user = new Users { email = online, isOnline = false };
db.Entry(user).Property("isOnline").IsModified = true;
db.SaveChanges();
}
But I get this error:
Member 'IsModified' cannot be called for property 'isOnline' because
the entity of type 'Users' does not exist in the context. To add an
entity to the context call the Add or Attach method of DbSet<Users>.
The part I don't know how to do:
To add an entity to the context call the Add or Attach method of
DbSet<Users>.
How do I fix my problem?
If you want to update like this, you'll need to Attach the entity where the entity has its primary key set.
Given you don't have its primary key but only one of its (unique, hopefully) fields, you need to query the record first and then update it:
var existing = db.Users.FirstOrDefault(u => u.email == email);
existing.IsOnline = true;
db.SaveChanges();
That being said, this is not how you record a user's online status in a web application. You rather update a user's LastAction timestamp with each action they perform, and mark a user as offline at runtime when their LastAction is more than N seconds or minutes ago.
While looking for a way to be able to assign and revoke roles via an admin controller for users other than the one making a request, I've implemented a custom IAuthorizeFilter that checks if Guid tag, stored as a Claim, matches to a value in the Entity Framework 7 Code First Identity table for UserClaims.
Essentials, it's this code:
public class RefreshUserClaimsFilterAttribute : IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext context)
{
var User = context.HttpContext.User;
var dbContext = context.HttpContext.ApplicationServices.GetRequiredService<ApplicationDbContext>();
var stampFromClaims = User.Claims.FirstOrDefault(Claim => Claim.Type == "ClaimsStamp")?.Value;
var stampFromDb = dbContext.UserClaims.Where(UserClaim => UserClaim.UserId == User.GetUserId()).ToList().FirstOrDefault(UserClaim => UserClaim.ClaimType == "ClaimsStamp")?.ClaimValue;
// Update claims via RefreshSignIn if necessary
}
}
I'm having the problem at the line where I'm assigning var stampFromDb, it could be much more readable in the following way:
var stampFromDb = dbContext.UserClaims.FirstOrDefault(UserClaim => UserClaim.UserId == User.GetUserId() && UserClaim.ClaimType == "ClaimsStamp")?.ClaimValue;
That, however, gives me cached (the same values as the actual claims from User.Identity) results and I could not find any documentation on this. My best guess is that the error is somewhere on my side, but I've never encountered such a problem before. This is the first time I'm using Asp.Net 5 and EF7. I'm using the default connection (LocalDB) to SQL Server 12.0.2000.
Is this a feature and, if yes, can it be turned off or did I make a mistake somewhere?
The issue was caused due to there being two different ways to create a service via dependency injection:
The sample code in my question used
var dbContext = context.HttpContext.ApplicationServices.GetRequiredService<ApplicationDbContext>();
where it should use
var dbContext = context.HttpContext.RequestServices.GetRequiredService<ApplicationDbContext>();
The difference here is between ApplicationServices and RequestServices. It looks like the ApplicationServices injector does have an instance of the database context somewhere which has had the DbSet filled earlier and therefore returning cached data instead of doing a database query.
I already asked a similar question to this but i can't even look the code i write, it looks horrible. There is something wrong.
I'm trying to create a simple web application with Visual Studio, ASP.Net Web Pages and Entity Framework. People are not familiar with Web Pages, it's basically a development environment like classic ASP and PHP.
I have two tables, one is workers and other one is overhours. I created models for both, they are related so every overhour record has one worker.
Basically i'm using this code:
if (IsPost)
{
try
{
Worker curWorker = new Worker();
try
{
curWorker = m.Workers.Find(decimal.Parse(Request.Form["WorkerId"]));
}
catch (Exception)
{
errors += "Please select a worker.";
}
try
{
overhour.OverhourAmount = decimal.Parse(Request.Form["OverhourAmount"]);
if (overhour.OverhourAmount == 0)
{
throw new Exception();
}
}
catch (Exception)
{
errors += "Hour field should be numerical and non-zero.";
}
overhour.Worker = curWorker;
overhour.OverhourDate = DateTime.Today;
curWorker.Overhours.Add(overhour);
if (errors != "")
{
throw new WrongValueException(errors);
}
m.SaveChanges();
Response.Redirect(Page.ParentPage);
}
catch (DbEntityValidationException ex)
{
errors = kStatic.getValidationErrors(ex.EntityValidationErrors, "<br />");
}
catch (WrongValueException ex)
{
errors = ex.Message.ToString();
}
catch (Exception ex)
{
errors = "Critical error, technical details: " + ex.Message;
}
}
Form has a combobox with all workers named WorkerId. It works but i have a few problems.
I hate validation method. I need to validate if user has selected a valid worker from the combobox because it has an option named "Select a worker" and it's value is empty string, so i need to check if it's numerical. I can include a [Regex..] code to my model class but it doesn't matter because there will be an error when i try to assign string to decimal field (decimal WorkerId). I can catch the exception but it will be likely a mismatch exception. I need more details.
Same thing with OverhourAmount, it should be numerical and non-zero too.
I don't like putting this code into the page code itself. I can create a repository class with methods like r.addOverhour but people say it's unnecesary. Is it unnecesary for MVC or if you're using Entity Framework, you shouldn't use an extra repository class.
I want to check the database for some validation before saving changes. For example, an user (user who has username and password, not worker) shouldn't be able to create a record about a worker if they are not in same building. For example, user A works in building X, and worker H works in building Y, user A shouldn't be able to create any data related to worker H. So i need to check if they work in same building before adding the record. I have BranchId field in both user and worker tables, i can check that easily but where?
Basically i don't know how to structure my code. I think i'm missing something big here because everybody validates their data and filter their inputs.
Thanks
Decorate your dtos with validation. Modelbinding will verify your data automatically and if failed will return the property that fails validation and the reason which you can customize. You business rules need to happen in a logic layer which will basically operate on your data models and return dtos. Your logic layer will accept dtos and this will provide separation from the data layer from the web API/mvc
I have DataService where T is an EntityFramework DbContext class
My client app is a Windows Forms app with a Service Reference.
What is the best approach to get a single entity from the service?
This code works:
var uri = new Uri("http://localhost/ProductService.svc/");
var context = new ProductContext(uri);
var result = context.Products.Where(x => x.ProductId == 123).FirstOrDefault();
However, it works because the product exists. That is because I can see the result by executing
http://localhost/ProductService.svc/Products(123)
in the browser. If I want to query product 123456, which does not exist in the database
http://localhost/ProductService.svc/Products(123456)
I see an errortext ` Resource not found for the segment 'Products'
The thing is, on the client side I get an exception but I would expect FirstOrDefault() to be null instead. Sure I could use some exception handling, but I am wondering if my approach is correct or if there is a better way to fetch a single object.
Update: Found the solution here https://stackoverflow.com/a/5987733/98491
The key is to set
context.IgnoreResourceNotFoundException = true;
Now SingleOrDefault() and FirstOrDefault() behave like I would expect. But I am still wondering if this is the right decision because in a browser
http://localhost/ProductService.svc/Prodducts(1)
(notice the typo) throws the same ResourceNotFound exception