I've been stuck in this issue for the whole afternoon, appreciate if anyone can tell me how, Thanks!
I am using MVC 3 and setup two models:
public class Employee
{
[Key]
public virtual int Id { get; set; }
public virtual string Emp_Id { get; set; }
public virtual string Emp_Name { get; set; }
public virtual int Emp_Type { get; set; }
public virtual ICollection<Job> Jobs { get; set; }
}
public class Job
{
[Key]
public virtual int Id { get; set; }
public virtual string SO { get; set; }
public virtual int? Manhour_Type { get; set; }
public virtual DateTime? StartJob { get; set; }
public virtual DateTime? EndJob { get; set; }
public virtual double? Duration { get; set; }
public virtual Employee employee { get; set; }
}
Two databases are generated by this code:
public class JMCDB : DbContext
{
public DbSet<Employee> Employees { get; set; }
public DbSet<Job> Jobs { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Job>().HasRequired(j => j.employee).WithMany(e => e.Jobs);
}
}
I Create the JobController as follows:
public class JobController : Controller
{
//
// GET: /Job/
JMCDB _db = new JMCDB();
public ActionResult Index()
{
var job = _db.Jobs;
return View(job);
}
}
In "View", I wanna see the employee name of this specific job:
#foreach (var item in Model) {
<tr>
<td>
#Html.DisplayFor(modelItem => item.SO)
</td>
<td>
#Html.DisplayFor(modelItem => item.Manhour_Type)
</td>
<td>
#Html.DisplayFor(modelItem => item.StartJob)
</td>
<td>
#Html.DisplayFor(modelItem => item.EndJob)
</td>
<td>
#Html.DisplayFor(modelItem => item.employee.Emp_Name)
</td>
<td>
</td>
</tr>
}
But when I run the program, there is an error for the last line of code
"#Html.DisplayFor(modelItem => item.employee.Emp_Name)"
I don't know how to solve, Thanks if anyone can tell me how.
You can use the Include method to eager load the navigational property employee
public ActionResult Index()
{
var job = _db.Jobs.Include(job => job.employee);
return View(job);
}
Related
As a sample that I can build on, I want to use Teams.ClubNo to find Club.ClubNo and add Club.ClubName to the Teams Index page.
Sounds simple but I haven't found anything that works yet.
The filter (which I like) is confusing what I need to do in the controller.
No direct navigation exists in the database. Am using EF 2.1
Part of _context.Club and _context.Teams
public partial class Club
{
public Club()
public short ClubNo { get; set; }
public string ClubName { get; set; }
}
public partial class Teams
{
public Teams()
public string Division { get; set; }
public string Grade { get; set; }
public short HomeGround { get; set; }
public short ClubNo { get; set; }
}
Part of my TeamsController.cs to display my Index page.
public class TeamsController : Controller
{
private readonly SSFA_SQLContext _context;
public TeamsController(SSFA_SQLContext context)
{
_context = context;
}
// GET: Teams
public async Task<IActionResult> Index(string filter, string filter1, string filter2, int page = 1, string sortExpression = "Division")
{
var qry = _context.Teams.AsNoTracking().AsQueryable();
if (!string.IsNullOrWhiteSpace(filter))
{ qry = qry.Where(p => p.Division.Contains(filter)); }
var model = await PagingList.CreateAsync(qry, 15, page, sortExpression, "Division");
model.RouteValue = new RouteValueDictionary { "filter", filter } ;
return View(model);
}
Part of my Teams Index page.
<table class="table">
<thead>
<tr>
<td>
#Html.DisplayNameFor(model => model.Division)
</td>
<td>
#Html.DisplayNameFor(model => model.Grade)
</td>
<th>
#Html.DisplayNameFor(model => model.ClubNo)
</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.Division)
</td>
<td>
#Html.DisplayFor(modelItem => item.Grade)
</td>
<td>
#Html.DisplayFor(modelItem => item.ClubNo)
</td>
Since there is no navigation property between Club and Teams, you need to query the ClubName based on ClubNo. And since there is no ClubName in Teams, you need to define a new model which contains Clubname.
TeamsVM.cs
public partial class TeamsVM
{
public string Division { get; set; }
public string Grade { get; set; }
public string ClubName { get; set; }
}
And change query like below:
public async Task<IActionResult> Index1(string filter, string filter1, string filter2, int page = 1, string sortExpression = "Division")
{
var qry = _applicationDbContext.Teams.AsNoTracking().AsQueryable();
if (!string.IsNullOrWhiteSpace(filter))
{ qry = qry.Where(p => p.Division.Contains(filter)); }
var result = qry.Select(q => new TeamsVM
{
Division = q.Division,
Grade = q.Grade,
ClubName = _applicationDbContext.Club.FirstOrDefault(c => c.ClubNo == q.ClubNo).ClubName
});
var model = await PagingList.CreateAsync(result, 15, page, sortExpression, "Division");
model.RouteValue = new RouteValueDictionary { "filter", filter };
return Ok(result);
}
These 2 documents were very helpful.
https://learn.microsoft.com/en-us/ef/core/modeling/relationships
https://learn.microsoft.com/en-us/aspnet/core/data/ef-rp/read-related-data?view=aspnetcore-2.2&tabs=visual-studio
This is intended as a sample upon which to build other requirements of Related Data.
Part of my Models for Club.cs , Ground.cs and Teams.cs
public partial class Club
{
public Club()
public short ClubNo { get; set; }
public string ClubName { get; set; }
public string PhoneNo { get; set; }
}
public partial class Ground
{
public Ground()
public short GroundNo { get; set; }
public string GroundName { get; set; }
public string Address { get; set; }
}
public partial class Teams
{
public Teams()
public string Division { get; set; }
public string Grade { get; set; }
public short HomeGround { get; set; }
public short ClubNo { get; set; }
}
Part of my TeamsController.cs to display my Index page.
public class TeamsController : Controller
{
private readonly SSFA_SQLContext _context;
public TeamsController(SSFA_SQLContext context)
{
_context = context;
}
// GET: Teams
public async Task<IActionResult> Index(string filter, int page = 1, string sortExpression = "Division")
{
var qry = _context.Teams
.AsNoTracking()
.AsQueryable();
if (!string.IsNullOrWhiteSpace(filter))
{ qry = qry.Where(p => p.Division.Contains(filter)); }
var model = await PagingList.CreateAsync(qry, 15, page, sortExpression, "Division");
model.RouteValue = new RouteValueDictionary { "filter", filter } ;
return View(model);
}
Object is to display ClubName and GroundName on Teams Index page.
Solution: (Most probably one of many possible ways to do it).
Add following to Teams context.cs file: (No change is required to Club or Ground context files)
entity.HasOne(q => q.ClubNavigation);
entity.HasOne(q => q.GroundNavigation);
Add following to Teams.cs model file: (No change is required to Club or Ground model files)
public Club ClubNavigation { get; set; }
public Ground GroundNavigation { get; set; }
Add [ForeignKey("GroundNavigation")] above
public short HomeGround { get; set; }
(This one is required as HomeGround has to find GroundNo)
Add [ForeignKey("ClubNavigation")] above
public short ClubNo { get; set; }
As ClubNo is same in both files the second foreign key is not necessary but I think it is nice to have for consistency.
In TeamsController.cs change
var qry = _context.Teams
.AsNoTracking()
.AsQueryable();
To
var qry = _context.Teams
.Include(q => q.ClubNavigation)
.Include(q => q.GroundNavigation)
.AsNoTracking()
.AsQueryable();
Add following to Teams Index file:
<th>#Html.DisplayNameFor(model => model.HomeGround)</th>
<td><b>#Html.DisplayNameFor(model => model.GroundNavigation.GroundName)</b></td>
<th>#Html.DisplayNameFor(model => model.ClubNo)</th>
<td><b>#Html.DisplayNameFor(model => model.ClubNavigation.ClubName)</b></td>
And
<td>#Html.DisplayFor(modelItem => item.HomeGround)</td>
<td>#Html.DisplayFor(modelItem => item.GroundNavigation.GroundName)</td>
<td>#Html.DisplayFor(modelItem => item.ClubNo)</td>
<td>#Html.DisplayFor(modelItem => item.ClubNavigation.ClubName)</td>
That's it.
having trouble with ajax i am using vs 2015 mvc 5.
following are the two model classes which have 1 to many relation.
i am facing some problem.
the view doesn't produce expected links i.e. instead
~/Global/Cities/Details/1 it result ~/Global/Cities/Details/ only.
when i fix that manually in browser it through httperror 400.0 Bade Request
when i remove Url property from ajax options the partial view opens in new tab window
namespace WebApplication1.Areas.Global.Models
{
public class Organization
{
public virtual int OrganizationId { get; set; }
public virtual int CityId { get; set; }
public virtual string Name { get; set; }
public virtual City city { get; set; }
}
public class City
{
public virtual int CityId { get; set; }
public virtual string Name { get; set; }
public virtual ICollection<Organization> Organization { get;set; }
}
}
and this is the partial view controller..
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
City city = db.Cities.Find(id);
if (city == null)
{
return HttpNotFound();
}
return PartialView(city);
}
<tr>
<td>
#Html.DisplayFor(modelItem => item.Name)
</td>
<td>
#Html.DropDownList("Organizations",new SelectList(item.Organization, "OrganizationId","Name"))
</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.CityId }) |
#Ajax.ActionLink("Details", "Details",item.CityId,
new AjaxOptions
{
UpdateTargetId = "detail",
Url = Url.Action("Details",item.CityId)
}, new { id=item.CityId }) |
#Html.ActionLink("Delete", "Delete", new { id=item.CityId })
</td>
</tr>
i have also render jqueryval bundle in layout page...
is there any problem with my code please help....
I'm creating a page that will display 6 blogs that were created in the passed 7 days. The blog contains an image and multiple comments
Here is the Blog model
public class Blog
{
public int BlogID { get; set; }
public int likes { get; set; }
public int ImageID { get; set; }
public Boolean removed { get; set; }
public DateTime dte_created { get; set; }
public DateTime? dte_modified { get; set; }
public virtual Image Image { get; set; }
public virtual ICollection<Comment> Comment { get; set; }
}
And here's the blogContent.
public class Image
{
public int ImageID { get; set; }
public string img_path { get; set; }
public string Description { get; set; }
public DateTime dte_created { get; set; }
}
public class Comment
{
public int CommentID { get; set; }
public string Commentation { get; set; }
public int likes { get; set; }
public int BlogID { get; set; }
public DateTime dte_created { get; set; }
public DateTime? dte_modified { get; set; }
public Boolean removed { get; set; }
//public virtual int Account_ID { get; set; }
public virtual Blog Blog { get; set; }
}
Here is the controller
private ACaptureDB db = new ACaptureDB();
public ViewResult Index()
{
ViewBag.Message = "ArchiCapture";
var dateCheck = DateTime.Now.AddDays(-7);
var results = from r in db.Blog
where r.dte_created >= dateCheck
select r;
return View(results);
}
and my View.
#model IEnumerable<ACapture.Models.Blog>
#{
ViewBag.Title = "Home Page";
}
<div class="Forum">
<p>The Forum</p>
<form class="Forum" runat="server">
#foreach (var item in Model)
{
<div class="ForumChild"><img src="#item.Image.img_path" alt="Not Found" />
<br />
<table>
?????????
</table>
</div>
}
</form>
</div>
How would I retrieve all the comments that are linked to the blog?
You should be able to loop over the Comments collection in your model blog object:
<table>
#foreach(var comment in Model.Comments) {
<tr>
<td>#comment.Commentation</td>
....
</tr>
}
</table>
I am trying to display two values in a entity by using two foreign keys.
I have three tables; one of table is Product table.
Two tables are Category and Model for displaying these value 'name', and 'modelName'.
When I use LINQ, I was using this coding before adding the Model entity.
var product = from a in db.Product.Include(a => a.Category)
select a;
How can I add Model entity in here?
such as
var product = from a in db.Product.Include(a => a.Category, a => a.Model)
select a;
Is it possible to write?
Here is my models.
--Prodruct.cs--
public class Product
{
[Key] public int productId { get; set; }
[Required(ErrorMessage = "Please select category")]
public int categoryId { get; set; }
[Required(ErrorMessage = "Please select model")]
public int modelId { get; set; }
[DisplayName("Model name")]
public String model { get; set; }
public virtual Category Category { get; set; }
public virtual Model Model { get; set; }
}
--Category.cs--
public class Category
{
[Key] public int categoryId { get; set; }
public String name { get; set; }
}
--Model.cs--
public class Model
{
[Key] public int modelId { get; set; }
public String name { get; set; }
}
--RentalDB.cs--
public class rentalDB : DbContext
{
public DbSet<Product> Product { get; set; }
public DbSet<Model> Model { get; set; }
public DbSet<Customer> Customer { get; set; }
public DbSet<Order> Order { get; set; }
public DbSet<Cart> Cart { get; set; }
public DbSet<Category> Category { get; set; }
public DbSet<OrderDetails> OrderDetails { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
Please let me know how to put inner join(?) in LINQ.
Thank you.
I think you might want the following since Include returns IQueryable:
var product = from a in db.Product.Include(a => a.Category).Include(a => a.Model)
select a;
Is this what you need in your ProductController.cs?...
public ViewResult index(int param_categoryId, int param_modelId)
{
List<Product> locvar_CollectionOfProduct
= getCollectionOfProduct(param_categoryId, param_modelId);
return View("index", locvar_CollectionOfProduct);
}
private List<Product> getCollectionOfProduct(int param_categoryId, int param_modelId)
{
return db.Product.Where(a => a.categoryId == param_categoryId && a.modelId == param_modelId).ToList();
}
public void Product_Save(List<Product> param_CollectionOfProduct)
{
if (Model.IsValid)
{
foreach (Product i_Product in param_CollectionOfProduct)
{
Product locvar_Product = null;
if (i_Product.productId == null || i_Product.productId == 0)
{
locvar_Product = new Product();
}
else
{
locvar_Product = new Product{productId = i_Product.productId};
db.Product.Attach(locvar_Product)
}
locvar_Product.categoryId = i_Product.categoryId;
locvar_Product.modelId = i_Product.modelId;
if (i_Product.productId == null || i_Product.productId == 0)
{
db.Product.Add(locvar_Product);
}
}
db.SaveChanges();
}
}
and then in your "Views\Product\index.cshtml" view you can iterate through these. I'll put them in a table for you:
#using insert_entity_reference_here.Models;
#model List<Product>
#{
List<Product> param_CollectionOfProduct = Model;
}
#using (Ajax.BeginForm("Product_Save", "Product", null, new AjaxOptions { HttpMethod = "POST" }))
{
<table style="width:100%">
<tr>
<th>
Category Name
</th>
<th>
Model Name
</th>
</tr>
#if(Model.Count() > 0)
{
for( i_Product = 0 ; i_Product < Model.Count() ; i_Product++ )
{
#Html.HiddenFor(modelItem => param_CollectionOfProduct[i_Product].productId)
<tr>
<td>
#Html.HiddenFor(modelItem => param_CollectionOfProduct[i_Product].Category.categoryId)
#Html.EditorFor(modelItem => param_CollectionOfProduct[i_Product].Category.Name, new { style="width:100%" })
#Html.ValidationMessageFor(modelItem => param_CollectionOfProduct[i_Product].Category.Name)
</td>
<td>
#Html.HiddenFor(modelItem => param_CollectionOfProduct[i_Product].Model.modelId)
#Html.EditorFor(modelItem => param_CollectionOfProduct[i_Product].Model.Name, new { style="width:100%" })
#Html.ValidationMessageFor(modelItem => param_CollectionOfProduct[i_Product].Model.Name)
</td>
</tr>
}
}
</table>
<input type="submit">Save</input>
}
Let me know if I'm on the right track. If so, I should be able to help you some more.
Best Regards,
Nick
I am trying to use dropdownList with two foreign keys which are modelId, and categoryId.
And I am using ViewBag with selectList.
public ActionResult Create()
{
ViewBag.categoryId = new SelectList(db.Category, "categoryId", "name");
ViewBag.modelId = new SelectList(db.Model, "modelId", "name");
return View();
}
//
// POST: /Product/Create
[HttpPost]
public ActionResult Create(Product product)
{
if (ModelState.IsValid)
{
db.Product.Add(product);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.categoryId = new SelectList(db.Category, "categoryId", "name", product.categoryId);
ViewBag.modelId = new SelectList(db.Model, "modelId", "name", product.modelId);
return View(product);
}
And here is my Create.cshtml.
<div class="editor-label">
#Html.LabelFor(model => model.Category)
</div>
<div class="editor-field">
#Html.DropDownList("categoryId", "--Select--")
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Model)
</div>
<div class="editor-field">
#Html.DropDownList("modelId", "--Select--")
</div>
When I press submit button, error come up,
'An item with the same key has already been added'
What is problem? Is it problem with in Model?
Here is my models.
--Prodruct.cs--
public class Product
{
[Key] public int productId { get; set; }
[Required(ErrorMessage = "Please select category")]
public int categoryId { get; set; }
[Required(ErrorMessage = "Please select model")]
public int modelId { get; set; }
[DisplayName("Model name")]
public String model { get; set; }
public virtual Category Category { get; set; }
public virtual Model Model { get; set; }
}
--Category.cs--
public class Category
{
[Key] public int categoryId { get; set; }
public String name { get; set; }
}
--Model.cs--
public class Model
{
[Key] public int modelId { get; set; }
public String name { get; set; }
}
--RentalDB.cs--
public class rentalDB : DbContext
{
public DbSet<Product> Product { get; set; }
public DbSet<Model> Model { get; set; }
public DbSet<Customer> Customer { get; set; }
public DbSet<Order> Order { get; set; }
public DbSet<Cart> Cart { get; set; }
public DbSet<Category> Category { get; set; }
public DbSet<OrderDetails> OrderDetails { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
Where it is wrong? Index page in Create can get category data and model data. However, when I submit it, it has error, 'An item with the same key has already been added'.
Could you help me where has got problem?
Thank you.
--added more coding--
I am using this LINQ. Probably here has problem.
How can I add 'Model' entity in here?
var product = from a in db.Product.Include(a => a.Category)
select a;
This is how I would have done it..
I would suggest that you don't send your domain models to the view, but rather create a view model for each view. Doing it this way you will only include what is needed on the screen.
Create a new view model for your Create view:
public class ProductCreateViewModel
{
// Include other properties if needed, these are just for demo purposes
public string Name { get; set; }
public string SKU { get; set; }
public string LongDescription { get; set; }
// This is the unique identifier of your category,
// i.e. foreign key in your product table
public int CategoryId { get; set; }
// This is a list of all your categories populated from your category table
public IEnumerable<Category> Categories { get; set; }
// This is the unique identifier of your model,
// i.e. foreign key in your product table
public int ModelId { get; set; }
// This is a list of all your models populated from your model table
public IEnumerable<Model> Models { get; set; }
}
Category class:
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
}
Model class:
public class Model
{
public int Id { get; set; }
public string Name { get; set; }
}
In your Create view you would have the following:
#model MyProject.ViewModels.ProductCreateViewModel
#using (Html.BeginForm())
{
<table>
<tr>
<td><b>Category:</b></td>
<td>
#Html.DropDownListFor(x => x.CategoryId,
new SelectList(Model.Categories, "Id", "Name", Model.CategoryId),
"-- Select --"
)
#Html.ValidationMessageFor(x => x.CategoryId)
</td>
</tr>
<tr>
<td><b>Model:</b></td>
<td>
#Html.DropDownListFor(x => x.ModelId,
new SelectList(Model.Models, "Id", "Name", Model.ModelId),
"-- Select --"
)
#Html.ValidationMessageFor(x => x.ModelId)
</td>
</tr>
</table>
<!-- Add other HTML controls if required and your submit button -->
}
Your Create action methods:
public ActionResult Create()
{
ProductCreateViewModel viewModel = new ProductCreateViewModel
{
// Here you do database calls to populate your dropdowns
Categories = categoryService.GetAllCategories(),
Models = modelService.GetAllModels()
};
return View(viewModel);
}
[HttpPost]
public ActionResult Create(ProductCreateViewModel viewModel)
{
// Check that viewModel is not null
if (!ModelState.IsValid)
{
viewModel.Categories = categoryService.GetAllCategories();
viewModel.Models = modelService.GetAllModels();
return View(viewModel);
}
// Mapping
Product product = ... // Do your mapping here
// Insert product in database
productService.Insert(product);
// Return the view where you need to be
}
I would also recommend that you use AutoMapper to do the mappings for you between your domain model and view model. I would also recommend that you look at Fluent Validation to take care of your view model validations.
I hope this helps.
UPDATED ANSWER
The service that was used to get all the categories could look like this:
public class CategoryService : ICategoryService
{
private readonly ICategoryRepository categoryRepository;
public CategoryService(ICategoryRepository categoryRepository)
{
// Check if category repository is not null, throw exception if it is
this.categoryRepository = categoryRepository;
}
public IEnumerable<Category> GetAllCategories()
{
return categoryRepository.GetAllCategories();
}
}
categoryRepository is injected by Autofac.
Category service interface:
public interface ICategoryService
{
IEnumerable<Category> GetAllCategories();
}
I currently still use Entity Framework 4.1 code first.
My category repository:
public class CategoryRepository : ICategoryRepository
{
MyContext db = new MyContext();
public IEnumerable<Category> GetAllCategories()
{
return db.Categories
.OrderBy(x => x.Name);
}
}
My category repository interface:
public interface ICategoryRepository
{
IEnumerable<Category> GetAllCategories()
}
public class Test
{
rentalDB db = new rentalDB();
public Product LoadProductById(int pId)
{
return db.Products.Include(p => p.Model).Include(p => p.Category).Where(p => p.productId == pId).SingleOrDefault();
} // To get specific product.
public IEnumerable<Product> LoadAllProducts()
{
return db.Products.Include(p => p.Model).Include(p => p.Category).ToList();
} // To get all products.
}
I have changed your DbSet to Products make it more clear. This is how you load all the references for one product or all products, in order to iterate over them.