Save changes in database controller class asp.net mvc5 identity - asp.net-mvc-5

Visual Studio 2013, ASP.NEt MVC 5 Identity
Please someone help me in how to save the information in the database through controller. Let me explain...I want the following to happen:
The user when logged is able update his Education information. After making changes in the fields, he will press the save button and the information is saved in the database. This works fine when i am doing so with the default aspnet users class, i take the usermanager. update method and the info is saved, but i am unable to do so with for any other table. I would be really thankful if someone helps me.
Here's the controller class edit method
public async Task<ActionResult> Edit(string id, Education education)
{
if (!User.Identity.IsAuthenticated)
{
Response.Redirect("~/Account/Login");
}
var db = new ApplicationDbContext();
var educationdb = db.Edu.First(u => u.EducationID == education.EducationID);
educationdb.Qualification = education.Qualification;
educationdb.School = education.School;
educationdb.SchFrom = education.SchFrom;
educationdb.SchTo = education.SchTo;
educationdb.College = education.College;
educationdb.ClgFrom = education.ClgFrom;
educationdb.ClgTo = education.ClgTo;
educationdb.University = education.University;
educationdb.UniFrom = education.UniTo;
educationdb.Description = education.Description;
db.Entry(educationdb).State = System.Data.Entity.EntityState.Modified;
await db.SaveChangesAsync();
//return RedirectToAction("Index");
return View();
}
this is the model class:
namespace theme1.Models
{
public class Education
{
public string EducationID { get; set; }
public string UserID { get; set; }
public ApplicationUser User { get; set; }
public string Qualification { get; set; }
public string School { get; set; }
[DataType(DataType.Date)]
public DateTime SchFrom { get; set; }
[DataType(DataType.Date)]
public DateTime SchTo { get; set; }
public string College { get; set; }
[DataType(DataType.Date)]
public DateTime ClgFrom { get; set; }
[DataType(DataType.Date)]
public DateTime ClgTo { get; set; }
public string University { get; set; }
[DataType(DataType.Date)]
public DateTime UniFrom { get; set; }
[DataType(DataType.Date)]
public DateTime UniTo { get; set; }
public string Description { get; set; }
}
}
var educationdb = db.Edu.First(u => u.EducationID == education.EducationID); this line gives me an error Exception Details: System.InvalidOperationException: Sequence contains no elements
Source Error:
Line 109: var educationdb = db.Edu.First(u => u.EducationID == education.EducationID);

Instead of :
var educationdb = db.Edu.First(u => u.EducationID == education.EducationID);
Try this :
var educationdb = db.Edu.Where(u => u.EducationID == education.EducationID).FirstOrDefault();
OR
Instead of :
var educationdb = db.Edu.First(u => u.EducationID == education.EducationID);
Try this :
var educationdb = db.Edu.FirstOrDefault(u => u.EducationID == education.EducationID);

I suspect that your missing the DBSet for Education model in ApplicationContext.
If this doesn't fix it, could you please provide the code for your ApplicationContext class?

