How do I use 2 seperate models in 1 view in MVC - c#

I'm farely new to MVC with only going on 2 months of experience
What I would like to know is how I can use 2 models in 1 view because the current way I'm doing it is giving me this error: System.NullReferenceException: 'Object reference not set to an instance of an object.'
These are 2 seperate models
namespace SwiftMLS.Data.DataModels;
public class FAQTipCategory : BaseDataModel
{
public string Name { get; set; }
}
namespace SwiftMLS.Data.DataModels;
public class FAQTips : BaseDataModel
{
public int? FAQTipCategoryId { get; set; }
public virtual FAQTipCategory FAQTipCategory { get; set; }
public string? Title { get; set; } = string.Empty;
public string? Description { get; set; } = string.Empty;
}
This is my controller
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using SwiftMLS.Data;
using SwiftMLS.Data.DataModels;
using System.Text.Json;
namespace SwiftMLS.Web.Controllers;
public class FAQController : Controller
{
private readonly ApplicationDbContext _db;
private readonly UserManager<ApplicationUser> _userManager;
public FAQController(ApplicationDbContext context, UserManager<ApplicationUser> userManager)
{
_db = context;
_userManager = userManager;
}
public async Task<IActionResult> Index()
{
ViewData["FAQTipsDesc"] = _db.Tips.ToList();
ViewData["FAQDescription"] = _db.Descrpt;
List<FAQCategory> categories = await _db.Categories.Where(m => !m.IsDeleted).ToListAsync();
return View(categories);
}
//FAQ
[HttpGet]
public async Task<IActionResult> AddPost(FAQCategory category)
{
_db.Categories.Add(category);
await _db.SaveChangesAsync();
return RedirectToAction("Index");
}
[HttpGet]
public async Task<IActionResult> AddDescription(string Title, string Description, int FAQCategoryId)
{
FAQ faq = new FAQ
{
Title = Title,
Description = Description,
FAQCategoryId = FAQCategoryId
};
_db.Descrpt.Add(faq);
await _db.SaveChangesAsync();
return RedirectToAction("Index");
}
//FAQTip
[HttpGet]
public async Task<IActionResult> AddTip(FAQTipCategory tipCategory)
{
_db.TipsCat.Add(tipCategory);
await _db.SaveChangesAsync();
return RedirectToAction("Index");
}
[HttpGet]
public async Task<IActionResult> AddTipDescription(string Title, string Description, int FAQTipCategoryId)
{
FAQTips faqtips = new FAQTips
{
Title = Title,
Description = Description,
FAQTipCategoryId = FAQTipCategoryId
};
_db.Tips.Add(faqtips);
await _db.SaveChangesAsync();
return RedirectToAction("Index");
}
}
This is how I'm calling it in the view
#{
ViewData["Title"] = "FAQTips";
DbSet<FAQTips> Tips = ViewData["FAQTipsDesc"] as DbSet<FAQTips>;
}
#foreach (var item in ViewData["FAQTIPSDesc"] as List<FAQTipCategory>)

