many to many relationship collection not loading - c#

I have classes:
static class Program
public class MyContext
public class User
public class Permissions
Public static class LocalStorage
public enum List item
folowing are codes for each:
static class Program {
static void Main() {
Permissions p = new Permissions() { PermissionName = Permission.ReadUser } ;
User mirian = new User() { UserName = "miri", Password="123", PasswordChanged=true, Permissions = new List<Permissions>() {} };
mirian.Permissions.Add(p);
using (var db = new MyContext()) {
db.Permissions.Add(p);
db.Users.Add(mirian);
db.SaveChanges();
}
}
}
public class MyContext : DbContext {
public MyContext() : base("AppEntityDB") {
System.Data.Entity.Database.SetInitializer(new DropCreateDatabaseAlways<MyContext>());
}
public virtual DbSet<User> Users { get; set; }
public virtual DbSet<Permissions> Permissions { get; set; }
}
public class User : BaseCore {
MyContext _db = new MyContext();
[Key]
public int ID { get; set; }
[MaxLength(20)]
public string UserName { get; set; }
public ICollection<Permissions> Permissions { get; set; }
public bool Login(string username, string password) {
using (_db) {
var _userlist = _db.Users.Where(c => c.IsActive == true
&& c.UserName == username
&& c.Password == password).SingleOrDefault();
if (_userlist != null) {
LocalStorage.CurrentUser = _userlist;
return true;
}
return false;
}
}
}
public class Permissions : BaseCore {
[Key]
public int ID { get; set; }
public Permission PermissionName{ get; set; }
public ICollection<User> User { get; set; }
}
public enum Permission : byte {
ManageAccounts = 0,
ReadUser = 1,
WriteUser = 2,
ReadProduct = 3,
WriteProduct = 4,
}
public static class LocalStorage {
public static User CurrentUser { get; internal set; }
}
on login i call User.Login() method assigning LocalStorage.CurrentUser to current user object taken from base by entity+linq.
when i call LocalStorage.CurrentUser.Permissions, i expect it to return a list of permissions that have current user. but it gets value of null
any clue?

I'm not sure but it might be lazy loading and its not loading related entities.

Related

Associated Model class not being saved keeps returning null

I have a User model inside my Ticket model but every time I try and set the User inside the Ticket model, it always just ends up being null when I try and retrieve it later on and I cannot seem to figure out why.
The goal is to be able to create a Ticket, when creating you can type in a username to associate this ticket to someone. If the user is found, the ticket is created.
However, it does not seem to save the associated User that was typed in or any information regarding it.
Models
public class Ticket
{
[Key]
public Guid Id { get; set; }
[Required]
public string Title { get; set; }
public User owningUser { get; set; }
}
public class User
{
public Guid UserId { get; set; }
[Required]
public string Username { get; set; }
public List<Ticket> assignedTickets { get; set; }
}
Create.razor
<InputText id="title" #bind-Value="#ticketItem.owningUser.Username" class="form-control" aria-label="Disabled input example" />
private Ticket ticketItem { get; set; } = new Ticket();
protected override async Task OnInitializedAsync()
{
ticketItem.owningUser = new User();
}
public async Task CreateNewTodoItem()
{
var result = await Http.PostAsJsonAsync("TodoItems", ticketItem);
}
Controller
public async Task <ActionResult> CreateNewTodoItem(Ticket item)
{
ApplicationUser userValid;
User tempUser = new User();
try
{
userValid = userManager.Users.Where(s => s.Email == item.owningUser.Username).First();
item.owningUser = new User();
item.owningUser.Username = userValid.Email;
context.Tickets.Add(item);
await context.SaveChangesAsync();
return NoContent();
} catch (InvalidOperationException)
{
return BadRequest();
}
}
Retrieval method
private User associatedUser { get; set; }
[Parameter]
public Guid Id { get; set; }
protected override async Task OnInitializedAsync()
{
ticketItem = await Http.GetFromJsonAsync<Ticket>($"TodoItems/{Id}");
associatedUser = await Http.GetFromJsonAsync<User>($"User/{Id}");
ticketItem.owningUser = associatedUser;
}
// Controller ---
[HttpGet("{ticketID}")]
public async Task<ActionResult<User>> GetUserForTicket(Guid ticketID)
{
var ticketUser = context.Tickets.First(p => p.Id == ticketID);
User use = new User { Username = ticketUser.owningUser.Username };
return Ok(use);
}
Null error
DBContext
public class ApplicationDbContext : ApiAuthorizationDbContext<ApplicationUser>
{
public ApplicationDbContext(
DbContextOptions options,
IOptions<OperationalStoreOptions> operationalStoreOptions) : base(options, operationalStoreOptions)
{
}
public DbSet<Company> Companies { get; set; }
public DbSet<Ticket> Tickets { get; set; }
public DbSet<Project> Projects { get; set; }
public DbSet<User> Users { get; set; }
}
you have to include owningUser
var ticketUser = context.Tickets.Include(o=> o.owningUser).First(p => p.Id == ticketID);
if(ticketUser.owningUser!=null)
return new User { Username = ticketUser.owningUser.Username };
return BadRequest("no owningUser");
But it can be that owningUser is null or your dbcontext is not configured properly since I can see that your Create action doesn't make any sense at all.