I cant comment on your discussion with Kartikeya since I am new to stackoverflow and lack reputation.
But after you changed your lambda:
var educationdb = db.Edu.Where(u => u.EducationID ==
education.EducationID).FirstOrDefault();
From your discussion with Kartikeya It sounds like you are thinking of Creating rather than editing. You cant edit something that doesn´t exist.
// GET: Model
public ActionResult Create()
{
return View();
}
in MVC 5 it is very easy to scaffold the view by right clicking inside that Action and chosing "add view" and insert the information you need, and when you have created the view, create your post method for Create:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include ="EducationID, UserId....")] Education education)
{
if (ModelState.IsValid)
{
//any extra logic you might want
db.Edu.Add(education);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(education);
}

Related

AutoMapper One-To-Many Filter and ProjectTo

I have the below two Entities (one-to-many)
public class ApplicationCode
{
public Guid ApplicationId { get; set; }
public string? ApplicationAcrynom { get; set; }
public int ApplicationIndex { get; set; }
public IList<ApplicationCodeTranslation> ApplicationCodeTranslations { get; private set; } = new List<ApplicationCodeTranslation>();
}
public class ApplicationCodeTranslation
{
public Guid ApplicationCodeTranslationId { get; set; }
public Guid ApplicationId { get; set; }
public Guid LanguageId { get; set; }
public string? ApplicationDescription { get; set; }
public ApplicationCode ApplicationCode { get; set; } = null!;
}
The goal is to populate the below Dto where I need to filter one language from the child list (IList ApplicationCodeTranslations) in the ApplicationCode entity to get the description value
public class ApplicationCodeDto : IMapFrom<ApplicationCode>
{
public Guid ApplicationId { get; set; }
public string? ApplicationAcrynom { get; set; }
public string? ApplicationDescription { get; set; }
public void Mapping(Profile profile)
{
profile.CreateMap<ApplicationCode, ApplicationCodeDto>();
profile.CreateMap<ApplicationCodeTranslation, ApplicationCodeDto>();
}
}
I tried below, but it does not give the intended result.
It omits the ApplicationDescription (gives null), the whole .Include line is not translated to SQL at all.
var test1 = await _context.ApplicationCodes
.Include(e => e.ApplicationCodeTranslations.Where(l => l.LanguageId == _languageId))
.ProjectTo<ApplicationCodeDto>(_mapper.ConfigurationProvider)
.ToListAsync();
The other option I tried is
var test2 = await _context.ApplicationCodes
.SelectMany(e => e.ApplicationCodeTranslations)
.Where(l => l.LanguageId == _languageId)
.ProjectTo<ApplicationCodeDto>(_mapper.ConfigurationProvider)
.ToListAsync();
Which omits the properties of the CodeApplication and brings only the child list properties, as SelectMany only brings back the props of the child.
The only way I managed to make it work is below:
var test3 = await _context.ApplicationCodes
.SelectMany(e => e.ApplicationCodeTranslations.Where(l => l.LanguageId == _languageId),
(a,t) => new ApplicationCodeDto{
ApplicationId = a.ApplicationId,
ApplicationAcrynom = a.ApplicationAcrynom,
ApplicationDescription = t.ApplicationDescription,
})
.ToListAsync();
How can AutoMapper (v.11) help in this scenario to avoid mapping the DTO props manually?
Does ProjectTo work here?

PATCH in ServiceStack

I am trying to patch a object with the following code.
public object Patch(EditBlog request)
{
using (var db = _db.Open())
{
try
{
request.DateUpdated = DateTime.Now;
Db.Update<Blog>(request, x => x.Id == request.Id);
return new BlogResponse { Blog = Blog = Db.Select<Blog>(X=>X.Id == request.Id).SingleOrDefault()};
}
catch (Exception e)
{
return HttpError.Conflict("Something went wrong");
}
}
}
In Postman, I am calling the function like this "api/blog/1?=Title=Test1&Summary=Test&UserId=1".
When debugging I can see that those values has been assigned to the request.
During the Update it throws: "Cannot update identity column 'Id'"
My model looks like this
public class Blog
{
[AutoIncrement]
public int Id { get; set; }
public IUserAuth User { get; set; }
[Required]
public int UserId { get; set; }
[Required]
public string Title { get; set; }
public string Summary { get; set; }
public string CompleteText { get; set; }
[Required]
public DateTime DateAdded { get; set; }
public DateTime DateUpdated { get; set; }
}
And the EditBlog DTO looks like this:
[Route("/api/blog/{id}", "PATCH")]
public class EditBlog : IReturn<BlogResponse>
{
public int Id { get; set; }
public IUserAuth User { get; set; }
public int UserId { get; set; }
public string Title { get; set; }
public string Summary { get; set; }
public string CompleteText { get; set; }
public DateTime DateUpdated { get; set; }
}
The error message "Cannot update identity column 'Id'" does not exist anywhere in ServiceStack.OrmLite, it could be an error returned by the RDBMS when you're trying to update the Primary Key which OrmLite wouldn't do when updating a Model annotated with a Primary Key like your Blog class has with its annotated [AutoIncrement] Id Primary Key.
The error is within your Db.Up<T> method that's performing the update, which is not an OrmLite API, so it's likely your own custom extension method or an alternative library.
I would implement a PATCH Request in OrmLite with something like:
var blog = request.ConvertTo<Blog>();
blog.DateUpdated = DateTime.Now;
Db.UpdateNonDefaults(blog);
i.e. using OrmLite's UpdateNonDefaults API to only update non default fields and updating using the Blog Table POCO not the EditBlog Request DTO.
Also you should use the Single APIs when fetching a single record, e.g:
Blog = Db.SingleById<Blog>(request.Id)
or
Blog = Db.Single<Blog>(x => x.Id == request.Id)
Instead of:
Blog = Db.Select<Blog>(X=>X.Id == request.Id).SingleOrDefault()

How to get a subset of Users from ASP.NET Identity 2

Using a slightly modified version of the default ASP.NET MVC 5 template (with Individual Accounts), I am trying to get a subset of users based on an intermediary table. I have already built up an administration UI that can return a list of all users, but now I need to limit the set of users returned based on the currently logged in user's access privileges defined in the intermediary table.
Essentially, each user will have access to 1 or more clinics, so there will be one record for each clinic to which they have access.
If the currently logged in user belongs to a given role (e.g., "Clinic Admin"), then they should have the ability to retrieve a list of any users who belong to any of the clinics to which they have access.
Can anyone help point me in the right direction? This is my first Anything.NET application, so please feel free to explain like I'm five. :-)
Thank you in advance for any help you can offer.
Additional information:
Visual Studio 2013 Update 5
Entity Framework 6
MS SQL Server 2008 R2
Here is the intermediary table's class (ClinicUser):
[Table("clinic_users")]
public class ClinicUser
{
[Key]
public virtual ApplicationUser ApplicationUsers { get; set; }
[Required]
public string Id { get; set; }
[Required]
public System.Guid provider_id { get; set; }
[Required]
public System.Guid health_system_id { get; set; }
[Required]
public System.Guid clinic_id { get; set; }
}
Here is my ApplicationUser class:
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName
{
get { return FirstName + " " + LastName; }
}
[ForeignKey("ClinicUsers")]
public override string Id
{
get
{
return base.Id;
}
set
{
base.Id = value;
}
}
public virtual ClinicUser ClinicUsers { get; set; }
public IEnumerable<SelectListItem> RolesList { 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
userIdentity.AddClaims(ClinicClaimsProvider.GetClaims(userIdentity));
return userIdentity;
}
}
In case it wasn't clear, what I'm really trying to do is narrow the list of ApplicationUsers to return only the list of users to which I have access to based on the clinics we have have in common.
If I were writing this as a SQL query, this would be one way to accomplish what I want (I just can't seem to quite get what I want with LINQ):
SELECT *
FROM AspNetUsers au
WHERE Id IN (
SELECT Id
FROM clinic_users
WHERE clinic_id IN (
SELECT clinic_id
FROM clinic_users
WHERE Id = 'CurrentUserId'
)
)
First of all do not user much properties in ApplicationUser class, you can manage user profiles table and connect it with application user class, so you can put lot of information about user in profile table.
Next task is organize table of clinics, branches etc... and asociate application users with them.
Next you have 2 ways:
1. asociate application users with clinics or branches.
or
2. Manage them with roles.
Here is example with Application users:
[Table("Clinics")]
public class Clinic
{
[Key]
public string Id { get; set; }
public virtual ICollection<ClinicUser> ClinicUsers { get; set; }
}
[Table("ClinicUsers")]
public class ClinicUser
{
[Key]
[Column(Order = 0)]
public string ClinicId { get; set; }
[Key]
[Column(Order = 1)]
public string UserId { get; set; }
}
So next you need Other ViewModels to display them hope this help.
UPDATE
// GET: ClinicUsers by Clinic
public async Task<ActionResult> ViewCurrentClinicUsers(string id) // This is clinis ID
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Clinic clinic = await db.clinic.FindAsync(id); // Get Selectedclinic
if (clinic == null)
{
return HttpNotFound();
}
ClinicUsers model = new ClinicUsers() // ClinicUsers model
{
clinic = clinic, // View Currentclinic
ClinicUsers = await db.ClinicUsers.Were(x => x.clinicid == clinic.id)ToListAsync()) // Get Users that asigned to current clinic
};
return View(model);
}
UPDATE 2
And Finaly if you want display clinics were is assigned current loged user
// GET: Clinics by currentuser
public async Task<ActionResult> ViewClinicsWithCurrentUserAccess()
{
var currentuserId = User.Identity.GetUserId(); // This gets currentloged user id
var currentuser = await db.Users.SingleOrDefaultAsync(x => x.Id == myUserId); // This gets currentloged user virtual
return View(await db.Clinics.Were(x => x.clinicuserid == currentuserId).ToListAsync());
}
I solved this a while back, but I thought I had better come back here and update my question with an answer, in case this might help someone else.
I updated my Clinic and ClinicUser classes accordingly:
Clinic.cs
[Table("clinics")]
public class Clinic
{
[Key]
public System.Guid ClinicId { get; set; }
public List<ClinicUser> ClinicUsers { get; set; }
}
ClinicUser.cs
[Table("clinic_users")]
public class ClinicUser
{
[Key, Column(Order = 0)]
public string UserId { get; set; }
[Key, Column(Order = 1)]
public System.Guid ClinicId { get; set; }
[ForeignKey("UserId")]
public virtual ApplicationUser ApplicationUser { get; set; }
[ForeignKey("ClinicId")]
public Clinic Clinic { get; set; }
}
Also, I updated the following excerpt of my ApplicationUser class from this:
[ForeignKey("ClinicUsers")]
public override string Id
{
get
{
return base.Id;
}
set
{
base.Id = value;
}
}
public virtual ClinicUser ClinicUsers { get; set; }
to this:
public List<ClinicUser> ClinicUsers { get; set; }
Finally, in my ApplicationUsersController's Index() action, I was able to use this:
public async Task<ActionResult> Index()
{
if (User.IsInRole("Admin")) return View(await UserManager.Users.ToListAsync());
var userId = User.Identity.GetUserId();
//Get the Ids of the current user's clinics
var userClinics = db.ClinicUsers.Where(cu => cu.UserId == userId).Select(cu => cu.ClinicId).ToList();
//Get all userIds of the user at the current user's clinics
var clinicUserIds = db.ClinicUsers.Where(cu => userClinics.Contains(cu.ClinicId)).ToList().Select(cu => cu.UserId);
var users = UserManager.Users.Where(u => clinicUserIds.Contains(u.Id));
return View(await users.ToListAsync());
}
In essence, if the user has the "Admin" role, then they will get a list of all users in the database. If they aren't, they will only get a list of the users that also belong to the clinics they have in common.
It may not be perfect, but it works. If anyone has any suggestions on how to improve this, I would be glad to hear it.
Again, my thanks to Archil (https://stackoverflow.com/users/4089212/archil-labadze) for his helpful responses.

How to save ICollection data to database with MVC 5

I have searched and tried dozen of solutions for my problem, but cant get this working.
I have compressed the class definitions here, but here are the main parts.
public class ApplicationUser : IdentityUser
{
public ApplicationUser()
{
this.Device = new HashSet<Device>();
}
public virtual ICollection<Device> Devices { get; set; }
}
public class Device
{
public Device()
{
}
public int Id { get; set; }
public string DeviceId { get; set; }
public virtual ApplicationUser User { get; set; }
}
UserController.cs:
public async Task<ActionResult> SaveDevice(string id, string selection)
{
var selectedDevice = db.Devices.Where(u => u.DeviceId.Equals(selection));
var user = await UserManager.FindByIdAsync(id);
user.Devices.Add(selectedDevice.First());
==> here everything looks ok, device data can be seen from the user data
==> in comments different things that I have tried so far, without getting anything to work
//db.Entry(user).Property(p => p.Devices).IsModified = true;
//db.Entry(user).Reference(p => p.Devices).IsLoaded = true;
//db.Entry(user).Collection(p => p.Devices).IsLoaded = true;
//db.Entry(user).State = EntityState.Modified;
db.Users.Attach(user);
db.SaveChanges();
return RedirectToAction("Index");
}
==> now, when I return to user details view, the device data is empty, so nothing has be stored to db

Entity Framework Insert object with related object

I am a newbie with Entity Framework and I need to insert an object Comment that has a related FK object User into the database.
public Class Comment
{
public int CommentID { get; set; }
public string CommentContent { get; set; }
public virtual User User { get; set; }
public virtual DateTime CommentCreationTime { get; set; }
}
public class User
{
public int UserID { get; set; }
public string UserName { get; set; }
public string UserPassword { get; set; }
public string UserImageUrl{get; set;}
public DateTime UserCreationDate { get; set; }
public virtual List<Comment> Comments { get; set; }
}
public void AddComment()
{
User user = new User() { UserID = 1 };
Comment comment = new Comment() { CommentContent = "This is a comment", CommentCreationTime = DateTime.Now, User = user };
var ctx = new WallContext();
comments = new CommentsRepository(ctx);
comments.AddComment(comment);
ctx.SaveChanges();
}
Ideally, with T-SQL, if I know the PRIMARY KEY of my User object, I could just insert my Comment object and specify the PK of my 'User' in the insert statement.
I have tried to do the same with Entity Framework and it doesn't seem to work. It would be overkill to have to first fetch the User object from the database just to insert a new 'Comment'.
Please, how can I achieve this ?
You need to attach the user object to the context so that the context knows its an existing entity
public void AddComment()
{
var ctx = new WallContext();
User user = new User() { UserID = 1 };
ctx.Users.Attach(user);
Comment comment = new Comment() { CommentContent = "This is a comment", CommentCreationTime = DateTime.Now, User = user };
comments = new CommentsRepository(ctx);
comments.AddComment(comment);
ctx.SaveChanges();
}

Resources