Good morning, I have a problem with returning my database information (generated using the EF 6 code-first method) for my View Razor. The issue is that I'm wanting to return the information from inherited classes in the View, but they are not available, only the properties of the base class are presented, not those of the dependent classes.
The following are the Model, Controller, and View classes used:
Class ClientModel
public class Client
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ClientId { get; set; }
[DataType(DataType.Date)]
public DateTime Birth { get; set; }
[Display(Name = "Telefone principal")]
public string Phone1 { get; set; }
[Display(Name = "Telefone Alternativo")]
public string Phone2 { get; set; }
public ICollection<OrcamentoContato> Contacts { get; set; }
public ICollection<Contrato> Contracts { get; set; }
}
Class FisicModel
public class PessoaFisica : Client
{
public TipoPessoa PersonType { get; set; }
[Required]
[Display(Name = "Nome completo*")]
[StringLength(250, ErrorMessage = "O campo é obrigatório.")]
public string Name { get; set; }
public string RG { get; set; }
[Required(ErrorMessage = "O campo CPF é obrigatório.")]
[StringLength(14)]
public string CPF { get; set; }
[Display(Name = "Filiação")]
public string Filiacao { get; set; }
[Display(Name = "Endereço")]
public string Address { get; set; }
}
Class JuridicModel
public class PessoaJuridica : Client
{
public TipoPessoa PersonType { get; set; }
[Required]
[Display(Name = "Razão Social*")]
[StringLength(200, ErrorMessage = "O campo é obrigatório.")]
public string SocialName { get; set; }
[Required]
[Display(Name = "CNPJ*")]
[StringLength(200, ErrorMessage = "O campo é obrigatório.")]
public string CNPJ { get; set; }
[Display(Name = "Inscrição Estadual")]
public string InscricaoEstadual { get; set; }
[Display(Name = "Inscrição Municipal")]
public string InscricaoMunicipal { get; set; }
[Display(Name = "Endereço")]
public string Address { get; set; }
public string ContactWith { get; set; }
}
Controller
public ActionResult Index()
{
var clients = db.Clients.ToList();
return View(clients);
}
Index View
#model IEnumerable<CabinePhoto.Models.Entidades.Client>
<table class="table">
<tr>
<th>
#Html.DisplayNameFor(model => model.Birth)
</th>
<th>
#Html.DisplayNameFor(model => model.Phone1)
</th>
<th>
#Html.DisplayNameFor(model => model.Phone2)
</th>
<th></th>
</tr>
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.Birth)
</td>
<td>
#Html.DisplayFor(modelItem => item.Phone1)
</td>
<td>
#Html.DisplayFor(modelItem => item.Phone2)
</td>
</tr>
}
IdentityModel
public DbSet<Client> Clients { get; set; }
public DbSet<PessoaFisica> PessoaFisica { get; set; }
public DbSet<PessoaJuridica> PessoaJuridica { get; set; }
All the information is stored in the same client table, since I'm using the form of inheritance by hierarchy, but in the view only the client model information is returned
I've been able to solve the problem. I'll leave here recorded what I did to solve the problem in respect to inheritance.
First, I created a ViewModel and put two ICollection properties, I modified the controller by adding the queries referring to the client table, but specifically bringing the required types and finally, I passed the ViewModel to the Index.cshtml and I used two foreachs to retrieve the information from According to the type specified, shown below:
ClientesiewModel.cs
public class ClientesViewModel
{
public IEnumerable<PessoaFisica> Fisica { get; set; }
public IEnumerable<PessoaJuridica> Juridica { get; set; }
}
controlle.cs
public ActionResult Index()
{
var cliente_fisico = db.Clientes.OfType<PessoaFisica>().ToList();
var cliente_juridico = db.Clientes.OfType<PessoaJuridica>().ToList();
var cliente = db.Clientes.ToList();
ClientesViewModel clientes = new ClientesViewModel()
{
Fisica = cliente_fisico,
Juridica = cliente_juridico
};
return View(clientes);
}
View Index.cshtml
#model CabinePhoto.ViewModels.ClientesViewModel
<table class="table">
<tr>
<th>
#Html.DisplayName("Nome")
</th>
<th>
#Html.DisplayName("Telefone")
</th>
<th>
#Html.DisplayName("Telefone 2")
</th>
<th></th>
</tr>
#if (Model.Fisica != null || Model.Juridica != null)
{
foreach (var fisica in Model.Fisica)
{
<tr>
<td>
#Html.DisplayFor(modelItem => fisica.NomeCompleto)
</td>
<td>
#Html.DisplayFor(modelItem => fisica.TelefonePrincipal)
</td>
<td>
#Html.DisplayFor(modelItem => fisica.TelefoneAlternativo)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = fisica.ClienteId }) |
#Html.ActionLink("Details", "Details", new { id = fisica.ClienteId }) |
#Html.ActionLink("Delete", "Delete", new { id = fisica.ClienteId })
</td>
</tr>
}
foreach (var juridica in Model.Juridica)
{
<tr>
<td>
#Html.DisplayFor(modelItem => juridica.PessoaContato)
</td>
<td>
#Html.DisplayFor(modelItem => juridica.CNPJ)
</td>
<td>
#Html.DisplayFor(modelItem => juridica.TelefonePrincipal)
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id = juridica.ClienteId }) |
#Html.ActionLink("Details", "Details", new { id = juridica.ClienteId }) |
#Html.ActionLink("Delete", "Delete", new { id = juridica.ClienteId })
</td>
</tr>
}
}
</table>
Thanks for the help previously assigned
Suppose you have a variable like this in your controller.
Client c = new Client();
If you later wrote
c.ClientId = 1;
it would work perfectly.
Similarly, if you wrote
PessoaFisica p = new PessoaFisica ();
and later
p.Name = "abc";
it would also work.
However, if you wrote
Client c = new PessoaFisica();
c.Name = "abc";
It will fail to compile.
Along the same lines
#model IEnumerable <CabinePhoto.Models.Entidades.Client>
means static type of your Model is a collection of Client objects, it will only allow you to bind to properties defined in Client class.
Entity Framework actually returns the correct types, but you're effectively upcasting everything to Client by the variable type you're storing into and the model definition of the view. Essentially, you just need to cast to the right type. Unfortunately, there's no way to just know what type it should be after it's been upcasted. You'll have to conditionally check:
if (item is PessoaFisica)
{
var pessoaFiscica = (PessoaFisica)item;
// now you can access those derived typed properties off of `pessoaFiscica`
}
You can also use as and rely on the fact that it returns null when something can't be casted:
var pessoaFiscica = item as PessoaFisica;
if (pessoaFiscica != null)
{
// access PessoaFiscica properties
}
With C# 7.0, you can use the pattern matching syntax to streamline it a little:
if (item is PessoaFiscica pessoaFiscica)
{
// use `pessoaFiscica`
}
The pattern matching syntax also allows you use switch blocks, which may make things much easier on you:
switch (item)
{
case PessoaFisica pessoaFisica:
// do something with `PessoaFisica` instance
break;
case PessoaJuridica pessoaJuridica:
// do something with `PessoaJuridica` instance
break;
default:
// do something with generic `Client` instance
break;
}
Related
I have the following code in my controller:
public ActionResult Index(int Id)
{
Landbase _db = new Landbase();
OwnerWorkingInterests workingInterests = new OwnerWorkingInterests();
//Owner owner = new Owner();
var query = (from wg in _db.WorkingInterestGroups
join wi in _db.WorkingInterests on wg.Id equals wi.WorkingInterestGroupId
join l in _db.Leases on wg.LeaseId equals l.Id
where wi.OwnerId.Equals(Id)
select new OwnerWorkingInterests()
{
LeaseId = l.Id,
WorkingInterestAmount = wi.WorkingInterestAmount,
WorkingInterestGroupName = wg.Name,
ClientAlias = l.ClientAlias,
Lessor = l.Lessor,
Lessee = l.Lessee,
VolDocNumber = l.VolumeDocumentNumber,
County = l.County,
District = l.District
}).ToList();
//List<string> OwnerWorkingInterest = query.ToList<string>();
return View(query);
}
I have the following code in my view:
<div id="OwnerWorkingInterests" class="tab-pane fade">
<h3>Working Interests</h3>
<table class="table">
<thead>
<tr>
<td>Lease Id:</td>
<td>Working Int:</td>
<td>WI Group Name:</td>
<td>Alias:</td>
<td>Lessor:</td>
<td>Lessee:</td>
<td>VolPg:</td>
<td>County:</td>
<td>District0:</td>
</tr>
</thead>
<tbody>
#foreach (var owi in OwnerWorkingInterests)
{
<tr>
<td>#owi.LeaseId</td>
<td>#owi.WorkingInterestAmount</td>
<td>#owi.WorkingInterestGroupName</td>
<td>#owi.ClientAlias</td>
<td>#owi.Lessor</td>
<td>#owi.Lessee</td>
<td>#owi.VolDocNumber</td>
<td>#owi.County</td>
<td>#owi.District</td>
</tr>
}
</tbody>
</table>
</div>
I thought this would populate the table with the proper information
This is the viewmodel:
namespace LandPortal.Models
{
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.Spatial;
public partial class WorkingInterest
{
public int Id { get; set; }
public int? OwnerId { get; set; }
[Column("WorkingInterest")]
public decimal? WorkingInterestAmount { get; set; }
[StringLength(45)]
public string CreateUser { get; set; }
[StringLength(45)]
public string ModifyUser { get; set; }
public Guid? CreateUserId { get; set; }
public Guid? ModifyUserId { get; set; }
public DateTime? CreateDate { get; set; }
public DateTime? ModifyDate { get; set; }
public int? WorkingInterestGroupId { get; set; }
public WorkingInterestGroup WorkingInterestGroup { get; set; }
public decimal? ORRI { get; set; }
public int? ORRIOwnerId { get; set; }
public virtual Owner Owner { get; set; }
}
}
So what happens is it throws a very vague error when I run it in debugger. It literally just says Error: An error occurred when processing your request. So I am assuming that the list is populating but not working in the foreach in the view. I could be wrong at this point.
Here are the model directives for the view
#using LandPortal.Models
#using LandPortal.ViewModels
#using Microsoft.Ajax.Utilities
#model LandPortal.Models.Owner
If the view expects a model that is of type LandPortal.Models.Owner, and Index returns an entire ActionResult, then Index needs to return a model of that type.
A tiny example:
public ActionResult Index(int Id)
{
Landbase _db = new Landbase();
Owner owner = new Owner();
// some query has to set properties on this owner object
// let's pretend there's a property named OwnerWorkingInterests on it
owner.OwnerWorkingInterests = query.ToList(); // you will have to define "query" and set it similar to how you already did
return View(owner);
}
Now your view can access the property on the model as so
#foreach (var owi in Model.OwnerWorkingInterests)
This is a very high level example, but I see you have a partial class and mentioned partial views in your comment. If you have a large view and are trying to break up a query into pieces, that can be done with PartialViewResult and will be a bit different from this.
I'm making a feedback form on mvc 5 and the feedback form consists of 10 questions having 5 selected answers each. Those selected answers are the group of radio buttons that I'm populating. But in post action method, the radio buttons are not getting bind. Below is my code.
Model.cs
public class FeedbackForm
{
public string Id { get; set; }
public Models.Questionnaire mquestions { get; set; }
public List<Questionnaire> Question { get; set; }
public Batches batches { get; set; }
public User user { get; set; }
public DateTime Datetime { get; set; }
}
public class Questionnaire
{
public string QuestionId { get; set; }
public string QuestionTitle { get; set; }
[Required]
public int? SelectedAnswer { get; set; } // for binding
public IEnumerable<QuestionnaireStatus> PossibleAnswers { get; set; }
}
public class QuestionnaireStatus
{
public string StatusId { get; set; }
public string StatusTitle { get; set; }
}
Controller.cs
public ActionResult Index()
{
List<Models.FeedbackForm> lstfeedback = new List<Models.FeedbackForm>();
Models.FeedbackForm form = new Models.FeedbackForm();
Context.FeedBackForm contFeedback = new Context.FeedBackForm();
Models.FeedbackForm vm = new Models.FeedbackForm();
form.Question = new List<Models.Questionnaire>();
form.Question = contFeedback.GetAllQuestionnaire();
for (int i = 0; i < form.Question.Count; i++)
{
lstfeedback.Add(new Models.FeedbackForm
{
Question = new List<Models.Questionnaire>()
{ new Models.Questionnaire
{
QuestionId = form.Question[i].QuestionId,
QuestionTitle = form.Question[i].QuestionTitle
,
PossibleAnswers = contFeedback.GetAllStatus()
},
}
});
}
return View(lstfeedback);
}
View.cshtml
#using (Html.BeginForm())
{
<table class="table">
<tr>
<th>
#*#Html.Display("Questions")*#
</th>
#for (int i = 0; i < Model.Count; i++)
{
for (int j = 0; j < Model[i].Question.Count; j++)
{
foreach (var item in Model[i].Question[j].PossibleAnswers)
{
<th>
#item.StatusTitle
</th>
}
break;
}
break;
}
</tr>
#for (int i = 0; i < Model.Count; i++)
{
<tr>
#for (int j = 0; j < Model[i].Question.Count; j++)
{
<td>
#Html.DisplayFor(m=>Model[i].Question[j].QuestionTitle)
</td>
foreach (var item in Model[i].Question[j].PossibleAnswers)
{
<td>
#Html.RadioButtonFor(m => Model[i].Question[j].SelectedAnswer, item.StatusId, new { id = item.StatusId })
</td>
}
#Html.ValidationMessageFor(m => Model[i].Question[j].SelectedAnswer)
}
</tr>
}
</table>
<div class="form-group">
<div class="col-md-10">
<input type="submit" id="Attendance" value="Submit" class="btn btn-default" />
</div>
</div>
}
I am calling partial view controller through J_Query, controller action(ListInventoryProduct) is called and execute without error( the list is populated). But the partial view is not displayed.
In browser Developer tool says it is internal server error.
I can't figure out what is the problem.
The following is my code.
Model:
public class InventoryProductsViewModel
{
public long Id { get; set; }
[Display(Name = "Product Name")]
public string Title { get; set; }
[Display(Name = "SubCategory Name")]
public string SubCategory { get; set; }
[Display(Name = "Balance")]
public int Balance { get; set; }
[Display(Name = "Count")]
public int InventoryCount { get; set; }
[Display(Name = "Difference")]
public string Difference { get; set; }
[Display(Name = "IMEINumber")]
public string IMEINumber { get; set; }
[Display(Name = "BarrcodesString")]
public string BarrcodesString { get; set; }
public long subId { get; set; }
// public List<Category> lstCategory { get; set; }
}
Controller Action
public ActionResult LoadInventoryProducts(long categoryId)
{
Session["Products"] = null;
Session["InventoryMissing"] = null;
var userSession = Session.GetSessionUserInfo();
if (userSession != null)
{
List<InventoryProductsViewModel> products = db.Products.Where(p => !p.IsDeleted && p.CompanyId == userSession.CompanyId && (categoryId == 0 || p.SubCategory.CategoryId == categoryId)).Select(p => new InventoryProductsViewModel { Id = p.Id, Title = p.Title, SubCategory = p.SubCategory.Title, IMEINumber = p.IMEINumber, Balance = (p.PurchasedQuantity - p.SoldQuantity) }).ToList(); //&& (subCategoryId == 0 || p.SubCategoryId == subCategoryId)
Session["Products"] = products;
if (Session["InventoryMissing"] == null)
{
Session["InventoryMissing"] = new List<InventoryMissing>();
return PartialView("ProductsPartialView", products);
}
else
{
return Redirect("~/Error/Error");
}
}
}
PartialView
#model List<ViewModel.InventoryProductsViewModel>
<table >
<tr>
<th>#Html.DisplayNameFor(Model[0].Title)</th>
<th>#Html.DisplayNameFor(Model[0].SubCategory)</th>
<th>#Html.Label("Balance")</th>
<th>#Html.Label("Count")</th>
<th>#Html.Label("Difference")</th>
<th>#Html.Label("IMEI Number")</th>
</tr>
#for (int i = 0; i < Model.Count(); i++ )
{
<tr id="#Model[i].Id">
<td>
#Html.Hidden(Model[i].subId)
#Html.DisplayFor(Model[i].Title)
</td>
<td>#Html.DisplayFor(Model[i].SubCategory)</td>
<td class="balance">#Html.DisplayFor(Model[i].Balance)</td>
<td>#Html.EditorFor(Model[i].InventoryCount)</td>
<td class="difference">0</td>
<td>#Html.DisplayFor(modelItem =>Model[i].IMEINumber)</td>
</tr>
}
</table>
Html Helpers For Model were used which was creating problem.
Correct Helpers are as follow.
#model List<HiTechPOS.ViewModel.InventoryProductsViewModel>
<table class=" table table-striped table-advance table-hover">
<tr>
<th>
#Html.Label("Product Name")
</th>
<th>
#Html.Label("SubCategory Name")
</th>
<th>
#Html.Label("Balance")
</th>
<th>
#Html.Label("Count")
</th>
<th>
#Html.Label("Difference")
</th>
<th>
#Html.Label("IMEI Number")
</th>
</tr>
#for (int i = 0; i < Model.Count(); i++ )
{
<tr id="#Model[i].Id">
#*<td>
#Html.DisplayFor(modelItem => item.Id)
</td>*#
<td>
#Html.Hidden(Model[i].subId.ToString())
#Html.Label(Model[i].Title)
</td>
<td>
#Html.Label(Model[i].SubCategory)
</td>
<td class="balance">
#Html.Label(Model[i].Balance.ToString())
</td>
<td>
#Html.Editor(Model[i].InventoryCount.ToString())
</td>
<td class="difference">
#Html.Label(Model[i].Difference.ToString())
</td>
<td>
if(Model[i].IMEINumber == null)
{
#Html.Label("")
}
else
{
#Html.Label(Model[i].IMEINumber)
}
</td>
</tr>
}
I pass an instance of LoginViewModel to my view (Login)
When I click the submit button on the form another instance of LoginViewModel is created.
I can observe this because I put a breakpoint in the constructor.
Why does this happen and how can I fix it?
#using ViewModels
#model LoginViewModel
<form action="~/Home/VerifyLogin" method="post">
<table>
<tr>
<td>
User Name:
</td>
<td>
#Html.DropDownListFor(m => m.SelectedUserID, Model.UserList);
</td>
</tr>
<tr>
<td>
Password:
</td>
<td>
#Html.TextAreaFor(m => m.SelectedPassword);
</td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value ="Login" />
</td>
</tr>
</table>
</form>
Controller
public class HomeController : Controller
{
//
// GET: /Home/
public ActionResult Index()
{
return View();
}
public ActionResult RedirectToLogin()
{
return View("Login", new LoginViewModel());
}
[HttpPost]
public void VerifyLogin(LoginViewModel vm)
{
bool sucess = false;
// some logic
if(sucess)
RedirectToAction("ProjectList", "Project");
}
ViewModel
public class LoginViewModel :BaseViewModel
{
public IEnumerable<User> Users { get; private set; }
public IEnumerable<SelectListItem> UserList { get; private set; }
public int SelectedUserID { get; set; }
public string SelectedPassword { get; set; }
public LoginViewModel()
{
Users = DataService.GetUsers();
UserList = new SelectList(Users, "ID", "Name");
}
public bool Login(string userName, string password)
{
return true;
}
}
One is created here...
return View("Login", new LoginViewModel());
And one is created here
public void VerifyLogin(LoginViewModel vm)
The model binder creates objects and fills them with the parameters it receives from the request. This is normal operation.
To fix your problem, I would do this...
public ActionResult RedirectToLogin()
{
var vm = new LoginViewModel()
{
Users = DataService.GetUsers();
UserList = new SelectList(Users, "ID", "Name");
};
return View("Login", vm);
}
public LoginViewModel()
{
//Removed code
}
I have an application with a need to select all of the states (US) where a service is available. For convenience, I was trying to create the list from an enum "State", displayu array as checkboxes and save selected to database. Can't figure out the best way to create this.
Here is the class:
public class Offering
{
public int OfferingID { get; set; }
public string Name { get; set; }
public IList<State> States { get; set; }
}
public enum State
{
AL, AK, AZ, CA, CO, CT, DC, DE, FL
}
New to MVC so any help is greatly appreciated.
The simplest way without having to write your own model binders would be something like this:
<table>
<thead>
<tr>
<td></td>
<td>State</td>
</tr>
</thead>
<tbody>
#foreach (State state in Enum.GetValues(typeof(State)))
{
<tr>
<td>#Html.CheckBoxFor(model => model.States[state.ToString()].IsStateSelected)</td>
<td>#state.ToString()</td>
</tr>
}
</tbody>
Using this approach, you would need to make a new class, StateViewModel:
class StateViewModel
{
public bool IsStateSelected { get; set; }
}
and an additional property in your Offering model,
public Dictionary<string, bool> States { get; set; }