EF Core table first not saving entities in the database

I'm new to EF (table first) and I don't know why these related entities are not saving at all to my database.
These are the related entities, UserProfile has a set of Carts
public partial class UserProfile
{
public UserProfile()
{
Cart = new HashSet<Cart>();
Naquestions = new HashSet<Naquestions>();
}
public int Id { get; set; }
public string BotUserId { get; set; }
public int? PrestashopId { get; set; }
public bool Validated { get; set; }
public int Permission { get; set; }
public DateTime CreationDate { get; set; }
public ICollection<Cart> Cart { get; set; }
public ICollection<Naquestions> Naquestions { get; set; }
}
Cart has a set of OrderLines
public partial class Cart
{
public Cart()
{
OrderLine = new HashSet<OrderLine>();
OrderRequest = new HashSet<OrderRequest>();
}
public int Id { get; set; }
public int UserId { get; set; }
public bool Active { get; set; }
public UserProfile User { get; set; }
public ICollection<OrderLine> OrderLine { get; set; }
public ICollection<OrderRequest> OrderRequest { get; set; }
}
And when I try to add them:
public async Task AddOrderLineToUser(string botId, OrderLine orderLine)
{
using (var context = ServiceProvider.CreateScope())
{
var db = context.ServiceProvider.GetRequiredService<GretaDBContext>();
var user = await UserController.GetUserByBotIdAsync(botId);
var latestCart = user.Cart.OrderByDescending(c => c.Id).FirstOrDefault();
if (latestCart != null && latestCart.Active)
{
latestCart.OrderLine.Add(orderLine);
}
else
{
var newCart = new Cart()
{
Active = true,
};
newCart.OrderLine.Add(orderLine);
user.Cart.Add(newCart);
}
await db.SaveChangesAsync();
}
}
Nothing is saving to the database once db.SaveChangesAsync() is called.
As #Caius Jard said in the comments it seems that user comes from another context. Try
if (latestCart != null && latestCart.Active)
{
orderLine.CartId = latestCart.Id;
db.OrderLines // I assume it is name of your orderlines DbSet
.Add(orderLine);
}
else
{
var newCart = new Cart()
{
Active = true,
UserId = user.Id,
};
newCart.OrderLine.Add(orderLine);
db.Carts // also assuming name of DbSet
.Add(newCart);
}
Also you can take a look at Attach method.
But I would say that in general you are doing something not good. Usually creating new scope is not needed, and db context should be injected in corresponding class via ctor. If you still need to create new scope it would make sense to resolve UserController also. Also is UserController an ASP controller?

In nested class, how can control that the duplicate information is not recorded in a table

