I am working in a larger project, but because it is very complex I made my own small project to make an Azure Blobs Gallery, that displays images from Azure Blobs using container, to implement into the large project after that.
I was able to create this Gallery with the following .Net 6 / C# (MVC) code:
Controller
public class ResourceController : Controller
{
const string blobContainerName = "thumbnails";
static BlobContainerClient blobContainer;
private IConfiguration _configuration;
public ResourceController(IConfiguration configuration)
{
_configuration = configuration;
}
public ActionResult Index()
{
var model = LoadModelMethodAsync();
return View(model);
}
public ActionResult Info()
{
var model = LoadModelMethodAsync();
return View(model);
}
public List<Uri> LoadModelMethodAsync()
{
try
{
var s = _configuration.GetConnectionString("AzureStorageConnectionString");
BlobServiceClient blobServiceClient = new BlobServiceClient(s);
blobContainer = blobServiceClient.GetBlobContainerClient(blobContainerName);
blobContainer.CreateIfNotExistsAsync(PublicAccessType.Blob);
List<Uri> allBlobs = new List<Uri>();
foreach (BlobItem blob in blobContainer.GetBlobs())
{
if (blob.Properties.BlobType == BlobType.Block)
allBlobs.Add(blobContainer.GetBlobClient(blob.Name).Uri);
}
return allBlobs;
}
catch (Exception ex)
{
ViewData["message"] = ex.Message;
ViewData["trace"] = ex.StackTrace;
throw;
}
}
}
}
ViewModel
using Microsoft.AspNetCore.Mvc;
namespace EBP_V1.ViewModels {
public class ResourceViewModel
{
public string RequestId { get; set; }
public bool? ShowRequestId => !string.IsNullOrEmpty(RequestId);
}
}
Partial
#using AzureBlobLearning.Models
#model List<Uri>
#{
}
#if (Model != null && Model.Count > 0)
{
foreach (var item in Model)
{
<div class="imageBlock" value="GetGallery">
<img class="thumb" src="#item" alt="images"/><br />
<div class="deleteDiv"><img class="deleteIcon" src="~/Images/deleteImage.png" title="Delete Image" onclick="deleteImage('#item');" /></div>
</div>
}
}
View: Info.cshtml
<partial name="~/Views/Resource/_ResourcePartial.cshtml"/>
This small project works well, but when I try to implement it in the large project, the page isn't working anymore, even though it doesn't throw any error, and when I try to debug it all the data the same as it was before.
Controller in the large project ( everything else is identical with the previous one)
[HttpGet]
public async Task<ViewResult> Info(Guid? id, string filterTxt )
{
await SetCompanyName(id, filterTxt);
var model = LoadModelMethodAsync();
return View(model);
}
The interesting thing is that even if I don't call the model variable in the View the page still won't work anymore.
Question:
Why my code doesn't work in the large project?
What should I change to make it work?
P.S.:
- I am a completely beginner in .Net 6 / C# world, my first language
is Javascript, so please be understanding.
- If you have any questions, suggestions don't hesitate to ask.
Related
I am experiencing with the C#/.Net world using Azure Blobs.
I made a Gallery that takes the images from an Azure Blob Storage, and displays it on the screen.
I want to create a Partial View from it, because of the DRY principle so I could reuse it in other files as well.
When I display the Partial View on the index it works well, but when I try to display it in the Info.cshtml file doesn't find the Model variable.
Controller
public class ResourceController : Controller
{
const string blobContainerName = "blobContainer";
static BlobContainerClient blobContainer;
private IConfiguration _configuration;
public ResourceController(IConfiguration configuration)
{
_configuration = configuration;
}
// Create a List of images using Azure Blobs and Displays it on the Screen.
{
try
{
var s = _configuration.GetConnectionString("AzureConnectionString");
BlobServiceClient blobServiceClient = new BlobServiceClient(s);
blobContainer = blobServiceClient.GetBlobContainerClient(blobContainerName);
await blobContainer.CreateIfNotExistsAsync(PublicAccessType.Blob);
List<Uri> allBlobs = new List<Uri>();
foreach (BlobItem blob in blobContainer.GetBlobs())
{
if (blob.Properties.BlobType == BlobType.Block)
allBlobs.Add(blobContainer.GetBlobClient(blob.Name).Uri);
}
return View(allBlobs);
}
catch (Exception ex)
{
ViewData["message"] = ex.Message;
ViewData["trace"] = ex.StackTrace;
return View("Error");
}
}
public ActionResult Info(){
//This class doesn't see the Model variable
return View();
}
Partial
//The Info class Doesn't see the Model variable
#if (Model != null && Model.Count > 0)
{
foreach (var item in Model)
{
<div class="imageBlock" value="GetGallery">
<img class="thumb" src="#item" alt="images"/><br />
<div class="deleteDiv"><img class="deleteIcon" src="~/Images/deleteImage.png" title="Delete Image" onclick="deleteImage('#item');" /></div>
</div>
}
}
ViewModel
using Microsoft.AspNetCore.Mvc;
namespace AzureBlobLearning.Models
{
public class ResourceViewModel
{
public string? RequestId { get; set; }
public bool? ShowRequestId => !string.IsNullOrEmpty(RequestId);
}
}
Index.csthml
//This displays the list of images
<partial name="~/Views/Resource/_ResourcePartial.cshtml" />
Info.cshtml
//This doesn't display
<partial name="~/Views/Resource/_ResourcePartial.cshtml" />
My Question:
Why it doesn't see my Info view.
How should I change my code so the Info sees the partialView?
PS.: If you have any questions feel free to ask.
Why it doesn't see my Info view.
Because the action is just returning the View, with no Model inside.
Change from this:
public ActionResult Info(){
//This class doesn't see the Model variable
return View();
}
to this:
public ActionResult Index(){
var model = LoadModelMethod();
return View(model);
}
public ActionResult Info(){
var model = LoadModelMethod();
return View(model);
}
public List<Uri> LoadModelMethod()
{
try
{
var s = _configuration.GetConnectionString("AzureConnectionString");
BlobServiceClient blobServiceClient = new BlobServiceClient(s);
blobContainer = blobServiceClient.GetBlobContainerClient(blobContainerName);
await blobContainer.CreateIfNotExistsAsync(PublicAccessType.Blob);
List<Uri> allBlobs = new List<Uri>();
foreach (BlobItem blob in blobContainer.GetBlobs())
{
if (blob.Properties.BlobType == BlobType.Block)
allBlobs.Add(blobContainer.GetBlobClient(blob.Name).Uri);
}
return allBlobs;
}
catch (Exception ex)
{
ViewData["message"] = ex.Message;
ViewData["trace"] = ex.StackTrace;
throw;
}
}
How should I change my code so the Info sees the partialView?
you can do what I said above, extract the logic which loads the images to a method, and call it in both actions.
I am a junior front-end developer, and I got a task where I have to take an Azure Blobs Container lets call it "thumbnails" , loop through and display it on the screen using .Net 6.
Basically, I have to make an image gallery in Dotnet 6 that takes the images from the Blob Container with DotNet6, add it to the View (MVC) and list it with AngularJS but many of my attempts has failed.
Made a small example in case if it is not clear what I want to achieve:
Image
My questions are:
How can I take data from Azure Storage with .net 6?
How can I create an array from it and pass it to the View?
you need to use the nugget Azure.Storage.Blobs. This package exposes many methods you can utilise.
IAsyncEnumerable<BlobItem> blobs = blobContainer.GetBlobsAsync(prefix: "");
await foreach (BlobItem blobItem in blobs)
{
...
}
import the using System.Linq.Async for this to work.
Check the official docs to see more details on constructing the blobContainer.
You can extract the base64 string value from the blobs and bind it in the UI directly.
In MVC
Controller:
using System.Configuration;
using System.IO;
using System.Threading.Tasks;
using System.Web;
using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Models;
using Microsoft.AspNetCore.Mvc;
public class HomeController : Controller
{
const string blobContainerName = "tnail";
static BlobContainerClient blobContainer;
private IConfiguration _configuration;
public HomeController(IConfiguration configuration)
{
_configuration = configuration;
}
public async Task<ActionResult> Index()
{
try
{
var s = _configuration.GetConnectionString("AConnectionString");
BlobServiceClient blobServiceClient = new BlobServiceClient(s);
blobContainer = blobServiceClient.GetBlobContainerClient(blobContainerName);
await blobContainer.CreateIfNotExistsAsync(PublicAccessType.Blob);
List<Uri> allBlobs = new List<Uri>();
foreach (BlobItem blob in blobContainer.GetBlobs())
{
if (blob.Properties.BlobType == BlobType.Block)
allBlobs.Add(blobContainer.GetBlobClient(blob.Name).Uri);
}
return View(allBlobs);
}
catch (Exception ex)
{
ViewData["message"] = ex.Message;
ViewData["trace"] = ex.StackTrace;
return View("Error");
}
}
Model:
namespace AzureBlobLearning.Models
{
public class ErrorViewModel
{
public string? RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
}
}
appsetting.json:
Add Connectionstring data that you can find it the Azure portal, with the name of AConnectionString
View:
#if (Model != null && Model.Count > 0)
{
foreach (var item in Model)
{
<div class="imageBlock">
<img class="thumb" src="#item" alt="images"/><br />
<div class="deleteDiv"><img class="deleteIcon" src="~/Images/deleteImage.png" title="Delete Image" onclick="deleteImage('#item');" /></div>
</div>
}
}
See Sample: https://github.com/Azure-Samples/storage-blobs-dotnet-webapp
I am creating a web app with Razor Pages and I'm running into an issue. I am trying to send a message to the user when an exception is thrown. I'm imagining something similar to MessageBox class from System.Windows.Forms. Any ideas on whether this is possible or how to do it? Any help is appreciated.
try
{
double oneone = Convert.ToDouble(Project.projectServicesCost);
}
catch (Exception e)
{
//Throw message to user saying projectServicesCost is not in the correct
//format and therefore couldn't convert to double
}
Here's an example from my code.
You can use some library like NToastNotify.
You can follow the below steps to implement it:
1.Install NToastNotify package using Package Console Manager:
Install-Package NToastNotify
2.Configure NToastNotify service and
services.AddRazorPages().AddNToastNotifyNoty();
3.Add the middlware:
app.UseNToastNotify();
4.Add the view in _Layout page:
#await Component.InvokeAsync("NToastNotify")
5.Dependency injection IToastNotification then you can use it
public class IndexModel : PageModel
{
private readonly ILogger<IndexModel> _logger;
private readonly IToastNotification _toastNotification;
public IndexModel(ILogger<IndexModel> logger, IToastNotification toastNotification)
{
_logger = logger;
_toastNotification = toastNotification;
}
public void OnGet()
{
try
{
throw new NullReferenceException();
}
catch
{
_toastNotification.AddErrorToastMessage("Some Error Message");
}
}
}
Result:
For more details, refer to:
https://github.com/nabinked/NToastNotify
You can use modal from Bootstrap and return on a ViewBag the exception and check when is not empty and display the modal
[HttpPost]
public ActionResult Save(int? id){
UserViewModel model = new UserViewModel();
if(id == null){
ViewBag.Exception = "Please select an ID";
return View();
}
model.User = context.Users.FisrtOrDefault(a => a.id == id);
return View(model);
}
<script>
$(document).ready(function(){
var exception = #String.IsNullOrEmpty(ViewBag.Exception).ToString().ToLower();
if(!exception){
$("#modal-body").text("#ViewBag.Exception");
$("#modal").modal("show");
}
});
</script>
Or use alert from js
<script>
$(document).ready(function(){
var exception = #String.IsNullOrEmpty(ViewBag.Exception).ToString().ToLower();
if(!exception){
alert("#ViewBag.Exception");
}
});
</script>
I've been trying to use Namespace routing to build some APIs dynamically without the need to worry about hardcoding the routes. However, I did find an example from MSDN to use namespaces and folder structure as your API structure. Here's the sample that I have to use Namespace routing:
public class NamespaceRoutingConvention : Attribute, IControllerModelConvention
{
private readonly string _baseNamespace;
public NamespaceRoutingConvention(string baseNamespace)
{
_baseNamespace = baseNamespace;
}
public void Apply(ControllerModel controller)
{
var hasRouteAttributes = controller.Selectors.Any(selector => selector.AttributeRouteModel != null);
if (hasRouteAttributes)
{
return;
}
var namespc = controller.ControllerType.Namespace;
if (namespc == null) return;
var templateParts = new StringBuilder();
templateParts.Append(namespc, _baseNamespace.Length + 1, namespc.Length - _baseNamespace.Length - 1);
templateParts.Replace('.', '/');
templateParts.Append("/[controller]/[action]/{environment}/{version}");
var template = templateParts.ToString();
foreach (var selector in controller.Selectors)
{
selector.AttributeRouteModel = new AttributeRouteModel()
{
Template = template
};
}
}
}
And here's the controller:
namespace Backend.Controllers.Api.Project.Core
{
public class UserController : ApiBaseController
{
public UserController()
{
}
[HttpPost]
public IActionResult Login(LoginInput loginInput) // <-- loginInput properties return null
{
if (!ModelState.IsValid) return BadRequest();
return Ok(user);
}
}
}
in Startup.cs
namespace Backend
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// Let's use namespaces as the routing default way for our APIs
services.AddControllers(options =>
{
options.Conventions.Add(new NamespaceRoutingConvention(typeof(Startup).Namespace + ".Controllers"));
});
}
}
}
Everything works ok except that when I trigger a POST api call to Login action the LoginInput doesn't get populated the values I'm sending through Postman i.e. {"username": "value", "password": "sample"} and it always returns null value. I'm not sure what am I doing wrong with the NamespaceRoutingConvention. Bear in mind if I remove it and hard-code the route in the controller like:
[ApiController]
[Route("api/project/core/[controller]/[action]/proda/v1")]
It works as expected. Any ideas?
Try to use this instead:
[HttpPost]
public IActionResult Login([FromBody]LoginInput loginInput)
{
if (!ModelState.IsValid) return BadRequest();
return Ok(user);
}
I think that by setting AttributeRouteModel, you're preventing the middleware invoked by having ApiControllerAttribute in the Controller to do its job, and so the defaults of treating object parameters as body is not applied.
This is a guess though, I haven't been able to find the corresponding code in the source code.
InvalidOperationException: Unable to resolve service for type 'Echelon.Data.IAssignmentRepository' while attempting to activate 'Echelon.Controllers.SessionController'.
I keep getting this Error whenever I try to login or click something
in my navbar, I'm confused as to why it's erroring out, can someone
please help? This started when I got email verification working, then
whenever I try to login or click anything I get the error?
Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using Echelon.Models.SessionViewModels;
using Echelon.Classes;
using Microsoft.AspNetCore.Identity;
using Echelon.Models;
using Echelon.Data;
namespace Echelon.Controllers
{
public class SessionController : Controller
{
StoredProcedure storedProcedure = new StoredProcedure();
private IAssignmentRepository repository;
private readonly AssignmentDbContext _context;
public SessionController(IAssignmentRepository repo, AssignmentDbContext context)
{
repository = repo;
_context = context;
}
[Authorize]
public IActionResult Home()
{
return View();
}
public IActionResult Courses()
{
var Courses = storedProcedure.getCourseNames(User.Identity.Name);
var CoursesView = new CoursesViewModel();
foreach (var Course in Courses.ToList())
{
Course course = new Course();
course.CourseName = Course;
CoursesView.Courses.Add(course);
}
return View(CoursesView);
}
public IActionResult CreateCourse()
{
return View();
}
[HttpPost]
public IActionResult CreateCourse(CreateCourseModel course)
{
if (ModelState.IsValid)
{
storedProcedure.addCourse(User.Identity.Name, course.CourseName, course.CourseDesc, course.StartDate, course.EndDate);
return RedirectToAction("Courses");
}
else
{
return View();
}
}
public IActionResult Assignments(string courseName)
{
var assignments = storedProcedure.getAssignments(User.Identity.Name, courseName);
var AssignmentsView = new AssignmentsViewModel { CourseName = courseName };
foreach (var Assignment in assignments.ToList())
{
AssignmentsView.Assignments.Add(Assignment);
}
return View(AssignmentsView);
}
public IActionResult CreateAssignment(string courseName)
{
CreateAssignment assignmentModel = new CreateAssignment();
assignmentModel.CourseName = courseName;
assignmentModel.UserName = User.Identity.Name;
return View(assignmentModel);
}
[HttpPost]
public async Task<IActionResult> CreateAssignment([Bind("AssignmentID,UserName,CourseName,AssignmentName,AssignmentDescription,TotalPoints,DueDate")] CreateAssignment assignment)
{
if (ModelState.IsValid)
{
_context.Add(assignment);
try
{
await _context.SaveChangesAsync();
}
catch (Exception ex)
{
return View(assignment);
}
//return View(assignment);
return RedirectToAction("Assignments", "Session", new { courseName = assignment.CourseName });
}
else
return View(assignment);
}
public IActionResult Students(string courseName)
{
var students = storedProcedure.getStudents(User.Identity.Name, courseName);
var studentsView = new StudentsViewModel();
foreach (var student in students.ToList())
{
Student Student = new Student();
Student.StudentFName = student.StudentFName;
Student.StudentLName = student.StudentLName;
Student.CourseName = student.CourseName;
studentsView.Students.Add(Student);
}
return View(studentsView);
}
public IActionResult AllStudents()
{
var students = storedProcedure.getAllStudents(User.Identity.Name);
var studentsView = new AllStudentsViewModel();
foreach (var student in students.ToList())
studentsView.Students.Add(student);
return View(studentsView);
}
public IActionResult Attendance()
{
return View();
}
}
}
You usually get that error when you are trying to use an implementation of an interface, but you have not registered the concrete implementation you want to use with the framework.
If you are using the default dependency injection framework used by asp.net core, you should register it in the ConfigureServices method in Startup.cs class
services.AddTransient<IAssignmentRepository , AssignmentRepository>();
where AssignmentRepository is a your concrete class where you are implementing the IAssignmentRepository interface.
public interface IAssignmentRepository
{
IEnumerable<CreateAssignment> Assignments { get; }
}
public class AssignmentRepository : IAssignmentRepository
{
public IEnumerable<CreateAssignment> Assignments
{
get
{
return new List<CreateAssignment>()
{
new CreateAssignment(),
new CreateAssignment()
};
}
}
}
Here i just hard coded the Assignments property to return 2 CreateAssignment objects. But i guess you probably will be reading it from database and return it.
Now when a new request comes for your controller action method, the framework will create a new object of SessionController and pass an object of AssignmentRepository to the constructor of SessionController
If you are not familiar with the dependency injection concept, i strongly suggest you to spend 16 minutes to read the excellent documentation on learn.microsoft.com before trying to write any further code.
Introduction to Dependency Injection in ASP.NET Core