Sorry for the very long post. I have these table relationships:
- Room has many-to-many relationship with Activity
- RoomActivity has one-to-many relationship with Room and Activity
- Item has many-to-many relationship with Part
- ItemPart has one-to-many relationship with Item and Part
- Stage has foreign key relationship to RoomActivity and ItemPart
- Submission has a foreign key relationship to Stage
I have a web application where employees can submit what they do today, which consist of Room, Activity, Item, Part then submit the form.
My Controller:
public ActionResult Create()
{
ViewBag.ActivityRejectCodeId = GetRejectCodesByActivity(0);
ViewBag.Activities = GetActivities();
ViewBag.Workstations = GetRooms();
ViewBag.Platforms = GetItems();
ViewBag.Parts = GetParts();
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(WorkOrderSubmission workordersubmission)
{
if (ModelState.IsValid)
{
var activityId = Int32.Parse(Request.Form["Stage.RoomActivity.Activity.Id"]);
var workstationId = Int32.Parse(Request.Form["Stage.RoomActivity.Workstation.Id"]);
var platformId = Int32.Parse(Request.Form["Stage.ItemPart.Platform.Id"]);
var partId = Int32.Parse(Request.Form["Stage.ItemPart.Part.Id"]);
var rs = (from ps in db.Stages
join wa in db.RoomActivities on ps.RoomActivityId equals wa.Id
join pp in db.ItemParts on ps.ItemPartId equals pp.Id
where ps.RoomActivity.ActivityId == activityId
&& ps.RoomActivity.RoomId == workstationId
&& ps.ItemPart.ItemId == platformId
&& ps.ItemPart.ItemId == partId
select new { ps.Id }).FirstOrDefault();
var stageId = rs.Id;
workordersubmission.StageId = stageId;
workordersubmission.SubmissionDate = DateTime.Now;
// Error when saving here
db.WorkOrderSubmissions.Add(workordersubmission);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.stageId = new SelectList(db.Stages.OrderBy(p => p.Name), "Id", "Name", workordersubmission.StageId);
return View(workordersubmission);
}
public SelectList GetActivities()
{
var results = (from ps in db.Stages
join wa in db.RoomActivities on ps.RoomActivityId equals wa.Id
join a in db.Activities on wa.ActivityId equals a.Id
select new
{
Id = wa.ActivityId,
Name = a.Name
})
.Distinct()
.OrderBy(n => n.Name);
return new SelectList(results, "Id", "Name");
}
My View:
<div class="editor-label">
#Html.LabelFor(model => model.Stage.RoomActivity.Activity.Id, "Activity")
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.Stage.RoomActivity.Activity.Id,
(SelectList)ViewBag.Activities, "")
#Html.ValidationMessageFor(model => model.Stage.RoomActivity.Activity.Id)
</div>
I'm getting the error below(Updated):
Validation failed for entity [Part]. Validation errors:
Number: The Number field is required.
Validation failed for entity [Item]. Validation errors:
Name: The Name field is required.
Validation failed for entity [Activity]. Validation errors:
Name: The Name field is required.
Validation failed for entity [Room]. Validation errors:
Name: The Name field is required
Why do I get the error on Part, Item, Activity, Room? I'm trying to insert a new Submission.
Stage model:
public partial class Stage
{
public Stage()
{
this.WorkOrderSubmissions = new HashSet<WorkOrderSubmission>();
}
public int Id { get; set; }
public int RoomActivityId { get; set; }
public int ItemPartId { get; set; }
public string Description { get; set; }
public string Name { get; set; }
public virtual ItemPart ItemPart { get; set; }
public virtual RoomActivity RoomActivity { get; set; }
public virtual ICollection<WorkOrderSubmission> WorkOrderSubmissions { get; set; }
}
WorkOrderSubmission model:
public partial class WorkOrderSubmission
{
public int Id { get; set; }
public int WorkOrderId { get; set; }
public int StageId { get; set; }
public System.DateTime SubmissionDate { get; set; }
public virtual Stage Stage { get; set; }
public virtual WorkOrder WorkOrder { get; set; }
}
Could you add this code to get more information about DbEntityValidationException and show it?
try
{
db.SaveChanges();
}
catch (DbEntityValidationException e)
{
string errorFormat = #"Validation failed for entity [{0}]. Validation errors:" + Environment.NewLine + #"{1}";
var errorList = new List<String>();
foreach (var entityValidationError in e.EntityValidationErrors)
{
var entityName = entityValidationError.Entry.Entity.GetType().Name;
var errors = String.Join(Environment.NewLine, entityValidationError.ValidationErrors.Select(a => a.PropertyName + ": " + a.ErrorMessage));
errorList.Add(String.Format(errorFormat, entityName, errors));
}
throw new Exception(String.Join(Environment.NewLine + Environment.NewLine, errorList) + Environment.NewLine, e);
}
Related
I have these two models:
public class Film
{
public long Id { get; set; }
[Required]
public string Title { get; set; }
[Required]
public string Director { get; set; }
public string Synopsis { get; set; }
public int? Release { get; set; }
[Required]
public Genre Genre { get; set; }
}
public class Genre
{
public long Id { get; set; }
public string Description { get; set; }
}
And I want to be able to update a Film's Genre through a PUT method. I am currently trying this, but I get the following error:
[HttpPut]
public async Task<IActionResult> UpdateFilm(Film film)
{
var existingFilm = await _context.Films
.Include(f => f.Genre)
.FirstOrDefaultAsync(f => f.Id == film.Id);
if (existingFilm == null)
{
return NotFound(new JsonResponse { Success = false, Message = "Impossible to update, film was not found", Data = null });
}
existingFilm.Title = film.Title;
existingFilm.Synopsis = film.Synopsis;
existingFilm.Release = film.Release;
existingFilm.Director = film.Director;
if (existingFilm.Genre.Id != film.Genre.Id)
{
existingFilm.Genre.Id = film.Genre.Id;
existingFilm.Genre.Description = film.Genre.Description;
//_context.Entry(existingFilm.Genre).State = EntityState.Modified;
_context.Entry(existingFilm).CurrentValues.SetValues(film);
}
_context.Films.Update(existingFilm);
try
{
await _context.SaveChangesAsync();
}
catch (Exception e)
{
return BadRequest(new JsonResponse { Success = false, Message = e.Message, Data = null });
}
return Ok(new JsonResponse { Success = true, Message = "Film updated with success", Data = film });
}
The error message is:
System.InvalidOperationException: The property 'Id' on entity type 'Genre' is part of a key and so cannot be modified or marked as modified. To change the principal of an existing entity with an identifying foreign key first delete the dependent and invoke 'SaveChanges' then associate the dependent with the new principal.
Anyone able to help? Thanks a lot.
According to the error, its existingFilm.Genre.Id that you cannot update the id, if its not equal to the id.
My suggestion would be ignore the id update, but if it is necessary:
if (existingFilm.Genre.Id != film.Genre.Id)
{
var genre = _context.Genre.FirstOrDefault(x => x.Id == film.Genre.Id);
// Update the fields needed except the id
existingFilm.Genre = genre;
}
I have a form which has a place where a user can insert multiple tags separated by a comma into the database. I got it to insert, but I'm having trouble retrieving it to show on my edit form.
This is my Edit Action:
public IActionResult Edit(int id)
{
var gallery = _ctx.GalleryImages.SingleOrDefault(m => m.Id == id);
if (gallery == null)
return NotFound();
var categories = _ctx.Categories.ToList();
var model = new GalleryFormViewModel(gallery)
{
Tags = gallery.Tags,
Category = categories,
};
return View("Views/Image/UploadForm.cshtml", model);
}
Here is my ViewModel:
public class GalleryFormViewModel
{
public int? Id { get; set; }
public string Title { get; set; }
public IEnumerable<ImageTag> Tags { get; set; }
public IEnumerable<Category> Category { get; set; }
[Required]
public int CategoryId { get; set; }
public IFormFile ImageUplaod { get; set; }
public GalleryFormViewModel()
{
Id = 0;
}
public GalleryFormViewModel(GalleryImage galleryImage)
{
Id = galleryImage.Id;
Title = galleryImage.Title;
Tags = galleryImage.Tags;
CategoryId = galleryImage.CategoryId;
}
}
And here is the Form input: (I'm using this form for creating and editing the gallery)
<div class="form-group">
#Html.LabelFor(m => m.Tags)
#Html.TextBoxFor(m => m.Tags, new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.Tags)
</div>
Here is the Tag Model:
namespace SimpleImageGallery.Data.Models
{
public class ImageTag
{
public int Id { get; set; }
public string Description { get; set; }
}
}
Here is the Gallery Model:
public class GalleryImage
{
public virtual IEnumerable<ImageTag> Tags { get; set; }
// ....
}
This is how the tags table looks in the database:
It seems like I'm not getting any errors, maybe something is wrong in the actual input field?
There are some mistakes :
First, you have to Include the Tags to retrieve them from DB (if using Entity Framework):
var gallery = _ctx.GalleryImages.Include(m=>m.Tags).SingleOrDefault(m => m.Id == id);
Secondly, you are doing the same this twice :
var model = new GalleryFormViewModel(gallery)
{
Tags = gallery.Tags,
Category = categories,
};
and
public GalleryFormViewModel(GalleryImage galleryImage)
{
Id = galleryImage.Id;
Title = galleryImage.Title;
Tags = galleryImage.Tags;
CategoryId = galleryImage.CategoryId;
}
Thirdly, you cannot do this : #Html.TextBoxFor(m => m.Tags, new { #class = "form-control" }) for a enumerable, you have to reconstruct the string.
I could really use some help, I have the following SQL query that works and have been trying to convert it to Entity Framework to no avail. I am using the Methods rather than the other option, I think that is how you say it.
Anyway the SQL is
SELECT c.ACCOUNTID,
c.TOACCOUNTID,
fa.ACCOUNTNAME, ta.ACCOUNTNAME,
p.PAYEENAME
FROM checking AS c
LEFT JOIN
ACCOUNT AS fa ON c.ACCOUNTID = fa.ACCOUNTID
LEFT JOIN
ACCOUNT AS ta ON c.TOACCOUNTID = ta.ACCOUNTID
LEFT JOIN
PAYEE AS p ON c.PAYEEID = p.PAYEEID
WHERE c.ACCOUNTID == 1 OR
c.TOACCOUNTID == 1;
So far I have managed to get it this far.
var checking =
db.Transactions
.Where(item => item.ACCOUNTID == LookupAccount || item.TOACCOUNTID == LookupAccount)
.GroupJoin(db.PAYEE,
transaction => transaction.PAYEEID,
payee => payee.PAYEEID,
(check, payee) => new { Payee = payee }
).SelectMany(
transaction => transaction.Payee.DefaultIfEmpty(),
(transaction, payee) => new { Payee = payee })
.Select(item => new
{
ToAccount = item.ToAccount.AccountName
FromAccount = item.FromAccount.AccountName
Withdrawal = 0,
Deposit = 0,
Payee = item.Payee.PAYEENAME
}).ToList();
The issue I have now, is that I am not sure I understand how joins work in this manner, every time I try to get the 2 other joins in I end up falling flat on my face.
When I add this to the above code, the Payee section is out of whack and I don't understand why. I know it has to do with the select new {} section, I could really use some help understanding how this works.
.Join(db.ACCOUNT,
check => check.ACCOUNTID,
account => account.ACCOUNTID,
(check, fromaccount) => new { FromAccount = fromaccount }
)
Models
Transaction
public partial class Transaction
{
[Key]
public int Id { get; set; }
public int AccountID { get; set; }
public int? ToAccountId { get; set; }
public int PayeeId { get; set; }
[Column(TypeName = "numeric")]
public decimal TransAmount { get; set; }
[Column(TypeName = "numeric")]
public decimal ToTransAmount { get; set; }
public virtual Account Account { get; set; }
}
Account
public partial class Account
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Account()
{
Transaction = new HashSet<Transaction>();
}
[Key]
public int AccountId { get; set; }
[Required]
[StringLength(150)]
public string AccountName { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Transaction> Transaction { get; set; }
}
This won't work as a comment, but it might get you further. You have 2 FK in your tx class so you need 2 navigation properties:
public partial class Transaction
{
public int Id { get; set; } // Key by convention
public int FromAccountID { get; set; }
public virtual Account FromAccount { get; set; }
public int? ToAccountId { get; set; }
public virtual Account ToAccount { get; set; }
public int PayeeId { get; set; }
public virtual Payee Payee { get; set; }
public decimal TransAmount { get; set; }
public decimal ToTransAmount { get; set; }
}
public partial class Account
{
public Account()
{
Transaction = new HashSet<Transaction>();
}
public int AccountId { get; set; } // Key by convention
[Required]
[StringLength(150)]
public string AccountName { get; set; }
[InverseProperty("FromAccount")]
public virtual ICollection<Transaction> TransactionsFrom { get; set; }
[InverseProperty("ToAccount")]
public virtual ICollection<Transaction> TransactionsTo { get; set; }
}
Now your query becomes:
var checking =
db.Transactions
.Include(tx => tx.Payee)
.Include(tx => tx.FromAccount)
.Include(tx => tx.ToAccount)
.Where(tx => tx.FromAccountId == lookupAccountId || tx.ToAccountId == lookupAccountId)
.Select(tx => new
{
ToAccountName = tx.ToAccount.AccountName
FromAccountName = tx.FromAccount.AccountName
Withdrawal = tx.ToTransAmount,
Deposit = tx.TransAmount,
PayeeName = tx.Payee.PAYEENAME
}).ToList();
https://coding.abel.nu/2012/06/dont-use-linqs-join-navigate/
I have created this as an answer, for any one else who may come across this and is learning the syntax.
The reason I wasn't getting it to work was really down to a lack of how EF actually works when joining.
Transactions
.Join(Accounts,
tr => tr.AccountId,
ac => ac.AccountId,
(tr, ac) => new { Transaction = tr, ac})
.GroupJoin(Accounts,
tr => tr.Transaction.ToAccountId,
ta => ta.AccountId,
(tr, ta) => new { Transaction = tr.Transaction, Account = ta, FromAccount = tr.ac})
.SelectMany(
transaction => transaction.Account.DefaultIfEmpty()
,(transaction, account) => new { tt = transaction.Transaction, ToAccount = account, FromAccount = transaction.FromAccount}
)
.GroupJoin(Payees,
tr => tr.tt.PayeeId,
payee => payee.PAYEEID,
(tr, payee) => new { Transaction = tr, Payee = payee })
.SelectMany(
transaction => transaction.Payee.DefaultIfEmpty(),
(transaction, payee) => new {transaction = transaction.Transaction.tt, FromAccount = transaction.Transaction.FromAccount, ToAccount = transaction.Transaction.ToAccount, Payee = payee })
.Where (x=> x.FromAccount.AccountId == 1 || x.ToAccount.AccountId == 1)
.Select(item => new
{
item.transaction.Id,
TransDate = item.transaction.TRANSDATE,
Number = item.transaction.TransactionNumber,
Payee = item.Payee.PAYEENAME,
Status = item.transaction.Status,
Category = item.transaction.CategoryId,
FromAccount = item.FromAccount.AccountName,
ToAccount = item.ToAccount.AccountName,
Withdrawal = 0,
Deposit = 0,
Notes = item.transaction.Notes
})
The parts that I was not understanding was the relationship in code, from the joins and how the Selects then took over and created a hierarchy through the objects. I really wish that I could have used Navigation here, but from my understanding of Navigation's there needs to be a relationship between the columns and nearly all the fields being joined on here, can be null and therefore a foreign key would not satisfy the requirements for a Navigation.
I am still convinced there is a better way, but for now I have placed this here for others who are still learning and wish to see a working solution.
I'm trying to get values from userList viewbag.i can't figure out the solution. Error is:
An exception of type 'Microsoft.CSharp.RuntimeBinder.RuntimeBinderException' occurred in System.Core.dll but was not handled in user code
Additional information: 'object' does not contain a definition for 'name'
though in ViewBag.userList contains data (2 objects) which i can see while debugging
#foreach (var aUser in ViewBag.userList)
{
<tr>
<td>#aUser.name</td>
<td>#aUser.username</td>
.....
<td>#Html.ActionLink("Edit", "UserEdit","Users")</td>
<td>#Html.ActionLink("Delete", "UserDelete", "Users")</td>
</tr>
}
I have a superclass and a childclass
superclass
public partial class user
{
public int id { get; set; }
public string name { get; set; }
public string username { get; set; }
...
public string user_role { get; set; }
}
childclass
public class UserSub: user
{
public string CreatedUserName { get; set; }
public string ModifiedUserName { get; set; }
}
In my controller i used linq to get values from database and stored it to Viewbag.userList. My controller function is
public ActionResult UserList()
{
IEnumerable<user> users = null;
users = dbEntities.users.ToList();
if (users != null)
{
var userLists = (from a in users join b in users on a.created_user_id equals b.id select new { a.name, a.username, a.password, a.user_role, a.is_enable, a.is_approved, CreatedUserName = b.name, a.create_time, a.is_android, a.device_id }).ToList();
ViewBag.userList = userLists;
}
return View();
}
tried List<UserSub> users=ViewBag.userList....getting error too
Use a ViewModel to share data between view and controller.
For example, first create the ViewModel:
public class userViewModel{
public int id { get; set; }
public string name { get; set; }
public string username { get; set; }
public string user_role { get; set; }
public string CreatedUserName { get; set; }
public string ModifiedUserName { get; set; }
...
}
You can put all data that you need in your view model... Then, I'll recommend you create a class in your model with all the queries that you need (you have to investigate how to do), but you can get the queries from your controller (if you want).
Well, edit your controller function:
public ActionResult UserList()
{
List<userViewModel> userVM = new List<userViewModel>(); //Important! Don't return all the query, just the data that you need.
IEnumerable<user> users = null;
users = dbEntities.users.ToList();
if (users != null)
{
var userLists = (from a in users join b in users on a.created_user_id equals b.id select new { a.name, a.username, a.password, a.user_role, a.is_enable, a.is_approved, CreatedUserName = b.name, a.create_time, a.is_android, a.device_id }).ToList(); //I'm going to suppose that your query is ok and you get all the data that you need...
foreach (var item in userLists)
{
userVM.Add(new userVM(){
userVM.name = item.name;
userVM.username = item.username;
userVM.user_role = item.user_role;
.......
});
}
}
return View(userVM); //return your view model
}
Finally, modify your view and call the ViewModel userViewModel
#model Model.ViewModel.userViewModel //It depends on the namespace
//Then try something likes this...
#foreach (var aUser in Model)
{
<tr>
<td>#aUser.name</td>
<td>#aUser.username</td>
.....
<td>#Html.ActionLink("Edit", "UserEdit","Users")</td>
<td>#Html.ActionLink("Delete", "UserDelete", "Users")</td>
</tr>
}
That's the idea, improve my answer. ;)
Can anyone please tell me, I need to MVC C# Viewmodel join data from multiple tables and use chtml page #model ViewModels.StoreBrowseViewModel. But my logic will retrieve only one table data.
This is my class diagram. red box primary key, blue box foreign key
This is my StoreBrowseViewModel class
public class StoreBrowseViewModel
{
public int Id { get; set; }
public string Shape { get; set; }
public string Name { get; set; }
public string Clarity { get; set; }
public int CategoryID { get; set; }
public Category Category { get; set; }
public Shape Shapes { get; set; }
public IEnumerable<Gemstone> Gemstones { get; set; }
public IEnumerable<Clarity> Clarites { get; set; }
}
This is my action method.
public ActionResult Browse(string gemCategory = "")
{
var gemstones = from g in db.Gemstones
select g;
var category = db.Categories.Where(p => p.Name == gemCategory).FirstOrDefault();
gemstones = (gemstones.Include(s => s.Shapes)
.Include(c => c.Clarities)
.Where(p => p.CategoryID == category.CategoryID));
var viewModel = new StoreBrowseViewModel()
{
Category = category,
Gemstones = gemstones,
};
return this.View(viewModel);
}
This is my view model chtml page
#model ViewModels.StoreBrowseViewModel
grid.Column("Carat", header: "Weight " + Html.SortDirection(ref grid, "Carat")#item.Carat),
grid.Column("ShapeId", header: "Shape " + Html.SortDirection(ref grid, "Shape")#item.Shape),
grid.Column("ClarityId", header: "Clarity " + Html.SortDirection(ref grid, "Clarity")#item.Clarity),
grid.Column("Price", header: "Price(USD) " + Html.SortDirection(ref grid, "Price")#item.Price),
This is my out put It should display shape name and clarity name
I would do it differently from what im gona show below but this should help...
public ActionResult Browse(string gemCategory = "")
{
var category = db.Categories.FirstOrDefault(p => p.Name == gemCategory);
var gemstones = db.Gemstones.Include(s => s.Shapes)
.Include(c => c.Clarities)
.Include(c => c.Categories)
.Include(c => c.Cuts)
.Include(c => c.Orgins)
.Where(p => p.CategoryID == category.CategoryID);
var viewModel = new StoreBrowseViewModel() {Gemstones = gemstones};
return View(viewModel);
}
view model
public class StoreBrowseViewModel
{
public IEnumerable<Gemstone> Gemstones { get; set; }
}
in the view
#foreach(var item in Model.Gemstones)
{
<span>#item.Name</span>
#foreach(var item2 in Model.Gemstones.Clarities)
{
<span>#item2.Name</span>
}
}