Inserting Sub-Array into Azure's Document DB - asp.net

I am trying to insert a sub class (document) of "Video" into my Organization document.
However, when I try to add a record, I get "Object reference is not set to an instance of an object."
I tried to use Add and Insert, but neither worked. I looked at the Dcoument explorer and I can see that Videos is returning "null."
I am assuming my problem is that Document DB doesn't know that Video is a list. (in my model, I have defined it as a list though)
Also, I have tried created new objects for Organization and Video. Also, I have a class called Category, it has the exact same code (except the object is Category) and it is inserting fine.
Below is the action that I am using.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create([Bind(Include = "name,description,link")] Video video)
{
if (ModelState.IsValid)
{
UserSession usersession = new UserSession();
usersession = (UserSession)Session["user"];
Organization organization = (Organization)DocumentDBRepository<Organization>.GetItem(d => d.Id == usersession.organizationId);
video.DateAdded = DateTime.Now;
organization.Videos.Add(video);
await DocumentDBRepository<Organization>.UpdateItemAsync(organization.Id, organization);
return RedirectToAction("Index");
}
return View(video);
}

Set organization.Videos to a non-null value. Document db simply preserves what you stored. Apparently, you previously stored null.

Related

Adding a location that is tied to a userID via Foreign Key

I'm learning .NET here and have build a small API that allows me to register users, retrieve user lists, or a single user, as well as edit them. Microsoft Identity is used for most of this process.
I am now trying to add new section to this API that will handle physical locations. Each location will be tied back to a specific user, so there may be one or multiple locations per user. I'm having trouble building this part of it though. I cant seem to figure out how to tie the ID of the user to the location as a foreign key. I've been battling this one now for quite some time.
I have the controller build for the location calls, and the DTO's, but it does not seem to want to actually work correctly.
Anyone up to talking a look and letting me know what needs to be done and how to do this? I'm a bit lost and really wanting to learn how this works. The github repo with the full working project is here:
https://github.com/sapper6fd/API
The issue at hand here was the way it was configured.
The following adjustments fixed the issue:
CreateLocation() method:
[HttpPost]
public async Task<IActionResult> CreateLocation(int clientId, LocationCreateDto locationCreateDto)
{
// Grab the current users roles to verify they have access to this API call
var userRole = User.FindFirst(ClaimTypes.Role).ToString();
if (userRole != "http://schemas.microsoft.com/ws/2008/06/identity/claims/role: GlobalAdmin")
return Unauthorized();
// Create the new location
var newLocation = _mapper.Map<Locations>(locationCreateDto);
_repo.Add(newLocation);
if (await _repo.SaveAll())
{
var locationToReturn = _mapper.Map<LocationCreateDto>(newLocation);
return CreatedAtRoute(nameof(GetLocation), new { id = locationToReturn.Id, userid=clientId }, locationToReturn);
}
throw new Exception("Unable to create new location. Failed to save.");
}
HttpGet Method:
[HttpGet("{id}", Name = nameof(GetLocation))]
public async Task<IActionResult> GetLocation(int id)
{
var location = await _repo.GetLocation(id);
var locationToReturn = _mapper.Map<LocationCreateDto>(location);
return Ok(locationToReturn);
}

Updating user name and email

