I am pretty new to c#, EF Code First and all that, so my question might be an easy one.
I am trying to create a very simple login. Each user must have a type (admin or client). How can I bind the usertype to my user table without generating a new type each time I insert a new user in the DB?
Here are my code first class:
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public bool Active { get; set; }
public UserType TypeId { get; set; }
}
public class UserType
{
public int Id { get; set; }
public string Type { get; set; }
}
public enum TypeEnum
{
Admin,
Client
}
When I launch the app, I want to be able to create 2 tables:
Users which is empty
UserTypes which must contain 2 types (Admin and Client).
Then, everytime I register a new user, so everytime I add a user to the Users table, I want to be able to use the UserTypes table.
When I launch the app, I want to be able to create 2 tables...
Not sure if I understand this correctly but you can seed initial data into the database with EF Code-First. An example how to do that is here:
Entity Framework Inserting Initial Data On Rebuild
If you really want to recreate the tables with every launch of your application you can use the DropCreateDatabaseAlways<T> initializer as mentioned in the example.
Then, everytime I register a new user, so everytime I add a user to
the Users table, I want to be able to use the UserTypes table.
For this you would load the existing UserType you want to assign from the database so that it is attached to the context and then create the new user:
using (var context = new MyContext())
{
var userType = context.UserTypes
.Single(u => u.Type == TypeEnum.Admin.ToString());
var newUser = new User { TypeId = userType, Username = ... etc. };
context.Users.Add(newUser);
context.SaveChanges();
}
Attaching to the context - by either loading from the database or calling Attach explicitely - makes sure that the UserType is not duplicated, i.e. no INSERT command will be sent to the database for the UserType.
With new version of EF (currently beyond the offical 4.1 version: Entity Framework June 2011 CTP) you can also do that:
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public bool Active { get; set; }
public UserType Type { get; set; }
}
public enum UserType
{
Admin = 1,
Client = 2,
}
The data will be saved as integer in your database but within your application you can use your enum like this:
var newUser = new User { Type = UserType.Admin, Username = ... };
context.Users.Add(newUser);
context.SaveChanges();
Related
Fairly new to EF.Core and I'm having some issues as my tables start getting more complex. Here's an example of what I have defined for my classes. Note ... there are many more columns and tables than what I have defined below. I've paired them down for brevity.
public class User
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public bool Active { get; set; }
}
Followed by
public class JournalEntry
{
public int Id { get; set; }
public int UserId { get; set; }
public string Details { get; set; }
public DateTime DateEntered { get; set; }
public virtual User User { get; set; }
}
I want to be able to issue the following query and INCLUDE the User Table so that I can then populate a ViewModel with columns from the User Table without having to do another lookup and also to sort the data while retrieving it:
public IQueryable<JournalEntry> GetByUser(int userId)
{
return _DbContext.JournalEntries.Where(j => j.UserId == userId)
.Include(u => u.User)
.OrderBy(u=> u.User.FirstName)
.ThenBy(j => j.DateEntered);
}
My controller would then have something similar to the following:
public IActionResult List(int userId)
{
var journalEntries = new _dbRepository.GetByUser(userId);
var myViewModel = new MyViewModel();
myViewModel.UserName = ($"{journalEntries.User.FirstName} {journalEntries.User.LastName}");
myViewModel.Entries = journalEntries;
etc ....
return View(myViewModel);
}
I'm loading the user's first and last name in the View Model and whatever other attributes from the various tables that are referenced. The problem that I'm having is that I'm getting errors on the Migration creation "Foreign key constraint may cause cycle or multiple cascade paths." And of course, if I remove the line reading public virtual User User { get; set; } from the JournalEntry class then the problem goes away (as one would expect).
I believe that the way I'm doing the models is incorrect. What would be the recommended way that I should code these models? I've heard of "lazy loading". Is that what I should be moving towards?
Thanks a bunch.
--- Val
Your query returns an IQueryable<JournalEntry> not a JournalEntry.
Change the code to get the user details from the first object:
var myViewModel.UserName = ($"{journalEntries.First().User.FirstName} {journalEntries.First().User.LastName}");
In the line above I'm calling First() on your journal entries collection and that would have a User. Then I can access FirstName and LastName.
Also, don't bother with LazyLoading since you are learning. It could cause select n+1 issues if used incorrectly
I'm new to entity framework and even if i know how to do it in Merise, i can't do it using code first.
In an entity User, i should have a foreign Key 'Promotion_Id'
In an entity Promotion, i should have a foreign key 'Pilote_Id' that points out to the User entity.
Here is the thing : i also have a List in Promotion which is a list of all users in a promotion. Pilote_Id is the Id of the pilote of that formation, who's, of course, a user.
I tried the following :
public class User : EntityWithId
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public string Phone { get; set; }
public virtual Promotion Promotion { get; set; }
}
public class Promotion : EntityWithNameAndId
{
//Site is another entity, the place where the promotion is
public virtual Site Site { get; set; }
public List<User> Users { get; set; }
public virtual User Pilote { get; set; }
}
(Note : EntityWithId only contains an Id and EntityWithNameAndId inherits from EntityWithId and only contains a name)
But it only results in having 2 foreign keys in User named Promotion_Id and Promotion_Id1.
I already maked the whole thing work by changing
public virtual User Pilote { get; set; }
with
public virtual Guid PiloteId { get; set; }
But i want some consistency in my entities so.. Is there a correct way to achieve this ?
You will probably need to use explicit mapping to achieve this:
In the OnModelCreating() for your context:
modelBuilder.Entity<User>()
.HasOptional(u => u.Promotion)
.WithRequired(p => p.Pilote)
.Map(u => u.MapKey("PiloteId"); // EF6
// .HasForeignKey("PilotId") // EF Core
This assumes that a user may, or may not have a Promotion, but all promotions have a Pilot.
The Promotion.Users will probably map ok by convention using a UserId on the promotion table, but if there is any issue there:
However, there is a big caveat with this approach which relates to the schema, not EF. There is no restriction/guard that will ensure that the Pilot is one of the Users associated with the promotion. A PiloteId could point to any user, and that user's promotionId may be different.
In any case, the logic around managing who is the pilot will need to be done by code, but this means either checking IDs for valid combinations, or something like:
If a User can only be associated to 1 Promotion, and one user on that promotion can be the Pilot, then you could consider adding a flag to User called "IsPilot".
Then in Promotion:
public virtual ICollection<User> Users { get; set; } = new List<User>();
[NotMapped]
public User Pilote
{
get { return Users.SingleOrDefault(u => u.IsPilote); }
set
{
var newPilote = Users.Single(u => u.UserId == value.UserId); // Ensure the user nominated for Pilote is associated with this Promotion.
var existingPilote = Pilote;
if (existingPilote != null)
existingPilote.IsPilote = false;
newPilote.IsPilote = true;
}
}
If users can be assigned to multiple promotions then you will want to update the schema and mappings to support a many-to-many relationship between user and promotions, such as a UserPromotions table containing UserId and PromotionId. In this case I would consider assigning the IsPilote in this table / linking entity, but again this would need logic to ensure that rules around 1 pilot per promotion, and whether a user can be pilot for more than one promotion.
This is my first attempt to use the ASP.NET identity with the builtin authentication. All previous attempts resulted in having a manual check for user credentials and then setting FormsAuthentication AuthCookie. But I had some problems with a signalR connection not having authentication information this way. So I started from scratch with the builtin authentication.
So I extended my ApplicationUser object with some fields:
public class ApplicationUser : IdentityUser
{
public long SteamId { get; set; }
public string Avatar { get; internal set; }
public string Name { get; internal set; }
public int Credits { get; internal set; }
public long? DiscordId { get; internal set; }
public DateTime? LastLogin { get; internal set; }
public DateTime RegisterDate { get; internal set; }
}
These fields will create new columns in my AspNetUsers table. The problem is, I can't access these values in my views. For that I need to use claims if I understand correctly. These claims are stored in another table called AspNetUserClaims. So I have to add those claims to the user
await UserManager.AddClaimAsync(user.Id, new Claim("Avatar", user.Avatar));
and creating a extension method to get the avatar from the principal
public static class ClaimsPrincipalExtension
{
public static string GetAvatar(this ClaimsPrincipal principal)
{
var avatar = principal.Claims.FirstOrDefault(c => c.Type == "Avatar");
return avatar?.Value;
}
}
Now I can access the avatar in my view
#(((ClaimsPrincipal)User).GetAvatar())
I don't think that's a really good and clean way to do this, but this is the first time I am using it, so I don't know what's the best practices to do. There are three main reasons why I don't like it:
The avatar is stored twice, once in the AspNetUsers table as column and once as a new entry in AspNetUserClaims
Fields like SteamId, Credits or RegisterDate are saved as string in the AspNetUserClaims table, and I have to convert them to int, long or DateTime in the extension method
I have to write an extension method for every property I'll add to the ApplicationUser
What is the best way to handle additional fields?
Might it be an option to create a new object called AdditionalUserInformation and store the json serialized string as claim and just have one extension method to return the object? Then the properties would have the correct type.
Or is there a way to access the properties of the ApplicationUser in the view?
Or is the problem the use of those in the view? Should I access them in the controller and create a Model for the view containing all information? What about a _Layout page or a _navigation partial view then? They also might need the information of the user. I think I can't feed them with the controllers.
I also checked some examples. Mostly they add those extension methods and mostly just string properties.
Examples
How to extend available properties of User.Identity
How should I access my ApplicationUser properties from within my MVC 6 Views?
I am currently kinda stuck here on finding the best and maybe clean solution.
You can access AspNetUsers table with the name "Users" from your DbContext. First query AspNetUsers with current user's username or userId, then fill your ViewModel and send it to the view. The following code shows what I described:
[Authorize]
public ActionResult UserTopNavBar()
{
var userTopNavBarViewModel = new UserTopNavBarViewModel();
using(ApplicationDbContext _db = new ApplicationDbContext())
{
var user = _db.Users.FirstOrDefault(a => a.UserName == User.Identity.Name);
if (user != null)
{
userTopNavBarViewModel.Name = user.FirstName + " " + user.LastName;
userTopNavBarViewModel.Picture = user.Picture;
userTopNavBarViewModel.Id = user.Id;
}
}
return PartialView(userTopNavBarViewModel);
}
And this is my ViewModel
public class UserTopNavBarViewModel
{
public string Name { get; set; }
public byte[] Picture { get; set; }
public string Id { get; set; }
}
I have seen several examples on this site of mapping multiple foreign keys on the same table to an entity. I think I have coded things the way they are shown on the examples, but when I try to save my entity, both foreign keys are pointing to the same entity rather than two separate ones. I am expecting that my parent entity would have reference to two different entities from the linked table. My classes are as follows:
public class User: PersistentEntity
{
[Required]
public string FullName { get; set; }
[Required]
public string HID { get; set; }
[Required]
public string VID { get; set; }
[Required]
[MaxLength(100)]
public string UserName { get; set; }
//nav properties
[InverseProperty("ImpersonatedUser")]
public virtual ICollection<UserOverride> ImpersonatedUsers { get; set; }
[InverseProperty("User")]
public virtual ICollection<UserOverride> Users { get; set; }
}
public class UserOverride: PersistentEntity
{
[Required]
[ForeignKey("User")]
public int UserId { get; set; }
[Required]
[ForeignKey("ImpersonatedUser")]
public int ImpersonatedUserId { get; set; }
[Required]
public bool Active { get; set; }
//nav fields
[IgnoreForValidation]
[ForeignKey("UserId")]
public virtual User User { get; set; }
[IgnoreForValidation]
[ForeignKey("ImpersonatedUserId")]
public virtual User ImpersonatedUser { get; set; }
}
I create several users without any ImpersonatedUser objects on them, then try to create a new User object with one of these previous users set as the ImpersonatedUser. I am setting the ImpersonatedUserId equal to the UserId from that user and adding this entity to the list of ImpersonatedUsers on User, but when I try to save, it changes the ImpersonatedUserId to the id of the new user I am saving, and adds this same user to the list of Users on the User object. If I try to set the entire ImpersonatedUseer object and then save, I get an error about multiplicity of foreign keys not being correct.
My question is, what am I doing wrong? this looks like what I have seen as other examples out here, but I can't get it to work properly. Do I have it modeled correctly? Thanks for any help.
EDIT---
//create a couple users
var user = new User
{
FullName = "Ritchie Blackmore",
HID = "01010101",
VID = "rblackmore",
UserName = "rblackmore"
};
var userResult = UserService.SaveOrUpdate(user);
here is how I am creating my user I am trying to save:
var impersonatedUsers = UserService.FindByReadOnly(u => u.UserName.Equals("rblackmore"));
var impersonatedUser = Queryable.FirstOrDefault(impersonatedUsers);
var user = new User
{
FullName = "Ronnie James Dio2",
HID = "03030303",
VID = "rjdio2",
UserName = "rjdio2",
Roles = new List<Role>
{
roleResult1.Entity, //pre-existing role
//TODO: the following are new roles, added while adding the user
//probably shouldn't allow this scenario, but it might come in handy
new Role
{
Name = "Client2",
Description = "Client",
DisplayName = "Client"
},
new Role
{
Name = "Developer2",
Description = "Developer",
DisplayName = "Developer"
}
},
ImpersonatedUsers = new List<UserOverride>
{
new UserOverride {ImpersonatedUserId = impersonatedUser.Id, SystemId = system.Id, Active = true}
}
};
var result = UserService.SaveOrUpdate(user);
As you can see, I am only setting the id of the impersonated user, not the entire impersonated user object. I have tried assigning the entire object as well, which threw an exception as it tried to change the key of the object to the new user's key.
Did you try just setting the Id and leaving the navigation properties alone?
Basically, when you say:
I am setting the ImpersonatedUserId equal to the UserId from that
user and adding this entity to the list of ImpersonatedUsers on User.
Leave the second part (after and) and just set the Id of User in ImpersonatedUserId property. That should create correct relationship.
Let me know how did it go.
I am getting a simple object from a form in a MVC4 application
portfolio = {code: xxxxx, quantity: -10}
I need to add the Username to the database when I do this insert which I already know I can get from the HTTPContext.
What would be the best way to include this in the below code. I know I will need to change the object I am adding but what is the right way to do this?
public ActionResult Add(Portfolio portfolio)
{
var ctx = new MarginEntities();
ctx.Portfolios.Add(portfolio);
ctx.SaveChanges();
return RedirectToAction("Index");
}
Extend the Portfolio class to include the necessary property:
class Portfolio
{
public string Code { get; set; }
public int Quantity { get; set; }
public string Username { get; set; }
}
When the request comes from client, fields Code and Quantity are initialized, and Username is set to its default value, which is null. So simply initialize it before submitting to DB:
public ActionResult Add(Portfolio portfolio)
{
portfolio.Username = Session["Username"]; // or however the username is stored
var ctx = new MarginEntities();
ctx.Portfolios.Add(portfolio);
ctx.SaveChanges();
return RedirectToAction("Index");
}
You could use the User object, I take it all users are first authenticated?
User.Identity.Name
Your Portfolio object could then either have a string for the username or you could use a FK to the 'users' table, and get this ID by passing in the Name property from above.
You could then have either:
class Portfolio
{
...
public string Username { get; set; }
}
If you take this approach, then you would just pass the Portfolio object with the new Username property set.
or
class Portfolio
{
...
public int UserId { get; set; }
}
if you take this approach then you would need to request the UserId from EF context and then populate the Portfolio object with the returned UserId