I have a web service that users can send for that information like the following example.
The problem occurs when the information sent in a class is the same. Like a "sampleLine" in code.
How can I control it not to be stored in the duplicate information "sampleLine" table?
public class samplePerson
{
public int ID { get; set; }
public string Name { get; set; }
public sampleCopmany PersonCopmany { get; set; }
public sampleLine PersonLine { get; set; }
}
public class sampleCopmany
{
public int ID { get; set; }
public string Name { get; set; }
public sampleLine CopmanyLine { get; set; }
}
public class sampleLine
{
public int ID { get; set; }
public string Name { get; set; }
}
public class sampleDBContext
{
private MyDBContext dBContext;
public sampleDBContext()
{
dBContext = new MyDBContext();
}
public void Save()
{
samplePerson samplePerson = new samplePerson();
samplePerson.ID = -1;
samplePerson.Name = "Reza";
samplePerson.PersonCopmany = new sampleCopmany()
{
ID = -1,
Name = "Test",
CopmanyLine = new sampleLine()
{
ID = -1,
Name = "line"
}
};
samplePerson.PersonLine = new sampleLine()
{
ID = -1,
Name = "line"
};
dBContext.Add(samplePerson);
dBContext.SaveChangesAsync();
}
}
Is it possible to control this item?
There is not any automatic way in EF that handles it for you and you need to manually check for the existence of data before creating it.
If the record exists, then use the existing key and if not then create the record as you have done in your sample code.

Add object as property in ASP.Net identity

