I am creating products from a product template. Each time a customer selects a product to view information about, the data from that product needs to get loaded. I have created a controller, model and view. The model is generated with TDS. I need to pass the item id to the [SitecoreId] from the controller. Here is the code I am using:
From the layout:
#{var id = Sitecore.Data.ID.Parse("{74A67488-8E33-47E2-86F5-25AD23FDF3D3}"); }
#Html.Sitecore().ControllerRendering("ProductOverview", "Index", new { ItemId = #id })
The controller:
public class ProductOverviewController : Controller
{
private readonly IMvcContext _mvcContext;
public ProductOverviewController(IMvcContext mvcContext)
{
_mvcContext = mvcContext;
}
// GET: ProductOverview
public ActionResult Index()
{
var itemId = string.Empty;
var rc = RenderingContext.CurrentOrNull;
if (rc != null)
{
var parms = rc.Rendering.Properties;
itemId = parms["ItemId"];
}
var dataSource = _mvcContext.GetContextItem<ProductOverviewModel> ();
return View(dataSource);
}
}
The itemId var has the correct id that I am passing from the layout (hard coded for now). From here I am at an absolute loss on how to get that into the model. I have tried dozens of suggestions from searches but the model always uses the current item (as set by GlassBase in the model itself) as opposed to the product id that contains the data for that product.
Is what I want to do even possible? Can the [SitecoreId] even be overridden?
The line where you are setting the value for dataSource using Glass Mapper is where you'll want to make your change..
Glass Mapper lets you use a number of different options to get the Item and cast to your "type" which looks to be ProductOverviewModel currently.
you can use the following for example (notice that I've used .SitecoreService.GetItem instead of .GetContextItem ):
//pass the GUID into here (you'd need to cast to a Guid first instead of ID)
var dataSource = _mvcContext.SitecoreService.GetItem<ProductOverviewModel>(guid);
//or if you wanted to get your ID as a Sitecore Item you could use
var dataSource = _mvcContext.SitecoreService.GetItem<ProductOverviewModel>(item.Paths.Path);
Currently I am doing like this:
For Example:
public update(Person model)
{
// Here model is model return from form on post
var oldobj = db.Person.where(x=>x.ID = model.ID).SingleOrDefault();
db.Entry(oldobj).CurrentValues.SetValues(model);
}
It works, but for example,
I have 50 columns in my table but I displayed only 25 fields in my form (I need to partially update my table, with remaining 25 column retain same old value)
I know it can be achieve by "mapping columns one by one" or by creating "hidden fields for those remaining 25 columns".
Just wondering is there any elegant way to do this with less effort and optimal performance?
This is a very good question. By default I have found that as long as change tracking is enabled (it is by default unless you turn it off), Entity Framework will do a good job of applying to the database only what you ask it to change.
So if you only change 1 field against the object and then call SaveChanges(), EF will only update that 1 field when you call SaveChanges().
The problem here is that when you map a view model into an entity object, all of the values get overwritten. Here is my way of handling this:
In this example, you have a single entity called Person:
Person
======
Id - int
FirstName - varchar
Surname - varchar
Dob - smalldatetime
Now let's say we want to create a view model which will only update Dob, and leave all other fields exactly how they are, here is how I do that.
First, create a view model:
public class PersonDobVm
{
public int Id { get; set; }
public DateTime Dob { get; set; }
public void MapToModel(Person p)
{
p.Dob = Dob;
}
}
Now write the code roughly as follows (you'll have to alter it to match your context name etc):
DataContext db = new DataContext();
Person p = db.People.FirstOrDefault();
// you would have this posted in, but we are creating it here just for illustration
var vm = new PersonDobVm
{
Id = p.Id, // the Id you want to update
Dob = new DateTime(2015, 1, 1) // the new DOB for that row
};
vm.MapToModel(p);
db.SaveChanges();
The MapToModel method could be even more complicated and do all kinds of additional checks before assigning the view model fields to the entity object.
Anyway, the result when SaveChanges is called is the following SQL:
exec sp_executesql N'UPDATE [dbo].[Person]
SET [Dob] = #0
WHERE ([Id] = #1)
',N'#0 datetime2(7),#1 int',#0='2015-01-01 00:00:00',#1=1
So you can clearly see, Entity Framework has not attempted to update any other fields - just the Dob field.
I know in your example you want to avoid coding each assignment by hand, but I think this is the best way. You tuck it all away in your VM so it does not litter your main code, and this way you can cater for specific needs (i.e. composite types in there, data validation, etc). The other option is to use an AutoMapper, but I do not think they are safe. If you use an AutoMapper and spelt "Dob" as "Doob" in your VM, it would not map "Doob" to "Dob", nor would it tell you about it! It would fail silently, the user would think everything was ok, but the change would not be saved.
Whereas if you spelt "Dob" as "Doob" in your VM, the compiler will alert you that the MapToModel() is referencing "Dob" but you only have a property in your VM called "Doob".
I hope this helps you.
I swear by EntityFramework.Extended. Nuget Link
It lets you write:
db.Person
.Where(x => x.ID == model.ID)
.Update(p => new Person()
{
Name = newName,
EditCount = p.EditCount+1
});
Which is very clearly translated into SQL.
Please try this way
public update(Person model)
{
// Here model is model return from form on post
var oldobj = db.Person.where(x=>x.ID = model.ID).SingleOrDefault();
// Newly Inserted Code
var UpdatedObj = (Person) Entity.CheckUpdateObject(oldobj, model);
db.Entry(oldobj).CurrentValues.SetValues(UpdatedObj);
}
public static object CheckUpdateObject(object originalObj, object updateObj)
{
foreach (var property in updateObj.GetType().GetProperties())
{
if (property.GetValue(updateObj, null) == null)
{
property.SetValue(updateObj,originalObj.GetType().GetProperty(property.Name)
.GetValue(originalObj, null));
}
}
return updateObj;
}
I have solved my Issue by using FormCollection to list out used element in form, and only change those columns in database.
I have provided my code sample below; Great if it can help someone else
// Here
// collection = FormCollection from Post
// model = View Model for Person
var result = db.Person.Where(x => x.ID == model.ID).SingleOrDefault();
if (result != null)
{
List<string> formcollist = new List<string>();
foreach (var key in collection.ToArray<string>())
{
// Here apply your filter code to remove system properties if any
formcollist.Add(key);
}
foreach (var prop in result.GetType().GetProperties())
{
if( formcollist.Contains(prop.Name))
{
prop.SetValue(result, model.GetType().GetProperty(prop.Name).GetValue(model, null));
}
}
db.SaveChanges();
}
I still didn't find a nice solution for my problem, so I created a work around. When loading the Entity, I directly make a copy of it and name it entityInit. When saving the Entity, I compare the both to see, what really was changed. All the unchanged Properties, I set to unchanged and fill them with the Database-Values. This was necessary for my Entities without Tracking:
// load entity without tracking
var entityWithoutTracking = Context.Person.AsNoTracking().FirstOrDefault(x => x.ID == _entity.ID);
var entityInit = CopyEntity(entityWithoutTracking);
// do business logic and change entity
entityWithoutTracking.surname = newValue;
// for saving, find entity in context
var entity = Context.Person.FirstOrDefault(x => x.ID == _entity.ID);
var entry = Context.Entry(entity);
entry.CurrentValues.SetValues(entityWithoutTracking);
entry.State = EntityState.Modified;
// get List of all changed properties (in my case these are all existing properties, including those which shouldn't have changed)
var changedPropertiesList = entry.CurrentValues.PropertyNames.Where(x => entry.Property(x).IsModified).ToList();
foreach (var checkProperty in changedPropertiesList)
{
try
{
var p1 = entityWithoutTracking.GetType().GetProperty(checkProperty).GetValue(entityWithoutTracking);
var p2 = entityInit.GetType().GetProperty(checkProperty).GetValue(entityInit);
if ((p1 == null && p2 == null) || p1.Equals(p2))
{
entry.Property(checkProperty).CurrentValue = entry.Property(checkProperty).OriginalValue; // restore DB-Value
entry.Property(checkProperty).IsModified = false; // throws Exception for Primary Keys
}
} catch(Exception) { }
}
Context.SaveChanges(); // only surname will be updated
This is way I did it, assuming the new object has more columns to update that the one we want to keep.
if (theClass.ClassId == 0)
{
theClass.CreatedOn = DateTime.Now;
context.theClasses.Add(theClass);
}
else {
var currentClass = context.theClasses.Where(c => c.ClassId == theClass.ClassId)
.Select(c => new TheClasses {
CreatedOn = c.CreatedOn
// Add here others fields you want to keep as the original record
}).FirstOrDefault();
theClass.CreatedOn = currentClass.CreatedOn;
// The new class will replace the current, all fields
context.theClasses.Add(theClass);
context.Entry(theClass).State = EntityState.Modified;
}
context.SaveChanges();
In EF you can do like this
var result = db.Person.Where(x => x.ID == model.ID).FirstOrDefault();
if(result != null){
result.Name = newName;
result.DOB = newDOB;
db.Person.Update(result);
}
Or you can use
using (var db= new MyDbContext())
{
var result= db.Person.Where(x => x.ID == model.ID).FirstOrDefault();
result.Name= newName;
result.DOB = newDOB;
db.Update(result);
db.SaveChanges();
}
For more detail please EntityFramework Core - Update Only One Field
No Worry guys
Just write raw sql query
db.Database.ExecuteSqlCommand("Update Person set Name='"+_entity.Name+"' where Id = " + _entity.ID + "");
I have 2 tables tbl_computer and tbl_computerperipheral
I need a editor view which consists of data from both tables.
How Can I get 2 tables in a single view so that I can insert data into 2 tables at once.
Thanx
For your Better Reference just have a look to ::
How to Combine two models into a single model and pass it to view using asp.net MVC razor
and then on Form submit on server side (i.e. into controller's action) save the data coming from view in the form like::
public ActionResult Save(CommonViewModel common)
{
var FirstModel = new FirstModel();
FirstModel = common.FirstModel;
db.Entry(FirstModel).State = EntityState.Added;
var SecondModel = new SecondModel();
SecondModel = common.SecondModel;
db.Entry(SecondModel).State = EntityState.Added;
db.SaveChanges();
}
May be this answer will be helpful to get answer of your Query.
[HttpPost]
public ActionResult Edit(CompPeripheral cp, int c_id,int em_id,int asset_id)
{
if (ModelState.IsValid)
{
cp.Compconfig.c_id = c_id;
cp.Compconfig.em_id = em_id;
cp.Compconfig.asset_id = asset_id;
db.tbl_compconfig.Add(cp.Compconfig);
db.SaveChanges();
var id = db.tbl_compconfig.Max(a => a.comp_id);
cp.Comperipheral.comp_id = id;
//saving Comp Peripheral in database
db.tbl_comperipheral.Add(cp.Comperipheral);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.asset_id = new SelectList(db.tbl_assetm, "asset_id", "asset_name", cp.Compconfig.asset_id);
ViewBag.em_id = new SelectList(db.tbl_employee, "em_id", "em_fullname", cp.Compconfig.em_id);
ViewBag.c_id = new SelectList(db.tbl_client, "c_id", "c_name", cp.Compconfig.c_id);
return View(cp);
}
I am using MVC3-Viewmodel model first on my project.
When a user enters a value in my DDL and TextArea and then click on my form button it will basicly execute a ajax url.post to my POST action, right now my Post Action method creates and saves it. But what I want is some type of check, example:
step 1: If SelectQuestion has any answer
step 2: If answer exist do an update
step 3: if answer do not exist create a new and save it.
This is how my controller looks like now:
[HttpPost]
public JsonResult AnswerForm(int id, SelectedQuestionViewModel model)
{
bool result = false;
var goalCardQuestionAnswer = new GoalCardQuestionAnswer(); // Creates an instance of the entity that I want to fill with data
SelectedQuestion SelectedQ = answerNKIRepository.GetSelectedQuestionByID(model.QuestionID); // Retrieve SelectedQuestion from my repository with my QuestionID.
goalCardQuestionAnswer.SelectedQuestion = SelectedQ; // Filling my entity with SelectedQ
goalCardQuestionAnswer.SelectedQuestion.Id = model.QuestionID; // filling my foreign key with the QuestionID
goalCardQuestionAnswer.Comment = model.Comment; // Filling my entity attribute with data
goalCardQuestionAnswer.Grade = model.Grade; // Filling my entity attribute with data
answerNKIRepository.SaveQuestionAnswer(goalCardQuestionAnswer); // adding my object
answerNKIRepository.Save(); // saving
result = true;
return Json(result);
}
Comment and Grade are nullable aswell.
The entitys are associated like
[Question](1)------(*)[SelectedQuestion](1)-----(0..1)[GoalCardQuestionAnswer]
Any kind of help is appreciated.
Thanks in advance!
I achieved my question and the answer is following:
[HttpPost]
public JsonResult AnswerForm(int id, SelectedQuestionViewModel model)
{
SelectedQuestion SelectedQ = answerNKIRepository.GetSelectedQuestionByID(model.QuestionID);
if (SelectedQ.GoalCardQuestionAnswer == null)
{
var goalCardQuestionAnswer = new GoalCardQuestionAnswer();
goalCardQuestionAnswer.SelectedQuestion = SelectedQ;
goalCardQuestionAnswer.SelectedQuestion.Id = model.QuestionID;
goalCardQuestionAnswer.Comment = model.Comment;
goalCardQuestionAnswer.Grade = model.Grade;
this.answerNKIRepository.SaveQuestionAnswer(goalCardQuestionAnswer);
this.answerNKIRepository.Save();
const bool Result = true;
return this.Json(Result);
}
else
{
if (SelectedQ.GoalCardQuestionAnswer != null)
{
SelectedQ.GoalCardQuestionAnswer.Comment = model.Comment;
}
if (SelectedQ.GoalCardQuestionAnswer != null)
{
SelectedQ.GoalCardQuestionAnswer.Grade = model.Grade;
}
const bool Result = false;
return this.Json(Result);
}
}
I need help with trying to insert a record using MVC and Entity Framework. I have a dynamically created form which can contain many questions. When Editing, I want to delete the existing answers (which it does successfully) and insert new answers.
I am getting the following error:
Cannot insert explicit value for identity column in table 'tblModeratorReportAnswers' when IDENTITY_INSERT is set to OFF.
If I add the following line in my DbContext class
modelBuilder.Entity<QuestionAnswer>().Property(p => p.AnswerID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); I get this error:
A dependent property in a ReferentialConstraint is mapped to a store-generated column. Column: 'AnswerID'.
Here's my code that is doing the update
//
// POST: /Home/Edit/1
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(FormCollection formCollection, int moderatorReportId)
{
ModeratorReport reportToEdit = repository.GetModeratorReportById(moderatorReportId);
List<QuestionAnswer> originalReportAnswers = repository.GetAllModeratorReportAnswers(moderatorReportId).ToList();
foreach (QuestionAnswer answer in originalReportAnswers) {
repository.DeleteAnswer(answer);
}
repository.Save();
int sectionID;
int questionID;
foreach (string key in formCollection.AllKeys)
{
var value = formCollection[key.ToString()];
Match m = Regex.Match(key, "section(\\d+)_question(\\d+)");
if (m.Success) {
QuestionAnswer newAnswer = new QuestionAnswer();
sectionID = Convert.ToInt16(m.Groups[1].Value.ToString());
questionID = Convert.ToInt16(m.Groups[2].Value.ToString());
newAnswer.ModeratorReportID = moderatorReportId;
newAnswer.QuestionID = questionID;
newAnswer.Answer = value;
repository.AddAnswer(newAnswer);
}
}
repository.Save();
reportToEdit.Status = "SUBJECTOFFICER SAVED";
AuditItem auditItem = new AuditItem();
auditItem.ModeratorReportID = moderatorReportId;
auditItem.Status = "SUBJECTOFFICER SAVED";
auditItem.AuditDate = DateTime.Now;
auditItem.Description = "The Moderator report ID: " + moderatorReportId + " was saved.";
auditItem.UserID = User.Identity.Name;
db.Audit.Add(auditItem);
repository.Save();
return RedirectToAction("Details", new { id = moderatorReportId });
}
...and in my repository
//
// Persistance
public void Save()
{
db.SaveChanges();
}
public void AddAnswer(QuestionAnswer answer)
{
db.Answers.Add(answer);
Save();
}
public void DeleteAnswer(QuestionAnswer answer)
{
db.Answers.Attach(answer);
db.Answers.Remove(answer);
}
I have also checked all my Primary Keys, Foreign Keys and they are all ok. The Primary Keys are all set to 'Is Identity'.
I've been trying to sort this problem out all day. I have no idea what to do to resolve it. If anyone can give my any advice, it'd be much appreciated.
Maybe it's my inexperience with ASP.NET MVC and Entity Framework, but I have now resolved this issue by changing the logic of that I update the report.
Instead of deleting the answers and reinserting them. I now retrieve the answers and change Answer property to be the new answer. Then just use db.SaveChanges().
//
// POST: /Home/Edit/1
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(FormCollection formCollection, int moderatorReportId)
{
ModeratorReport reportToEdit = repository.GetModeratorReportById(moderatorReportId);
List<QuestionAnswer> originalReportAnswers = repository.GetAllModeratorReportAnswers(moderatorReportId).ToList();
int sectionID;
int questionID;
foreach (string key in formCollection.AllKeys)
{
var value = formCollection[key.ToString()];
Match m = Regex.Match(key, "section(\\d+)_question(\\d+)");
if (m.Success) {
QuestionAnswer newAnswer = new QuestionAnswer();
sectionID = Convert.ToInt16(m.Groups[1].Value.ToString());
questionID = Convert.ToInt16(m.Groups[2].Value.ToString());
foreach(QuestionAnswer answerToEdit in originalReportAnswers) {
if (answerToEdit.QuestionID == questionID)
{
answerToEdit.Answer = value;
}
}
}
}
repository.Save();
reportToEdit.Status = "SAVED";
AuditItem auditItem = new AuditItem();
auditItem.ModeratorReportID = moderatorReportId;
auditItem.Status = "SAVED";
auditItem.AuditDate = DateTime.Now;
auditItem.Description = "The Moderator report ID was saved.";
auditItem.UserID = User.Identity.Name;
db.Audit.Add(auditItem);
repository.Save();
return RedirectToAction("Details", new { id = moderatorReportId });
}
Cannot insert explicit value for identity column in table
'tblModeratorReportAnswers' when IDENTITY_INSERT is set to OFF.
This error says that you are explicitly inserting value into autogenerated column (identity column).
A dependent property in a ReferentialConstraint is mapped to a
store-generated column. Column: 'AnswerID'.
This error says that there is some incorrectly configured relation where autogenerated AnswerID is considered as FK - that is not supported. Identity and Computed properties must not be FKs.