I am trying to recover the value of the user who is logged on the site but I am currently blocked.
I use .Net core 2.1 with EF Core. I created a context called jakformulaireContext and also jakformulaireUser.
I have to send an email to confirm that a user has registered to an event but I can not find the piece of code allowing me to recover the field "email" which is in the database.
In the scaffolding part which contains the Register controller I succeeded because I would recover the value of the field "email" but here I am in a controller that is located outside of Account.
So I tried to do a find but it does not seem to be the right method because I get a Task <> while I just want a string.
var user = _userManager.FindByEmailAsync ("jon#live.be");
Would you have a solution?
If you have an async method it will return a Task. So what you need to do is wait until that method is finished. The way to do it is:
var user = await _userManager.FindByEmailAsync ("jon#live.be");
More information about async/wait
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
How can I retrieve all users within a Service Principal with navigation properties such as Manager and properties such as Full name using Microsoft Graph API in as few as possible calls?
I'm using the Microsoft.Graph package and I've tried:
Attempt 1:
await graphClient.ServicePrincipals["objectId"].AppRoleAssignedTo.Request().GetAsync();
But this gives me only the Ids, which is 'okay' but then I would have to get all users separately, which would make for a lot of calls to the Graph API.
I could use the DirectoryObjects to get all users at once but that does not allow me to expand on the Manager property.
For example:
var users = await graphClient.DirectoryObjects.GetByIds(principals.Select(p => p.PrincipalId.ToString()), new string[1] { "User" }).Request().Expand("manager").PostAsync();
Gives me a error:
Could not find a property named 'manager' on type 'microsoft.graph.directoryObject'.
Attempt 2:
I've also tried to get the users by using a filter
await graphClient.Users.Request().Filter("appRoleAssignments/any(u:u/principalId eq objectId)").Expand(u => u.Manager).GetAsync();
But that gives me a error:
Request_UnsupportedQuery
You can write the code like this:
var user = await graphClient.Users.Request().Filter("id in ('{objectId1}', '{objectId2}', ..., '{objectIdn}')").Expand(u => u.Manager).Select("displayName").GetAsync();
Ideally, this will use just one call to return the data you need.
BUT based on the requirement "in as few as possible calls", we will have to face a problem, that is, we need to put all the object ids into the request, which may cause the request to be too long and eventually fail.
I didn't test this scene and it's just a direction which may be helpful.
I still recommend that you use loop to get the information.
So I have multiple websites running off one code base (asp.net standard MVC). I'm using the built in ASPNet.Identity methods for users to register/log in (using ApplicationSignInManager, ApplicationUserManager).
Currently, all websites are using a single database to store user information. This is causing a couple of issues:
When user A registers on website A, they are now able to log into website B with the same details. As far as they are aware, they did not register on website B. Not good!
If I constrain user A to only access website A, if that user then tried to register on website B, they get the 'email address already in use' error. Also not good!
I've tried separating the databases, one per site, to get around this issue but I don't know how to dynamically change the DBContext assigned to the ApplicationSignInManager and ApplicationUserManager in my controller.
For example, when a user comes to website A, I grab the connection string for website A and perform login/register actions. I need the domain name to work out which connection string to load, which I can't access until after startup.cs code has run, configuring my manager instances.
I figure other people must have done this. Ideally I need to dynamically change the DBContext AFTER Startup.cs has run. Failing that, I need a nice approach to storing multiple identical email addresses in the same DB
Thanks
If you have a flag somewhere which website the current context is for, i'd say easiest to achieve that is to do two things:
extend the IdentityUser with a Website property, something simple either just an int WebsiteId or a String.
extend the AccountController to use that property wherever needed, I think you'd need to modify "Register" and all "Login" functions to verify the website the account is for.
I managed to find a solution. It's not the most elegant but it solves the issue until I can figure out how to dynamically change the DB Context used during login/register
In IdentityConfig.cs I switched 'RequireUniqueEmail' to false:
manager.UserValidator = new UserValidator<ApplicationUser>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = false
};
Then when a user registers, I take the email address, make it unique to the website they registered on, and store it in the UserName field (I don't use the UserName for anything). When they log in, I make the same alteration to the entered email address before attempting login.
If this user registers on a different website, the username will be different even though the email is identical. Not perfect, but it works.
E.g.
string uniqueCode = "Website_Specific_String";
var user = new ApplicationUser { uniqueCode + model.Email, Email = model.Email};
var result = await UserManager.CreateAsync(user, model.Password);
and the login
var user = await UserManager.FindByNameAsync(uniqueCode + model.Email);
Still open to better ideas. Thanks
I am trying to give my LUIS the missing piece of information which it requires when the query has missing item. For example.
if someone says, i want to make an order. For this i need to know how many users. The LUIS on https://api.projectoxford.ai/luis/v2.0/apps/ automatically creates the context id when that ensures i am talking to this query.
How can i get or tell the same thing in Bot Framework when the LUIS Dialog Prompts the question for missing thing.
This is the code
[Serializable]
[LuisModel("something", "something")]
public class SimpleLUISDialog : LuisDialog<object>
{
[LuisIntent("GetQuote")]
public async Task GetQuote(IDialogContext context, LuisResult result)
{
PromptDialog.text(context, GetChildNumberAsync, "How many Users will you be adding ?", "Sorry please try again", 2);
}
}
private async Task GetUserNumberAsync(IDialogContext context, IAwaitable<string> result)
{
// send to LUIS again for checking the entity for number of users with Context ID
}
}
This is what is written is LUIS Dialog on API (JSON)
"dialog": {
"prompt": "How many users needed?",
"parameterName": "NumberOfLicenses",
"parameterType": "number",
"contextId": "746024ff-a4eb-4f58-b014-42605a3cb757",
"status": "Question"
}
It seems you are using ActionParameters in your LuisModel and that you are trying to fulfill them if they are not provided in the original message.
While you could do some "manual hacks" to call LUIS again as explained here (not with the context id, but simulating a new message), I wouldn't recommend that.
Instead, I would encourage you to take a look to the BotBuilder's develop branch (see commits from Nov-11) where the BotFramework team added support for LUIS v2 API and added some brand new capabilities; one of them, I believe is exactly what you are looking for.
With the latest changes, the LuisDialog will now act if your intent requires parameters and those are not provided. In that scenario, the LuisDialog will automatically launch and a LuisActionDialog and ask the user for the missing parameter, using the Prompt message you defined in the action parameter.
The last time I checked this wasn't published as NuGet package yet; but it might be worth checking again. In the worst case, you can temporarily download the BotBuilder's code and reference that in your project.
You will need to specify the API Version in your LuisModel attribute to start suing it.
[LuisModel("something", "something", LuisApiVersion.V2)]
My setup:
ASP.NET 4.5 web api (on Azure) saving data to SQL db (also on Azure)
AngularJS web front end (another Azure web site)
When a user first signs up, I show them a "getting started intro". The intro is only supposed to run once - I log the timestamp of the intro launch date as a custom field in the ASP.NET user table.
Imagine my surprise when I log in (as a user would) and see the intro TWICE.
The AngularJS front end is properly sending the "intro viewed" message to the ASP.NET api, and the api responds with a success message. However, when I look at the raw data in the db, the timestamp is most definitely NOT updated. Consequently, the user will see the intro a second time (at which point the timestamp gets recorded in the db properly).
I have a crappy workaround. After the client requests an OAuth Bearer token from my server, the client then requests user information (to decide whether or not to show the tour). Waiting 100ms and then sending the "tour viewed" message back to the server masks the issue.
I've not seen ANY other issues storing data at any point. Because our db is on Azure, I can't hook up Profiler and the built in auditing doesn't give me any clues.
Is there something about requesting the token that leaves ASP.NET identity in a funny state? And it takes a brief wait before you can write to the table? Are custom fields that extend the base Identity setup prone to problems like this? Is the UserManager possibly doing something weird in its black box?
Does anyone have suggestions for how to continue debugging this problem? Or ever hear of anything like it?
Here's the relevant code that should be updating the "tour viewed" timestamp in the db:
[HttpPost, Route("UserInfo")]
public async Task<IHttpActionResult> UpdateUserInfo(UpdateBindingModel model)
{
var currentUser = UserManager.FindById(User.Identity.GetUserId());
if (model.FirstName != null)
{
currentUser.FirstName = model.FirstName;
}
if (model.LastName != null)
{
currentUser.LastName = model.LastName;
}
if (model.SetIntroViewCompleteDate)
{
currentUser.IntroViewCompleteDate = DateTime.UtcNow;
}
if (model.SetIntroViewLaunchDate)
{
currentUser.IntroViewLaunchDate = DateTime.UtcNow;
}
if (model.SetTipTourCompleteDate)
{
currentUser.TipTourCompleteDate = DateTime.UtcNow;
}
if (model.SetTipTourLaunchDate)
{
currentUser.TipTourLaunchDate = DateTime.UtcNow;
}
IdentityResult result = await UserManager.UpdateAsync(currentUser);
if (result.Succeeded)
{
var data = new UserInfoViewModel
{
FirstName = currentUser.FirstName,
LastName = currentUser.LastName,
IntroViewLaunchDate = currentUser.IntroViewLaunchDate
};
return Ok(data);
}
return InternalServerError();
}
UPDATE ********* 4/18
I've also tried to move completely away from UserManager stuff. I've tried the following modifications (pulling the user data from a table like I would access any other data), but it still behaves the same. I'm starting to think that putting custom fields on the ApplicationUser object is a bad idea...
New db retrieve and save looks like this:
ApplicationDbContext newContext = new ApplicationDbContext();
var currentUser = await (from c in newContext.Users
where c.Email == User.Identity.Name
select c).SingleOrDefaultAsync();
//update some values
await newContext.SaveChangesAsync();
Basically the problem might be with initialization of the `UserManager' and the fact that this class works on the db context so you need to persist changes to that context. Here is an example:
var userStore = new UserStore<ApplicationUser>(new MyDbContext());
var userManager = new UserManager(userStore);
That way you remember both manager and context. Then in your method you would normally call:
IdentityResult result = await userManager.UpdateAsync(currentUser);
followed by persisting this change to db context:
var dbContext = userStore.context;
dbContext.saveChanges();
Based on your comment that waiting 100ms masks the issue, I think you may have a problem with the multiple async await calls. Try running the calls synchronously and see if you still have the same issue. My guess is that the problem might go away. My experience has been that using async await can be tricky when you have calls to asynchronous methods that call other asynchronous methods. You may have code that is executing without the proper results returned.
Well, here's what I did to solve the problem. I totally de-coupled my custom user data from the built in ASP.NET identity stuff. I've now got a separate object (and therefore separate SQL table) that stores things like FirstName, LastName, LastActiveDate, etc, etc.
This has solved my problem entirely, though it has introduced another call to the database in certain situations. I've deemed it to be not a big enough performance issue to worry about. I'm left thinking that this was some sort of weird race condition involving the generation of a token for an ASP.NET identity user then quickly writing to an Azure SQL database - lord knows what it was exactly in my code that caused the problem.
If you've got a problem that's hard to solve, often the best plan is to change the problem.
Now I need to find a meta thread discussing what to do with bounty points when you've blown up the problem...