Make a real view model, that contains all the things that are going to need to be available on the view (I've guessed at some of the type names):
public class IndexViewModel
{
public List<FAQTip> FaqTips { get; set; }
public List<FAQDescription> FaqDescriptions { get; set; }
public List<FAQTipCategory > Categories { get; set; }
}
Views often use aggregate information from different tables in the database. It's good to create a ViewModel that is scoped to just the web project, that contains all the data the view will need.

Related

Populate a Razor view from two models

I am using entity framework in MVC. I have two models that populate from a SQL table using DBContext
public class Incident
{
public Guid IncidentKey { get; set; }
public int IncidentID { get; set; }
}
public class Request
{
public Guid RequestKey { get; set; }
public int RequestID { get; set; }
}
Below is how I am getting the the data to pull from SQL
public class CallViewerDbContext : DbContext
{
public DbSet<Incident> Incident { get; set; }
public DbSet<Request> Request { get; set; }
}
public class SqlData : IIncident, IRequest
{
private readonly CallViewerDbContext db;
public SqlData(CallViewerDbContext db)
{
this.db = db;
}}
internal static void RegisterContainer(HttpConfiguration httpConfiguration)
{
var builder = new ContainerBuilder();
builder.RegisterControllers(typeof(MvcApplication).Assembly);
builder.RegisterApiControllers(typeof(MvcApplication).Assembly);
builder.RegisterType<SqlData>()
.As<IIncident>()
.InstancePerRequest();
builder.RegisterType<SqlData>()
.As<IRequest>()
.InstancePerRequest();
builder.RegisterType<CallViewerDbContext>().InstancePerRequest();
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
httpConfiguration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
}
I am trying to create a new model that will will allow me to get a count for both of the entity's when I put it on a new Razor view. I am currently able to get one by using the below method.
IIncident db;
public HomeController(IIncident db)
{
this.db = db;
}
public ActionResult Index()
{
var model = db.IGetAll();
return View(model);
}
public IEnumerable<Incident> IGetAll()
{
return db.Incident;
}
I have tried making a new model.
public class DataModel
{
public List<Incident> allIncidents { get; set; }
public List<Request> allRequests { get; set; }
}
I tried adding it to the Register Container
builder.RegisterType<SqlData>()
.As <IDataModel>()
.InstancePerRequest();
Then updated the Home controller
IDataModel db;
public HomeController(IDataModel db)
{
this.db = db;
}
public ActionResult Index()
{
var model = db.MGetAll();
return View(model);
}
public IEnumerable<Incident> MGetAll()
{
return db.Incident;
}
I am trying to return the data using the MGetAll method to get the list of Incidents, I then have a another method that does the same for requests. I was trying to return individual counts to the view first then add in the second.
I am now getting the below stack trace error
The model item passed into the dictionary is of type 'System.Data.Entity.DbSet1[CallViewerData.Models.Incident]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable1[CallViewerData.Models.DataModel]'.
Any help would be greatly appreciated.
Something like this should work:
public ActionResult Index()
{
var model = PrepareViewModel();
return View(model);
}
private DataModel PrepareViewModel()
{
return new DataModel
{
allIncidents = db..., // Get Incidents
allRequests = db... // Get Requests
};
}
And in your view
#model namespace.DataModel
Then you can access them with Model.allIncidents and Model.allRequests.

ASP.NET - API Url to List of Model Objects

Currently I am doing an API call via jQuery, my question is, is there away to do this call in C# or away to convert the results of an API call to an ASP.NET List of Model Objects?
Here is my Model
public class TeamStatsClass
{
public int id { get; set; }
public string name { get; set; }
public string league { get; set; }
public string division { get; set; }
}
And here is my current ajax call
$.ajax({
url: "https://statsapi.web.nhl.com/api/v1/teams?sportId=1",
success: function (data) {
for (var team of data.teams) {
console.log(team.name);
}
}
});
UPDATE
I changed my classes to look like so:
public class StatsTeamsClass
{
public IEnumerable<Teams> teams { get; set; }
public string copyright { get; set; }
}
public class Division
{
public int id { get; set; }
public string name { get; set; }
public string link { get; set; }
}
public class Teams
{
public int id { get; set; }
public string name { get; set; }
public string link { get; set; }
public League league { get; set; }
public Division division { get; set; }
}
and created this method which indeeds puts the results in model object:
public async System.Threading.Tasks.Task<StatsTeamsClass> GetTeams()
{
HttpClient Http = new HttpClient();
var json = await Http.GetStringAsync("https://statsapi.web.nhl.com/api/v1/teams?sportId=1");
StatsTeamsClass teams = JsonConvert.DeserializeObject<StatsTeamsClass>(json);
return teams;
}
But when I try to call this method in another controller, it just hangs there, no error, no nothing, I am assuming it will just time out after a while
public class HomeController : Controller
{
APIController webService = new APIController();
public ActionResult Index()
{
var item = webService.GetTeams().Result.teams;
return View();
}
}
(GetTeams() is inside the controller APIController)
So what would be the proper way to A. get the results of an API in object model and then call those results?
The controller action needs to be made async as well to avoid mixing async-await and blocking calls like .Result or .Wait() that could potentially cause deadlocks.
Reference Async/Await - Best Practices in Asynchronous Programming
public class HomeController : Controller {
APIController webService = new APIController();
public async Task<ActionResult> Index() {
var model = await webService.GetTeams();
var teams = model.teams;
return View();
}
}
Assuming APIController is an actual ApiContoller
public class APIController : ApiController {
//Your original code
public async Task<StatsTeamsClass> GetTeams() {
HttpClient Http = new HttpClient();
var json = await Http.GetStringAsync("https://statsapi.web.nhl.com/api/v1/teams?sportId=1");
StatsTeamsClass teams = JsonConvert.DeserializeObject<StatsTeamsClass>(json);
return teams;
}
//...
}
I would suggest not calling APIController directly like that from the HomeController and instead extract the GetTeams() method out into a reusable service
public class WebService {
static Lazy<HttpClient> http = new Lazy<HttpClient>();
public async Task<T> GetAsync<T>(string url) {
var json = await http.Value.GetStringAsync(url);
return JsonConvert.DeserializeObject<T>(json);
}
public Task<StatsTeamsClass> GetTeamsAsync() {
var url = "https://statsapi.web.nhl.com/api/v1/teams?sportId=1";
return GetAsync<StatsTeamsClass>(url);
}
}
Reference You're using HttpClient wrong
that can be properly used in HomeController
public class HomeController : Controller {
public async Task<ActionResult> Index() {
// Ideally web service should be injected but that topic
// is outside of the scope of the question at the moment.
var webService = new WebService();
var model = await webService.GetTeamsAsync();
var teams = model.teams;
//...
return View(teams);
}
}
The assumption here is that the project is a mixed Asp.Net MVC and Web Api 2+
Index.cshtml
#model IEnumerable<Teams>
#{
ViewBag.Title = "Teams";
}
#if(Model != null && Model.Count() > 0) {
#foreach (var #team in Model) {
<p>#team.name</p>
}
}
Yes, the equivalent in C# would be to use HttpClient. You're best off creating a static instance of the class that you reuse for a particular kind of repeated call:
private static readonly HttpClient Http = new HttpClient();
and then used it from an async method using Newtonsoft.Json like this:
var json = await Http.GetStringAsync("https://statsapi.web.nhl.com/api/v1/teams?sportId=1");
You can then parse this string of JSON into a model class like this:
var model = JsonConvert.DeserializeObject<TeamStatsClass>(json);
As the question is answered by #Daniel above just want to add couple of more points here The json you are getting cannot be directly casted to TeamStatsClass you might have to introduce another base class as teams is the collection in the json you are getting.
Im posting it here to get a clearer view
public class ResponseBaseClass
{
public IEnumerable<TeamStatsClass> teams { get; set; }
public string copyright { get; set; }
}
public class TeamStatsClass
{
public int id { get; set; }
public string name { get; set; }
public Division division { get; set; }
}
public class Division
{
public int id { get; set; }
public string name { get; set; }
public string nameShort { get; set; }
public string link { get; set; }
}
HttpClient Http = new HttpClient();
var json = await Http.GetStringAsync("https://statsapi.web.nhl.com/api/v1/teams?sportId=1");
var model = JsonConvert.DeserializeObject<ResponseBaseClass>(json);
var yourTeamModelObj = model.teams;