In a Asp.net Core project, I´m using the code below to change user name and email. But the property "User" in the controller does not update, just after logoff.
obs.: This "User" property is already defined by the framework in controller bases class, and it is a "ClaimsPrincipal" type.
Controller
public async Task<IActionResult> Create(EditViewModel viewModel)
{
if (ModelState.IsValid)
{
var model = await _userManager.FindByIdAsync(CurrentUser.Id);
model.UserName = viewModel.UserName;
model.Email = viewModel.Email;
await _userManager.UpdateAsync(model);
//THIS ONE STILL HAS OLD VALUES ==============================
Console.WriteLine(_userManager.GetUserName(User));
//THIS ONE HAS NEWER VALUES ==================================
model = await _userManager.FindByIdAsync(CurrentUser.Id);
Console.WriteLine(model.UserName);
...
I´m using this "User" property in my views, to display user informations, like this:
View
#inject UserManager<ApplicationUser> UserManager
...
<p>Hello: #UserManager.GetUserName(User)</p>
This is likely because your current User is going to be stored in a fashion similar to a Claim (i.e. in memory) or Cookie to avoid frequent hits to the database and thus in order to pull the new values, you would need to log out and log back in to refresh these.
However, you could try following your code by calling the RefreshSignInAsync() method, which should handle this same behavior :
// Update your User
await _userManager.UpdateAsync(model);
// signInManager is an instance of SignInManager<ApplicationUser> and can be passed in via
// dependency injection
await signInManager.RefreshSignInAsync(existingUser);

Editing details on a page with a picture

I have a page with a picture and few other fields . Uploading picture works fine.If user wants to edit and put a different picture, that works fine too. The problem occurs when user edit a different field (other than the image field) in the record . After saving , the image disappears.
Here is my controller
public ActionResult Edit([Bind(Include = "GlobalMaterialId,Length,Picture")] MetalStock metalStock, HttpPostedFileBase ImageFile)
{
if (ModelState.IsValid)
{
if (ImageFile != null)
{
string pic = System.IO.Path.GetFileName(ImageFile.FileName);
metalStock.ImagePath = pic;
using (MemoryStream ms = new MemoryStream())
{
ImageFile.InputStream.CopyTo(ms);
metalStock.Picture = ms.GetBuffer();
}
}
m_db.Entry(metalStock).State = EntityState.Modified;
m_db.SaveChanges();
return RedirectToAction("Index");
}
return View(metalStock);
}
here is the image uploading bit of the view
<input name="ImageFile" type='file' / >
I understand that when I edit a field and save, ImageFile that is passed to the controller is empty and that creates the problem. I tried few other things such as trying to get the picture from the record and reassigning it to the object etc. Non of them worked. Please help.
I would guess your controller dies because it looks for a HttpPostedFile and does not get one.
You can either make it nullable in the declaration of your action
public ActionResult Edit([Bind(Include = "GlobalMaterialId,Length,Picture")] MetalStock metalStock, HttpPostedFileBase ImageFile = null)
or don't declare it in the controller at all and pick it up from the Request:
var Image = Request.Files["ImageFile"]
You could store your existing image file in a hidden field as a base64 string and in your post action check to see if the HttpPostedFileBase is null (they haven't selected a different image). If it is, convert your base64 string back to a byte array and assign it to your metalStock.Picture property.
This is how I've handled this scenario in the past. You will need to create another property on your viewmodel to hold this base64 string. This is beneficial assuming your view is not bound directly to your entity, but a viewmodel instead.

Weird caching issue with ASP.net/Linq

I'm writing an application involving storing a profile. I'm using Linq to access the database, but having a weird issue when saving a profile. When I save it, it writes to the DB correctly - but when I leave the page and come back, the old values still remain in profile form.
My profile page:
if(!Page.IsPostBack) {
Profile p = Student.GetProfile(Int32.Parse(Session["userID"].ToString()));
if (p != null)
{
FirstNameTextBox.Text = p.FirstName;
LastNameTextBox.Text = p.LastName;
Address1TextBox.Text = p.Address1;
.....
}
And my Student class:
public static Profile GetProfile(int uID)
{
var profile = (from p in db.Profiles
where p.uID == uID
select p).FirstOrDefault();
return profile;
}
I'm not doing any fancy caching anywhere, so not sure where the old values are stored...
** EDIT **
So, it seems that it's down to a global LinqDataContext. In my Student class, I had:
public class Student
{
private static LinqClassesDataContext db = new LinqClassesDataContext() { CommandTimeout = 36000 };
public static Profile GetProfile(int uID)
{
var profile = (from p in db.Profiles
where p.uID == uID
select p).FirstOrDefault();
return profile;
}
If I give the GetProfile method it's own DataContext, problem solved.
Still being very new to Linq, what's the best way to have a class with numerous methods that use the same access to a database? Having a global context like this? Or each method using it's own data context?
Assuming you're storing the userID in Session["userID"] somewhere and not clearing it out when you save, this could be where the "caching" occurs. Session objects will live (roughly) for the life of the browser session (or the life of the server process if in-memory session is enabled).
i guess even if linq send the query to the database it use the old value stored in the cache.so you have to clear the cache if you want the new value.
Going on your added comments my guess is that the page you are getting is cached by the browser / http server. If the url is the same as a previously requested page some default setting will tell the browser / server to use the cache html. To avoid this and get new html for each request you could try adding a meta tag within the head tags of your html.
<meta http-equiv="Pragma" CONTENT="no-cache">
It ended up being the DataContext I was using. I'm not exactly sure why this fixed the issue, but I changed my class from:
public class Student
{
private static LinqClassesDataContext db = new LinqClassesDataContext() { CommandTimeout = 36000 };
public static Profile GetProfile(int uID)
{
var profile = (from p in db.Profiles
where p.uID == uID
select p).FirstOrDefault();
return profile;
}
}
to:
public class Student
{
public static Profile GetProfile(int uID)
{
LinqClassesDataContext db = new LinqClassesDataContext();
var profile = (from p in db.Profiles
where p.uID == uID
select p).FirstOrDefault();
return profile;
}
}

MVC Music Store Saving concurrency. What is causing this?

I am doing the MVC Music Store Tutorials and I have finished, every works fine but for some reason when I edit an album on an Admin account it comes up with this error when I try to save changes. It highlights db.SaveChanges(); what is causing this problem?
Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries.
public ActionResult Edit(int id)
{
Album album = db.Albums.Find(id);
ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name", album.GenreId);
ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name", album.ArtistId);
return View(album);
}
//
// POST: /StoreManager/Edit/5
[HttpPost]
public ActionResult Edit(Album album)
{
if (ModelState.IsValid)
{
db.Entry(album).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.GenreId = new SelectList(db.Genres, "GenreId", "Name", album.GenreId);
ViewBag.ArtistId = new SelectList(db.Artists, "ArtistId", "Name", album.ArtistId);
return View(album);
}
I am the only person accessing the site I have made as I am doing it locally only on my machine, I have been told that somebody else may changed something but this is not the case. What's going on?
This issue is explained on this page on the musicstore codeplex site.
Here is an excerpt:
In the Album class, you've defined [Bind(Exclude = "AlbumId")] on the class, which means that the code won't try and bind the AlbumId from the form. Which is fine, except that on the edit page, you're passing in a populated Album, which is presumably being populated using binding, which is of course ignoring the Album Id, so the AlbumId of the object passed into the edit method, is always 0, which throws a misleading concurrency error, because no rows are affected, because there's no album with ID of 0.

Resources