I'm using MVC 5 ASP.net Identity entity framework code-first to create an online application form. I have created a new project with the ASP.net identity scaffold, and need to be able to add additional properties. Some of them are simple properties- mobile phone number etc, and this works fine. however I need to add a more complex object to the user to store the application form information. I tried doing this:
public class ApplicationUser : IdentityUser
{
public string Title { get; set; }
public string FirstName { get; set; }
public string Surname { get; set; }
public override string PhoneNumber { get; set; }
public string PracticeName { get; set; }
public string Address { get; set; }
public string Mobile { get; set; }
public string GMCNumber { get; set; }
public AppForm ApplicationForm { get; set; }
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public DbSet<AppForm> AppForms { get; set; }
public DbSet<AppFormDocument> AppFormDocuments { get; set; }
public DbSet<AppFormAnswer> AppFormAnswers { get; set; }
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
and have created the appform models like this:
public class AppForm {
public int Id { get; set; }
public int PercentComplete { get; set; }
public string Status {get; set; }
public bool Completed { get; set; }
public bool Reviewed { get; set; }
public bool SignedOff { get; set; }
public int LastPageCompleted { get; set; }
public List<AppFormDocument> Documents { get; set; }
public List<AppFormAnswer> Answers { get; set; }
}
public class AppFormDocument {
public int Id { get; set; }
public DateTime DateSubmitted { get; set; }
public string Name { get; set; }
public DateTime? ExpiryDate { get; set; }
public bool Accepted { get; set; }
public string ScannedFile { get; set; }
}
public class AppFormAnswer {
public int Id { get; set; }
public string QuestionNumber { get; set; }
public string Question { get; set; }
public string Answer { get; set; }
}
The application form is very large and has many questions which is why I didnt just put it all in the applicationuser class. There is also the requirement to upload documents with the form.
Now, when I create the user and attach an instance of the AppForm and some instances of AppFormAnswers to it, then save it, the data gets stored successfully, but when I try to access the data again after logging in, it is not found. The additional simple properties of the user are available though, including mobile number and title. here's my code:
[Route("appform/{action}")]
[Authorize]
public class AppFormController:Controller {
// GET: AppForm
public ActionResult Index() {
ApplicationUser user = System.Web.HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>().FindById(System.Web.HttpContext.Current.User.Identity.GetUserId());
var model = new AppFormIndexViewModel();
if (user != null) {
if (user.ApplicationForm != null) {
model.PercentComplete = user.ApplicationForm.PercentComplete;
model.NextPage = "Page" + user.ApplicationForm.LastPageCompleted + 1;
model.Status = user.ApplicationForm.Status;
} else {
// if appform is not available for user for some reason, create a new one.
user.ApplicationForm = new AppForm { PercentComplete = 0, Reviewed = false, Status = "Incomplete", SignedOff = false, Completed = false, LastPageCompleted = 0 };
var uManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
uManager.UpdateAsync(user);
model.PercentComplete = 0;
model.Status = "Incomplete";
model.NextPage = "Page1";
}
}
return View(model);
}
Now when the uManager.UpdateAsync(user) line runs, the data is saved to the database fine and a new appForm record is added. The database automatically creates primary keys and foreign keys for all the tables too.
So, do I need to write an overload of the login method to retrieve the data from the other tables? Or do I need to add something to the ApplicationDbContext class?
This is my first MVC application so not really sure where to turn now. Have tried reading many forum posts and blogs but not found anything that really matches my needs. A lot of information relates to earlier version of mvc which do not work with the newer asp.net identity system.
I finally managed to figure it out. I added an appformid property to the applicationuser class like this:
[ForeignKey("AppForm")]
public int AppFormId { get; set; }
Then created another class to load the appform object like this:
public static class AppFormManager {
public static ApplicationUser GetCurrentUser() {
ApplicationUser user = System.Web.HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>().FindById(System.Web.HttpContext.Current.User.Identity.GetUserId());
if (user != null) {
AppForm form = new AppForm();
ApplicationDbContext db = new ApplicationDbContext();
AppForm ap = db.AppForms.Where(af => af.Id == user.AppFormId).Include("Answers").Include("Documents").SingleOrDefault();
if (ap == null) {
var AppForm = new AppForm { PercentComplete = 0, Reviewed = false, Status = "Incomplete", SignedOff = false, Completed = false, LastPageCompleted = 0 };
user.AppForm = AppForm;
} else {
user.AppForm = ap;
}
return user;
}
return null;
}
public static bool SaveCurrentUser() {
ApplicationUser user = System.Web.HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>().FindById(System.Web.HttpContext.Current.User.Identity.GetUserId());
if (user == null) { return false; }
var uManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
uManager.UpdateAsync(user);
return true;
}
}
So in the controller, the code is much cleaner:
// GET: AppForm
public ActionResult Index() {
ApplicationUser user = AppFormManager.GetCurrentUser();
var model = new AppFormIndexViewModel();
if (user != null) {
model.PercentComplete = user.AppForm.PercentComplete;
model.NextPage = "Page" + user.AppForm.LastPageCompleted + 1;
model.Status = user.AppForm.Status;
}
return View(model);
}
and I can call the AppFormManager.SaveCurrentUser() method to save the data in the post action.
Thanks for all those who made suggestions which helped me figure out a way to do it. Possibly not the best way to do it, but it works for me for now.

Is this the way to implement a collection participating in and EF code first many to many relationship?

I have a situation where the code I've arrived at doesn't match any examples I find so I wonder if I'm missing something.
Basically, I want an EF code first Entity that contains a collection of Entities participating in a many-to-many relationship.
Then, I'd like to be able to:
Add to collection at the same time as creating an entity
Not get a warning about accessing a virtual member from constructor
Here's what I have:
public class NotificationUser
{
private ICollection<NotificationUserGroup> _userGroups = new HashSet<NotificationUserGroup>();
public int UserId { get; set; }
public string UserName { get; set; }
public bool IsActive { get; set; }
public virtual ICollection<NotificationUserGroup> UserGroups
{
get { return _userGroups; }
set { _userGroups = value; }
}
}
Is there a better/different way to accomplish my goal?
This example might help
public class NotificationUser
{
public NotificationUser()
{
UserGroups = new HashSet<NotificationUserGroup>();
}
public int NotificationUserId { get; set; }
public string UserName { get; set; }
public bool IsActive { get; set; }
public virtual ICollection<NotificationUserGroup> UserGroups { get; set; }
}
public class NotificationUserGroup
{
public int NotificationUserGroupId { get; set; }
public string GroupName { get; set; }
}
public class Context : DbContext
{
public Context()
: base()
{
}
public DbSet<NotificationUser> NotificationUsers { get; set; }
public DbSet<NotificationUserGroup> NotificationUserGroup { get; set; }
}
class Program
{
static void Main(string[] args)
{
Database.SetInitializer(new DropCreateDatabaseAlways<Context>());
using (var ctx = new Context())
{
var user = new NotificationUser() { UserName = "Name1" };
user.UserGroups.Add(new NotificationUserGroup() { GroupName = "Group1" });
user.UserGroups.Add(new NotificationUserGroup() { GroupName = "Group2" });
ctx.NotificationUsers.Add(user);
ctx.SaveChanges();
}
using (var ctx = new Context())
{
foreach (var user in ctx.NotificationUsers)
{
foreach (var group in user.UserGroups)
Console.WriteLine("Group Id: {0}, Group Name: {1}, UserName: {2}", group.NotificationUserGroupId, group.GroupName,user.UserName);
}
foreach (var group in ctx.NotificationUserGroup)
{
Console.WriteLine("Group Id: {0}, Group Name: {1}", group.NotificationUserGroupId, group.GroupName);
}
}
Console.ReadKey();
}
}

Categories