ASP.Net Core2.0 with Windows authentication how i add column which will give me username of creater of element

I created asp net core application(MVC) with Windows authentication.
How can i add option or column which is giving information about who created element. Am i doing this in model or in cshtml page ?
For example my model is
public class TestModel
{
public int Id { get; set; }
public string Description { get; set; }
}
My controller ( create )
public IActionResult Create()
{
return View();
}
// POST: TestModels/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("Id,Description,CreatedBy")] TestModel testModel)
{
if (ModelState.IsValid)
{
_context.Add(testModel);
testModel.CreatedBy = this.User.Identity.Name;
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
return View(testModel);
}
You need a property to store name of user, that creates TestModel instance like this:
public class TestModel
{
public int Id { get; set; }
public string Description { get; set; }
public string CreatedBy { get; set; }
}
Then, on controller action that creates and stores instances of TestModel, you need to fill this property like this:
[Authorize]
public class TestModelsController : Controller
{
...
public TestModel Create(int id, string description)
{
var testModel = new TestModel { Id = id, Description = description };
testModel.CreatedBy = this.User.Identity.Name;
// store model (EntityFramework, etc...)
// dbContext.TestModels.Add(testModel);
// dbContext.SaveChanges();
return testModel;
}
...
}
Now you have store element creater name in TestModel.CreatedBy.

Receiving "InvalidOperationException:" in Asp.net MVC

I am trying to create a simple Asp.NET MVC database where a user can create an account, create categories for recipes, and then enter their recipe and file them into the category of their choosing. However, when I attempt to run the test to see if I can reach my list of categories(where I can also add a category), I get the following error message:
InvalidOperationException: Unable to resolve service for type 'LC101Project2017.Data.RecipeDbContext' while attempting to activate 'LC101Project2017.Controllers.CategoryController'.
I'm new to C# and am completely confused as to what I'm doing wrong. Here are my codes:
Controller: (CategoryController.cs)
public class CategoryController : Controller
{
private readonly RecipeDbContext context;
public CategoryController(RecipeDbContext dbContext)
{
context = dbContext;
}
public IActionResult Index()
{
List<RecipeCategory> categories = context.Categories.ToList();
return View(categories);
}
public IActionResult Add()
{
AddCategoryViewModel addCategoryViewModel = new AddCategoryViewModel();
return View(addCategoryViewModel);
}
[HttpPost]
public IActionResult Add(AddCategoryViewModel addCategoryViewModel)
{
if (ModelState.IsValid)
{
RecipeCategory newCategory = new RecipeCategory
{
Name = addCategoryViewModel.Name
};
context.Categories.Add(newCategory);
context.SaveChanges();
CategoryController: return Redirect("/Category");
};
return View(addCategoryViewModel);
}
}
Database (RecipeDbContext.cs)
public class RecipeDbContext : DbContext
{
public DbSet<Recipe> Recipes { get; set; }
public DbSet<RecipeCategory> Categories { get; set; }
}
MODEL (RecipeCategory.cs)
public class RecipeCategory
{
public int ID { get; set; }
public string Name { get; set; }
public IList<RecipeCategory> RecipeCategories { get; set; }
}
Check if you have configured to use your DbContext, RecipeDbContext, inside ConfigureServices method in Startup.cs
The method should look like this;
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddDbContext<RecipeDbContext >(options =>
options.UseSqlServer(Configuration.GetConnectionString("ConnectionStringName")));
}
Updated the answer in response to the comment by #Mel Mason
You need to declare a constructor that accepts DbContextOptions<RecipeDbContext>.
public class RecipeDbContext : DbContext
{
public DbSet<Recipe> Recipes { get; set; }
public DbSet<RecipeCategory> Categories { get; set; }
public RecipeDbContext(DbContextOptions<RecipeDbContext> options)
: base(options)
{ }
}
You can also check the official documentation:
https://learn.microsoft.com/en-us/ef/core/miscellaneous/configuring-dbcontext
https://learn.microsoft.com/en-us/ef/core/miscellaneous/connection-strings
Hope this helps.

