In my controller i am creating a subcategory object and giving that object a reference to the category it belongs to. Everything works fine when i debug the site but when i load the list of objects from my entityframework db all the category object references are removed. the rest persists. anybody got an idea why this happens ?
Controller:
[Authorize(Roles = "administrator")]
[HttpPost]
public ActionResult Create(CategoryViewModel viewmodel, HttpPostedFileBase Icon)
{
SubCategory subcategory = new SubCategory();
Category category = categorycontroller.getCategoryByName(viewmodel.SelectedValue);
viewmodel.subcategory.Category = category;
subcategory = viewmodel.subcategory;
category.Subcategories.Add(subcategory);
if (Icon != null && Icon.ContentLength > 0)
{
var fileName = Path.GetFileName(Icon.FileName);
var path = Path.Combine(Server.MapPath("../../Content/icons/"), fileName);
Icon.SaveAs(path);
subcategory.Icon = fileName;
}
if (ModelState.IsValid)
{
subcategorydb.categories.Attach(category);
subcategorydb.subcategories.Add(subcategory);
subcategorydb.SaveChanges();
return RedirectToAction("Index");
}
return View(subcategory);
}
and this is my viewmodel:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Entity;
using System.Web.Mvc;
using SkyLearn.Areas.Categories.Controllers;
namespace SkyLearn.Areas.Categories.Models
{
public class CategoryViewModel
{
public List<SelectListItem> PossibleValues { get; set; }
public string SelectedValue { get; set; }
public SubCategory subcategory { get; set; }
public CategoryController categorycontroller;
public CategoryViewModel()
{
PossibleValues = new List<SelectListItem>();
}
}
}
And here is my Category and SubCategory class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Entity;
namespace SkyLearn.Areas.Categories.Models
{
public class Category
{
public int ID { get; set; }
public string Title { get; set; }
public string Icon { get; set; }
public string Description { get; set; }
public List<SubCategory> Subcategories;
public Category()
{
Subcategories = new List<SubCategory>();
}
}
public class CategoryDBContext : DbContext
{
public DbSet<Category> categories { get; set; }
public DbSet<SubCategory> subcategories { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Entity;
using SkyLearn.Areas.Categories.Controllers;
namespace SkyLearn.Areas.Categories.Models
{
public class SubCategory
{
public int ID { get; set; }
public string Title { get; set; }
public string Icon { get; set; }
public string Description { get; set; }
public int CategoryID { get; set; }
public Category Category { get; set; }
}
public class SubCategoryDBContext : DbContext
{
public DbSet<SubCategory> subcategories { get; set; }
public DbSet<Category> categories { get; set; }
}
}
i have searched many sites for info on this problem but couldnt find much of help. Anybody here able to identify my problem?
as seen on this screenshot the object is correctly referenced by all means. but when i load the data next time the object reference has disapeared. all other attributes are still there and saved as they should:
thanks AronChan
//// Update ////
I am now getting the following error:
A foreign key value cannot be inserted because a corresponding primary
key value does not exist. [ Foreign key constraint name =
SubCategory_Category ]
Your category object won't get saved because it is not attached to the DbContext that you add the subcategory to. The subcategory might know about the reference but the category won't. If there is a lookup table (which there must be if its many to many) I don't see how that record would be saved. Try attaching the category, adding the subcategory and then saving. Personally I have always set up the many-to-many look up tables as their own entity. It is a little easier to visualize what is actually happening as opposed to giving all control to EF.
Also, if you are using code first, make sure your EntityConfigurations are correct. Try running the EdmxWriter over your context to make sure the data model looks exactly how you want it to.
Related
Good morning everyone, I'm learning to use asp.net core in version 6 and I'm having problems saving a record to the database it shows the following error:
SqlException: Invalid object name 'VehiclePerson'.
From what I know, it is actually because it does not find the table in "VehiclePerson" although the connection is made, so I used Scaffolding to obtain the DB from Microsoft SSMS and I created the models and the Context file, so I don't think there are problems with the connection.
This is my VehiclePerson.cs file
using System.Collections.Generic;
namespace APITDCON.Models.QQGUAR01;
public partial class VehiclePerson
{
public string? EmpNumber { get; set; }
public string? Lastname { get; set; }
public string? Firstname { get; set; }
public string? Costcenter { get; set; }
public string? Department { get; set; }
public string? CredentNumber { get; set; }
public string? FromDate { get; set; }
public string? ToDate { get; set; }
public string? Rights { get; set; }
public int Id { get; set; }
}
This is my VehiclePerson.cs file
using Microsoft.EntityFrameworkCore;
using APITDCON.Models.QQGUAR01;
using Microsoft.IdentityModel.Tokens;
using System.Reflection;
using System;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
namespace APITDCON.Data
{
public class APITDCONContext : DbContext
{
public APITDCONContext(DbContextOptions<APITDCONContext> options) : base(options)
{
}
public DbSet<VehiclePerson> VehiclePerson { get; set; }
}
}
This is my VehiclePerson.cs file
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using APITDCON.Models.TANKFARM;
using APITDCON.Models;
using APITDCON.Data;
using System.Collections;
namespace APITDCON.Controllers
{
public class VehiclePersonController1 : Controller
{
private readonly APITDCONContext _apiTDCONContext;
public VehiclePersonController1(APITDCONContext apiTDCONContext)
{
_apiTDCONContext = apiTDCONContext;
}
[HttpGet]
public async Task<IActionResult> Index()
{
List<VehiclePerson> lista = new List<VehiclePerson>();
lista = GetAllDataDrivers();
return View(lista);
}
[HttpGet]
public IActionResult Add()
{
return View();
}
[HttpPost]
public IActionResult Add(VehiclePerson vehiclePersonRequest)
{
string mensaje = "";
var vehicleperson = new VehiclePerson()
{
EmpNumber = vehiclePersonRequest.EmpNumber,
Lastname = vehiclePersonRequest.Lastname,
Firstname = vehiclePersonRequest.Firstname,
Costcenter = vehiclePersonRequest.Costcenter,
Department = vehiclePersonRequest.Department,
CredentNumber = vehiclePersonRequest.CredentNumber,
FromDate = vehiclePersonRequest.FromDate,
ToDate = vehiclePersonRequest.ToDate,
Rights = vehiclePersonRequest.Rights
};
using (var db = new Models.QQGUAR01.Qqguar01Context())
{
var result = new List<VehiclePerson>();
result = (from data in db.VehiclePerson
select new VehiclePerson
{
EmpNumber = data.EmpNumber,
Lastname = data.Lastname,
Firstname = data.Firstname
}).ToList();
}
_apiTDCONContext.VehiclePerson.Add(vehicleperson);
_apiTDCONContext.SaveChanges();
return RedirectToAction("Add");
}
}
}
Database and table
Error
The table name defined in your database context is VehiclePerson, but your table name in the database is VEHICLE_PERSON, the two names do not match.
The easiest way is to change the name of the table VEHICLE_PERSON in the database to VehiclePerson, or change public DbSet<VehiclePerson> VehiclePerson { get; set; } to public DbSet<VehiclePerson> VEHICLE_PERSON { get; set; } in your context.
If you want to avoid similar situations in the future, you can use the following commands to migrate and update the database after adding Scaffolding:
Add-Migration InitialCreate
Update-Database
For more details on scaffolding and connecting to the database, you can refer to this official document.
By default EF will use entity name as table name so you can override it in multiple ways, for example using Table attribute:
[Table("VEHICLE_PERSON")]
public partial class VehiclePerson
{
}
Perhaps I am misunderstanding how a ViewModel works. A ViewModel is used to display custom data to the user without having to use the full Model. So you would use only the database fields that you want displayed. I have a ViewModel that looks like this
using PagedList;
using Resolutions.Models;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Web;
namespace Resolutions.ViewModels
{
public class SearchPagingViewModels
{
public int ObjectID { get; set; } //Database Primary Key
[DisplayName("Resolution Year")]
public string ResolutionYear { get; set; } //Year of the Resolution
[DisplayName("Resolution Number")]
public string ResolutionNumber { get; set; } //Number of the Resolution
[DisplayName("Resolution Text")]
public string ResolutionText { get; set; } //OCR'd text for the Resolution
[DisplayName("Resolution Date")]
public Nullable<System.DateTime> ResolutionDate { get; set; } //Date of the Resolution
public int? PageNumber { get; set; }
public bool? IsResYearChecked { get; set; }
public bool? IsResNumChecked { get; set; }
public bool? IsKeywordChecked { get; set; }
public string SearchKeyword { get; set; }
}
}
I want to store the value of a checkbox from the client side into my object and use the boolean to be able to filter and page through the results. However, if I use my current ViewModel I get 25 checkboxes when I try to access the object's boolean; As I can only seem to access the value of the checkbox within a foreach loop that I am using to display each item. I am currently pulling information from a database and then storing the database results into a list of the ViewModel.
This is my controller:
using PagedList;
using Resolutions.Models;
using Resolutions.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace Resolutions.Controllers
{
public class SearchPagingController : Controller
{
private DocumentManagementEntities db = new DocumentManagementEntities();
// GET: SearchPaging
public ViewResult Index(int? page, bool? resnum)
{
var temp = resnum;
int pageSize = 25;
int pageNumber = (page ?? 1);
List<SearchPagingViewModels> searchList = new List<SearchPagingViewModels>();
var allResolutions = from c in db.AllResolutions select c;
allResolutions = allResolutions.OrderBy(c => c.ResolutionYear).ThenBy(c => c.ResolutionNumber);
foreach (var item in allResolutions)
{
SearchPagingViewModels searchItem = new SearchPagingViewModels();
searchItem.ObjectID = item.ObjectID;
searchItem.ResolutionYear = item.ResolutionYear;
searchItem.ResolutionNumber = item.ResolutionNumber;
searchItem.ResolutionText = item.ResolutionText;
searchItem.ResolutionDate = item.ResDate;
searchItem.IsResNumChecked = resnum;
searchList.Add(searchItem);
}
return View(searchList.ToPagedList(pageNumber, pageSize));
}
}
}
As you can see I create a List<SearchPagingViewModels> and then add each SearchPagingViewModels into the List<SearchPagingViewModels>. In my Index.cshtml, I use a foreach loop to traverse the list and display each item and create a PagedList. Should I be changing my ViewModel to store a List<AllResolutions> like so:
using PagedList;
using Resolutions.Models;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Web;
namespace Resolutions.ViewModels
{
public class SearchPagingViewModels
{
public List<AllResolution> AllResolutions { get; set; }
public int? PageNumber { get; set; }
public bool? IsResYearChecked { get; set; }
public bool? IsResNumChecked { get; set; }
public bool? IsKeywordChecked { get; set; }
public string SearchKeyword { get; set; }
}
}
or do I want to use the current values within the ViewModel? If instead I used a List<AllResolutions>, how do you access the values within the list in the cshtml file?
I start studying asp.net MVC and I am confused about sending data to VIEW. I have one class Dispatch:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace Logistic.Models
{
public class Dispatch
{
public int DispatchId { get; set; }
public int TrackingId { get; set; }
public int CustomerId { get; set; }
public int RecipientId { get; set; }
public int CityId { get; set; }
public DateTime Delivered { get; set; }
public string Notes { get; set; }
}
}
Another class City:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace Logistic.Models
{
public class City
{
public int CityId { get; set; }
public string Name { get; set; }
}
}
A class Client:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace Logistic.Models
{
public class Client
{
public int ClientId { get; set; }
public string Name { get; set; }
}
}
From my class Dispatch, CustomerId and RecipientId will be related to the class Client; CityId related to City.
I create my context class:
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;
using System.Linq;
using System.Web;
namespace Logistic.Models
{
public class LogisticContext : DbContext
{
public DbSet<Dispatch> Dispatches { get; set; }
public DbSet<Client> Clients { get; set; }
public DbSet<Driver> Drivers { get; set; }
public DbSet<City> Cities { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
}
And here is my Controller:
using Logistic.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace Logistic.Controllers
{
public class HomeController : Controller
{
private LogisticContext db = new LogisticContext();
public ActionResult Dispatch()
{
var dispatches = db.Dispatches.ToList();
return View(dispatches);
}
}
}
The problem is that in my View class I do not know how to display the Customer Name, Recipient Name, and City Name. I only can display the CustomerId, RecipientId, and CityId.
View:
#model IEnumerable<Logistic.Models.Dispatch>
#{
ViewBag.Title = "Dispatch";
}
<h2>Dispatch</h2>
#foreach(var item in Model)
{
<h2></h2>
}
Thanks
If you have relationships in place...
If you already have defined relationships between these properties, you can generally use the Include() method to pull related entities in as seen below :
// Pull all of the dispatches and their related cities
var dispatchesWithCities = db.Dispatches.Include("Cities")
.ToList();
If you do this and have the proper relationships, you would then be able to iterate through and access the item.City.Name, however it doesn't look like that is the case in your current scenario.
Without relationships...
If you don't have these "hard" relationships in place, you can still perform a few database queries to pull the properties you need and construct a ViewModel, but it wouldn't be pretty with so many results :
public class DispatchViewModel
{
public Dispatch Dispatch { get; set; }
public City City { get; set; }
public Client Client { get; set; }
public Driver Driver { get; set; }
}
And then use each of your individual Dispatch objects and map them to their related properties :
// Build your Dispatches with related information
var dispatchModels = db.Dispatches.AsEnumerable()
.Select(d => new DispatchViewModel()
{
Dispatch = d,
City = db.Cities.FirstOrDefault(c => c.CityId == d.CityId),
Client = db.Clients.FirstOrDefault(c => c.ClientId == d.CustomerId),
...
});
At this point you can simply iterate through the collection and output them by referencing the iterator in the loop :
#foreach(var item in Model)
{
<h2>#item.Dispatch.DispatchId</h2>
<ul>
<li>
<b>City</b>: #item.City.Name
<!-- Other stuff here -->
</li>
</ul>
}
You can use view model approach to send data from mutliple views to controller. Here is an example:
http://rachelappel.com/use-viewmodels-to-manage-data-amp-organize-code-in-asp-net-mvc-applications/
I have a DBContext "StatusDBContext", a Model "Status_T" with some(n) properties and a corresponding ViewModel "Status" with few of the Model properties. I am using Entity Framework with code first from database.
DBContext:
namespace xxx.Areas.SysParam.Models
{
using System;
using System.Data.Entity;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
public partial class StatusDbContext : DbContext
{
public StatusDbContext()
: base("name=xxxConnectionString")
{
}
public virtual DbSet<STATUS_T> STATUS_T { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<STATUS_T>().ToTable("STATUS_T");
}
}
}
Model:
namespace xxx.Areas.SysParam.Models
{
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.Spatial;
[Table("STATUS_T")]
public partial class STATUS_T
{
[Key]
[Column(Order = 0)]
[StringLength(35)]
public string TYPE { get; set; }
[Key]
[Column(Order = 1)]
public byte STATUS { get; set; }
[StringLength(30)]
public string DESCRIPTION { get; set; }
[Key]
[Column(Order = 2)]
[StringLength(2)]
public string LANG_CODE { get; set; }
public DateTime UPD_DTIME { get; set; }
public DateTime? DELETE_DTIME { get; set; }
public short? VER_NO { get; set; }
[StringLength(1)]
public string STAT_USE { get; set; }
}
}
View Model:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using xxx.Areas.SysParam.Models;
namespace xxx.Areas.SysParam.ViewModels
{
public class Status
{
[Display(Name = "Status ID")]
public byte STATUS { get; set; }
[Display(Name = "Description")]
public string DESCRIPTION { get; set; }
[Display(Name = "Type")]
public string TYPE { get; set; }
[Display(Name = "In Use")]
public string STAT_USE { get; set; }
}
}
Before introducing ViewModel the Controller looked like:
public ActionResult StatusView()
{
List<Models.STATUS_T> StatusList = new List<Models.STATUS_T>();
using (var status = new Models.StatusDbContext())
{
StatusList = Status.STATUS_T.ToList();
}
return View(StatusList);
}
Now I have two questions.
1) As I googled about Model, ViewModel and DataAnnotations its somewhat clear that DataAnnotations should be added to ViewModel but in Code First from Existing DB the Models are generated with few DataAnnotations so what will be the best practice here, to copy properties from Model to ViewModel and add more DataAnnotations whenever required?
2) How to replace Model with ViewModel in Controller code?
Help will be appreciated. Thanks!!!
Updated: Controller:
public ActionResult DWPStatusView()
{
var DWPStatusList = new List<ViewModels.DWPStatus>();
using (var DWPStatus = new Models.DWPStatusDbContext())
{
DWPStatusList = DWPStatus.DWP_STATUS_T.ToList().Select(p => new ViewModels.DWPStatus(p)).ToList();
}
return View(DWPStatusList);
}
1.Yes, best practise is to create separate view model class and copy there necessary properties from DB model, because:
may happen that DB model have unwanted properties for view
view model may need additional properties and adding them to DB model
is not good practise
View model usually have validation data annotations attributes, so better keep them also separate from DB annotations.
2.
public ActionResult StatusView()
{
var statusList = new List<ViewModels.Status>();
using (var status = new Models.StatusDbContext())
{
statusList = Status.STATUS_T.ToList().Select(p => new ViewModels.Status {
Property1 = p.Property1,
Property2 = p.Property2,
...
}).ToList();
}
return View(statusList);
}
You can place property mapping into view model constructor, then selecting data will look next:
statusList = Status.STATUS_T.ToList().Select(p => new ViewModels.Status(p)).ToList();
Update: and view model:
public class Status
{
public Status(){}
public Status(Models.STATUS_T model)
{
//here comes mapping
}
....
}
Following is a code written by me for searching a specific item by name in the Advertisement table.
public ActionResult SearchResult(string name)
{
var advertisement = db.Advertisements.ToArray(); // retrieve data from database
foreach (var ad in advertisement)
{
if (ad.Title.Equals(name))
{
return View(ad);
}
}
return View(advertisement);
}
Even though I search an item which is already in the database, in all cases the if condition is not being true.Each time I get the whole list of items as the result in the view page. what is the issue here?
My model for Advertisement looks like this.
using System;
using System.Drawing; // Image type is in this namespace
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
namespace Bartering.Models
{
public class Advertisement
{
[Key]
public int ID { get; set; }
[Required]
[StringLength(100)]
public string Title { get; set; }
public Guid OwnerID { get; set; }
[Required]
public string Category { get; set; }
public byte[] Image { get; set; }
[Required]
[StringLength(200)]
public string Description { get; set; }
}
}
I think you should be doing something like this
public ActionResult SearchResult(string name)
{
var ad=db.Advertisements.Where(s=>s.Title.ToUpper()==name.ToUpper())
.FirstOrDefault();
if(ad!=null)
return View(ad);
//Nothing found for search for the name, Let's return the "NotFound" view
return View("NotFound");
}
This code will get the first item(if exists) which matches with our check (Title==name) and return it. if there is nothing found which matches the condition, It will return a View called "Notfound"