Populate a Razor view from two models - c#

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.

Related

How do I use 2 seperate models in 1 view in MVC

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.

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.

Entity Framework 6 - Dependency Injection with Unity - Repository pattern - Add or Update exception for many to many relationship

I have a problem when adding new values with a many to many mapping in Entity Framework. I know about the unit of work pattern but in our solution we would like to keep a simple repository pattern and not a unit of work class that contains everything. Is this possible or should I just implement Unit of Work right away?
If I don't use iSupplierRepository below a supplier will be added, but it will always add a new one even though there already exists one with that name.
Error:
The relationship between the two objects cannot be defined because
they are attached to different ObjectContext objects.
Repository example:
public class SupplierRepository : IntEntityRepository<Supplier, DbContext>, ISupplierRepository
{
public SupplierRepository(DbContext context) : base(context, context.Suppliers)
{
}
}
Inherited repositories:
public class IntEntityRepository<TEntity, TContext> : EntityRepository<TEntity, TContext, int>
where TEntity : class, IEntity<int>
where TContext : BaseIdentityDbContext
{
public IntEntityRepository(TContext context, IDbSet<TEntity> set) : base(context, set)
{
}
public override async Task<TEntity> GetAsync(int id)
{
return (await GetAsync(entity => entity.Id == id)).SingleOrDefault();
}
...
public abstract class EntityRepository<TEntity, TContext, TId> : IEntityRepository<TEntity, TId>
where TEntity : class, IEntity<TId>
where TContext : BaseIdentityDbContext
{
protected TContext Context { get; }
protected IDbSet<TEntity> Set { get; }
protected EntityRepository(TContext context, IDbSet<TEntity> set)
{
Context = context;
Set = set;
}
public abstract Task<TEntity> GetAsync(TId id);
...
Unity:
container.RegisterType<ISupplierRepository, SupplierRepository>();
container.RegisterType<IContactRepository, ContactRepository>();
Controller:
private readonly IContactRepository iContactRepository;
private readonly ISupplierRepository iSupplierRepository;
public ContactsController(IContactRepository iContactRepository, ISupplierRepository iSupplierRepository)
{
this.iContactRepository = iContactRepository;
this.iSupplierRepository = iSupplierRepository;
}
[HttpPut]
[Route("UpdateContact/{id}")]
public async Task<IHttpActionResult> UpdateContact(ContactViewModel contactVm, int id)
{
try
{
var supplierList = new List<Supplier>();
foreach (var contactVmSupplier in contactVm.Suppliers)
{
var supplier = await iSupplierRepository.GetAsync(contactVmSupplier.Id);
supplierList.Add(supplier);
}
var contactOriginal = await iContactRepository.GetAsync(id);
var updatedContact = Mapper.Map<ContactViewModel, Contact>(contactVm, contactOriginal);
updatedContact.Suppliers = supplierList;
await iContactRepository.UpdateAsync(updatedContact);
return Ok();
}
catch (Exception e)
{
throw new Exception("Could not update a contact", e);
}
}
Viewmodels:
public class ContactViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<SupplierViewModel> Suppliers { get; set; }
}
public class SupplierViewModel
{
public int Id { get; set; }
public string Name { get; set; }
}
Models:
public class Contact : IEntity<int>
{
public Contact()
{
Suppliers = new List<Supplier>();
}
[Key]
public int Id { get; set; }
public DateTime Created { get; set; }
public DateTime Updated { get; set; }
public string Name { get; set; }
public ICollection<Supplier> Suppliers { get; set; }
}
public class Supplier: IEntity<int>
{
public Supplier()
{
Contacts = new List<Contact>();
}
[Key]
public int Id { get; set; }
public DateTime Created { get; set; }
public DateTime Updated { get; set; }
public string Name { get; set; }
public virtual ICollection<Contact> Contacts { get; set; }
}
If you install the Unity bootstrapper for ASP.NET Web API package, a UnityHierarchicalDependencyResolver is available which will use a new child container for each IHttpController resolution effectively making all registrations with a HierarchicalLifetimeManager resolved per request so that all repository instances in a controller will use the same DbContext.
The NuGet package will also install some bootstrapping code in App_Start which uses WebActivatorEx. You can either use this approach or change to align with what you are using right now. Based on your posted code it would look something like:
public static void ConfigureUnity(HttpConfiguration config)
{
var container = new UnityContainer();
container.RegisterType<DbContext>(new HierarchicalLifetimeManager());
container.RegisterType<ISupplierRepository, SupplierRepository>();
container.RegisterType<IContactRepository, ContactRepository>();
config.DependencyResolver = new UnityHierarchicalDependencyResolver(container);
}
Solved it like this, dependency injection is from the tutorial Dependency Injection in ASP.NET Web API 2.
https://learn.microsoft.com/en-us/aspnet/web-api/overview/advanced/dependency-injection
App_Start -> WebApiConfig
public static void Register(HttpConfiguration config)
{
UnityConfig.ConfigureUnity(config);
...
UnityConfig:
public static void ConfigureUnity(HttpConfiguration config)
{
var context = new DbContext();
var container = new UnityContainer();
container.RegisterType<ISupplierRepository, SupplierRepository>(new InjectionConstructor(context));
container.RegisterType<IContactRepository, ContactRepository>(new InjectionConstructor(context));
config.DependencyResolver = new UnityResolver(container);
}
Update: Use Randy Levy's answer instead.
My recommendation here is not to use Repository or UoW at all. EF already has them implemented. You'll encounter a lot of issues trying to re-implement them.
As to specific issue you encounter with exception: you have to use the same DbContext for your entities. At the same time, you wouldn't like to use DbContext as Singleton and use it per-request instead. A possible solution for it might be found here.
Application_BeginRequest(...)
{
var childContainer = _container.CreateChildContainer();
HttpContext.Items["container"] = childContainer;
childContainer.RegisterType<ObjectContext, MyContext>
(new ContainerControlledLifetimeManager());
}
Application_EndRequest(...)
{
var container = HttpContext.Items["container"] as IUnityContainer
if(container != null)
container.Dispose();
}

Using Controller.Content Outside MVC Controller c#

I'm creating a static class with static methods for helping the controllers to do their job. When build the application I get the following error:
Error 40 'System.Web.Mvc.Controller.Content(string)' is inaccessible due to its protection level"
Any idea how to solve this problem?
Notes:
It's a c# mvc aplication
public static ActionResult GetAlbumJSON(AlbumVO album)
{
return Controller.Content(
JsonConvert.SerializeObject(new
{
max_car = #ABookClient.maxCharsProjecName,
trans_img = #ABookClient.Transparent_Image,
show_description = #ABookClient.Show_Product_Description,
product_type = "Album",
obj = CreateObjAlbumVO(album),
})
);
}
Content method is protected internal, so you can't use it outside of controller.
Controller.Content Method. Most probably your static class violates SRP principle. Let him do his job (initializing, serializing,...) and controller - controller's job - return result to the client.
protected internal ContentResult Content(string content)
It would look smth like:
public static class MyHelper
{
public static object GetAlbum(AlbumVO album)
{
return new
{
max_car = #ABookClient.maxCharsProjecName,
trans_img = #ABookClient.Transparent_Image,
show_description = #ABookClient.Show_Product_Description,
product_type = "Album",
obj = CreateObjAlbumVO(album),
};
}
}
public class AlbumController : Controller
{
public ActionResult GetAlbums(int id)
{
var album = Context.GetAlbum(id);
var convertedResult = MyHelper.GetAlbum(album);
return Json(convertedResult);
}
}
Also I'd advice to take a look at AutoMapper for creating client response objects
I think this is valid case for a view-model for a JSON result since you do want a separation between the Domain model and the data sent back to the client. Using a view model also gives you a proper place to put this mapping between the domain model and the view (the JSON) so you don't need to delegate to a helper class.
public class AlbumModel
{
[JsonProperty(PropertyName = "max_car")]
public int MaxChars { get; private set; }
[JsonProperty(PropertyName = "trans_img")]
public string TransparentImage { get; private set; }
[JsonProperty(PropertyName = "product_type")]
public string ProductType { get; private set; }
[JsonProperty(PropertyName = "obj")]
public AlbumInfo Object { get; private set; }
[JsonProperty(PropertyName = "show_description")]
public bool ShowProductDescription { get; private set; }
public AlbumModel(AlbumVO album)
{
MaxChars = album.maxCharsProjecName;
TransparentImage = album.Transparent_Image;
ShowProductDescription = album.Show_Product_Description;
ProductType = "Album";
Object = new AlbumInfo(album);
}
}
The AlbumInfo class provides additional mappings for your JSON result, which becomes the "obj" property sent back to the client.
public class AlbumInfo
{
// ... define properties here
public AlbumInfo(AlbumVO album)
{
// ... map properties here
}
}
And your controller becomes nice and clean:
public class AlbumController : Conrtoller
{
public ActionResult GetAlbums(int id)
{
var album = Context.GetAlbum(id);
var model = new AlbumModel(album);
return Json(model);
}
}

DbContext and Interface in HTTPPost action

I am using StructureMap for my dependency resolving. I am faced with an issue I don't know how to solve as the only way to make a form posting I understand will end up being without dependency injection which negate the whole essence of having StructureMap in my project.
The error is that I don't have Add method in my IdbContext Model. Below is my approach.
I have a Model named Module defined as below
public class Module
{
public virtual int ID { get; set; }
public virtual string Name { get; set; }
public virtual DateTime? DateCreated { get; set; }
public virtual string ModuleDescription { get; set; }
}
and an interface defined as below.
public interface ISolnetDataSource
{
IQueryable<Module> Modules { get; }
void Save();
}
A DbContext like below
public class CMSDB : DbContext, ISolnetDataSource
{
public CMSDB() : base("DefaultConnection")
{
}
public DbSet<Module> Modules { get; set; }
void ISolnetDataSource.Save()
{
SaveChanges();
}
IQueryable<Module> ISolnetDataSource.Modules
{
get { return Modules; }
}
}
and my controller defined as
[HttpPost]
public ActionResult Index(CreateModulesViewModels module)
{
ViewBag.ListModule = _db.Modules.ToList();
if (ModelState.IsValid)
{
var model = new CreateModulesViewModels();
var CreateModule = new Module();
CreateModule.Name = module.Name;
CreateModule.ModuleDescription = module.ModuleDescription;
CreateModule.DateCreated = DateTime.Now;
//_db.modules.Add(CreateModule);
_db.Save();
return View(model);
}
return View(module);
}
The challenge I have with this is that I could'nt do _db.modules.Add(CreateModule) (the commented line in the controller) so as to add a new record. What I'm doing wrong. I want to do this using the best approach applicable.
What about:
_db.modules.Entry(CreateModule).State = System.Data.EntityState.Added;

Categories

Resources