HttpPost Action Method In WebAPI Controller Not Working In .NETCore Project

I'm trying to insert into a DB using WebAPI for a .Net Core project but it's not working -
[Route("api/IMTWebAPI")]
public class IMTWebAPIController : BaseController
{
[HttpPost("Create")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([FromBody] InterMemberTransfer interMemberTransfer)
{
var testController = new CRUDForIMT(_context);
var response = await testController.Create(interMemberTransfer);
return Json(response);
}
}
CRUDForIMT -
private readonly ARMStocktradeV2Context _context;
public CRUDForIMT(ARMStocktradeV2Context context)
{
_context = context;
}
public async Task<int> Create([Bind("Id,Date,EmailAddress,PhoneNumber,ResidentBroker")] InterMemberTransfer interMemberTransfer)
{
_context.Add(interMemberTransfer);
var res = await _context.SaveChangesAsync();
return res;
}
Anytime I test in postman, i get a 400 bad request and no response.
My Model -
public partial class InterMemberTransfer
{
public long Id { get; set; }
public DateTime Date { get; set; }
public string EmailAddress { get; set; }
public long PhoneNumber { get; set; }
public string ResidentBroker { get; set; }
}
I added a breakpoint in the Create action method but it's not even getting to the breakpoint.
Remove [ValidateAntiForgeryToken] from your Create Method.
Otherwise you'll never get into this Method at all. At least not with Postman.

Categories

Resources