ASP.NET MVC: How do I keep a field byte[] - asp.net-mvc

I've got a field which its type is byte[]. This field will hold my entity's RecordVersion property (timestamp in the database). How do I keep this field so that when I save my entity it is available?
I've tried two different things and haven't succeeded so far:
This renders "System.Byte[]":
<%= Html.Hidden("RecordVersion", Model.RecordVersion.ToString()) %>
This throws a ModelStateError where the type couldn't be converted:
ViewData["RecordVersion"] = entity.RecordVersion
Apparently the default MVC's mechanism that does the bind/unbind doesn't like much byte[] fields .....

You need to make a modelbinder and register it.
This article shows how to use a timestamp from a linq database in a hidden field much like what you are doing.
ModelBinders.Binders.Add(typeof(Binary), new LinqBinaryModelBinder());
In global.asax to register it.
That LinqBinaryModelBinder is in the futures assembly. If you want to user byte[] you'll have to write one yourself.

Have you tried.
<%= Html.Hidden("RecordVersion", System.Text.Encoding.Unicode.GetString(Model.RecordVersion)) %>

I wouldn't put the timestamp on the form. If you want to keep the object around I'd cache it server side and retrieve it from the cache using the id. Otherwise, you can re-retrieve the object from the database and apply the changes from your form data. The latter is what I do, using TryUpdateModel.
public ActionResult Update( int id )
{
using (var context = new DataContext())
{
var model = context.Models.Where( m => m.ID == id ).Single();
if (TryUpdateModel( model ))
{
...
context.SubmitChanges(); // wrapped in try/catch
...
}
else
{
...
}
}
return RedirectToAction( "Show", new { id = id } );
}

Related

Selecting only part of the DbSet in Read method of Kendo grid

I'm trying to use a Kendo UI grid in MVC and remote data. I want to only grab and display data from the DbSet, onload, where one of the fields, "Status", equals '1'. I thought this should be able to be accomplished in the controller:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Read([DataSourceRequest] DataSourceRequest request)
{
using (var db = new MyData(false))
{
var data = db.Training.Where(d => d.Status == '1').Select(d => new Training {
Id = d.Id,
Name = d.Name,
Status = d.Status
}).ToDataSourceResult(request);
return Json(data, JsonRequestBehavior.AllowGet);
}
}
The above code gives me the error that "The entity or complex type 'Training' cannot be constructed in a LINQ to Entities query". Any suggestions on how to rewrite the Linq statement so it'll work, or maybe a way to do it within the grid to suppress any that do not have a Status of '1'?
Your code is trying to project to a mapped entity which is not allowed. Additionally it's redundant to do that as you already have your entities. Remember that .Select() is for mapping one type to another but the .Where() method is already returning a list of your entities (Training).
Remove the .Select() and the query should work:
var data = db.Training.Where(d => d.Status == '1').ToDataSourceResult(request);

knockout js .net load page with and without model

I am trying to reuse the same form for adding and editing employee information. I am using knockout js and on my view I make the knockout model:
var koModel = new EmployeeModel(div);
and if I want to populate the fields from the server I want to do something like this:
var koModel = new EmployeeModel(unserializedModelFromController, div);
I was wondering what is the best way to distinguish if the request is for a new employee or if it is to edit an existing employee.
If you turn your parameters around you can write a single constructor function.
var EmployeeModel = function(div, model) {
if (model) {
// Existing model has been passed, it's an edit request
} else {
// No model has been passed, it's a new request
}
}
This can be called like:
new EmployeeModel(div);
or
new EmployeeModel(div, model);
You can send a parameter with a default value to the view.
If you are editing an employee, you can send the value of id, you're creating not send.
The function that receives a request to store or edit could have a default value.
public void SaveOrEditEmployee(int id=0, ...) //id=0 is a default value
{
if(id==0)
{
//SaveEmployee
}else
{
//EditEmployee
Employee empl = (x => employee.id == id);
...
}
}
Or you can do likewise, receive full model and assess whether the property 'id' already exists in your database

InvalidOperationException when using updatemodel with EF4.3.1

When I update my model I get an error on a child relation which I also try to update.
My model, say Order has a releationship with OrderItem. In my view I have the details of the order together with an editortemplate for the orderitems. When I update the data the link to Order is null but the orderid is filled, so it should be able to link it, TryUpdateModel returns true, the save however fails with:
InvalidOperationException: The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted.]
My update method:
public ActionResult ChangeOrder(Order model)
{
var order = this.orderRepository.GetOrder(model.OrderId);
if (ModelState.IsValid)
{
var success = this.TryUpdateModel(order);
}
this.orderRepository.Save();
return this.View(order);
}
I tried all solutions I saw on SO and other sources, none succeeded.
I use .Net MVC 3, EF 4.3.1 together with DBContext.
There are a number of code smells here, which I'll try to be elegant with when correcting :)
I can only assume that "Order" is your EF entity? If so, I would highly recommend keeping it separate from the view by creating a view model for your form and copying the data in to it. Your view model should really only contain properties that your form will be using or manipulating.
I also presume orderRepository.GetOrder() is a data layer call that retrieves an order from a data store?
You are also declaring potentially unused variables. "var order =" will be loaded even if your model is invalid, and "var success =" is never used.
TryUpdateModel and UpdateModel aren't very robust for real-world programming. I'm not entirely convinced they should be there at all, if I'm honest. I generally use a more abstracted approach, such as the service / factory pattern. It's more work, but gives you a lot more control.
In your case, I would recommend the following pattern. There's minimal abstraction, but it still gives you more control than using TryUpdateModel / UpdateModel:
public ActionResult ChangeOrder(OrderViewModel model) {
if(ModelState.IsValid) {
// Retrieve original order
var order = orderRepository.GetOrder(model.OrderId);
// Update primitive properties
order.Property1 = model.Property1;
order.Property2 = model.Property2;
order.Property3 = model.Property3;
order.Property4 = model.Property4;
// Update collections manually
order.Collection1 = model.Collection1.Select(x => new Collection1Item {
Prop1 = x.Prop1,
Prop2 = x.Prop2
});
try {
// Save to repository
orderRepository.SaveOrder(order);
} catch (Exception ex) {
ModelState.AddModelError("", ex.Message);
return View(model);
}
return RedirectToAction("SuccessAction");
}
return View(model);
}
Not ideal, but it should serve you a bit better...
I refer you to this post, which is similar.
I assume that the user can perform the following actions in your view:
Modify order (header) data
Delete an existing order item
Modify order item data
Add a new order item
To do a correct update of the changed object graph (order + list of order items) you need to deal with all four cases. TryUpdateModel won't be able to perform a correct update of the object graph in the database.
I write the following code directly using a context. You can abstract the use of the context away into your repository. Make sure that you use the same context instance in every repository that is involved in the following code.
public ActionResult ChangeOrder(Order model)
{
if (ModelState.IsValid)
{
// load the order from DB INCLUDING the current order items in the DB
var orderInDB = context.Orders.Include(o => o.OrderItems)
.Single(o => o.OrderId == model.OrderId);
// (1) Update modified order header properties
context.Entry(orderInDB).CurrentValues.SetValues(model);
// (2) Delete the order items from the DB
// that have been removed in the view
foreach (var item in orderInDB.OrderItems.ToList())
{
if (!model.OrderItems.Any(oi => oi.OrderItemId == item.OrderItemId))
context.OrderItems.Remove(item);
// Omitting this call "Remove from context/DB" causes
// the exception you are having
}
foreach (var item in model.OrderItems)
{
var orderItem = orderInDB.OrderItems
.SingleOrDefault(oi => oi.OrderItemId == item.OrderItemId);
if (orderItem != null)
{
// (3) Existing order item: Update modified item properties
context.Entry(orderItem).CurrentValues.SetValues(item);
}
else
{
// (4) New order item: Add it
orderInDB.OrderItems.Add(item);
}
}
context.SaveChanges();
return RedirectToAction("Index"); // or some other view
}
return View(model);
}

Best way to transfer an Entity Framework object over the web and back via JSON

I've got some MVC code that serializes an EF 3.5 object into an anonymous type for return as a JSON result to an AJAX call on my page. The hurdle I have is that when I send the object back to the server via JSON, (and let the ModelBinder deserialize it for me into my EF type), I have to update it in my Entity Framework context manually. Or at least that's what I'm doing now. It has no EntityKey, so attaching it fails. I end up having to look up the old object and update it property by property. Any ideas around this? Is the solution to pass the EntityKey around with my object?
Here's what I have:
public void Update(Album album)
{
using (var db = new BandSitesMasterEntities())
{
var albumToUpdate = db.Album.First(x => x.ID == album.ID);
albumToUpdate.AlbumTitle = album.AlbumTitle;
albumToUpdate.Description = album.Description;
albumToUpdate.ReleaseYear = album.ReleaseYear;
albumToUpdate.ImageURL = album.ImageURL;
albumToUpdate.OtherURL = album.OtherURL;
db.SaveChanges();
}
}
And here's what I'd like to do, or something similar:
public void Update(Album album)
{
using (var db = new BandSitesMasterEntities())
{
db.Attach(album)
db.SaveChanges();
}
}
or you could use AutoMapper to map those fields for you, so you'd just add one extra line to your example.
Why not just use the UpdateModel or TryUpdateModel controller methods instead? It works really well with EF and you can even explicitly set the included property list.
The id parameter will auto-map via the MVC framework to the hidden field on your form specifying the id.
public void Update(int id, FormCollection collection)
{
using (var db = new BandSitesMasterEntities())
{
var albumToUpdate = db.Album.First(x => x.ID == id);
//use UpdateModel to update object, or even TryUpdateModel
UpdateModel(albumToUpdate, new string[] { "AlbumTitle", "Description", "ReleaseYear", "ImageURL", "OtherURL" });
db.SaveChanges();
}
}
This became much easier for us in EF 4.0. This is what we did in EF 3.5:
public static void AttachAsModified(this ObjectContext objectContext, string setName, object entity,
IEnumerable<String> modifiedFields)
{
objectContext.AttachTo(setName, entity);
ObjectStateEntry stateEntry = objectContext.ObjectStateManager.GetObjectStateEntry(entity);
foreach (String field in modifiedFields)
{
stateEntry.SetModifiedProperty(field);
}
}
And then:
using (var db = new BandSitesMasterEntities())
{
db.AttachAsModified("Album", album, new string[] { "AlbumTitle", "Description", "ReleaseYear", "ImageURL", "OtherURL" })
db.SaveChanges();
}
It becomes more complicated if you have foreign key constraints, but it looks like you don't.
There is no way around the entity key issue. You either have to add it to your anonymous type or I would recommend you port your code to using data services.
http://www.hanselman.com/blog/jQueryToShipWithASPNETMVCAndVisualStudio.aspx
which would allow you to do all of the db manipulation on the client side.
http://msdn.microsoft.com/en-us/data/bb931106.aspx
Did you try something like:
object original;
var key = contexte..CreateEntityKey("EntitySet", modified);
if(contexte.TryGetObjectByKey(key, out original))
{
var originalEntity = (YourEntityType)original;
// You have to mannualy set your entityKey
originalEntity.YourEntityReference.EntityKey = new EntityKey("Entities.EntitySet", "Id", modified.YourEntity.Id);
contexte.ApplyPropertyChanges("EntitySet", modified);
}
contexte.SaveChanges();
Assuming that your EntityReference are set by dropDown, you'll still have the Id
In your Album entity's partial class you may define a CopyFrom function and call it from your Update function
partial class Album
{
public void CopyFrom(Album album)
{
//individual field copying here
}
}
public void Update(Album album)
{
...
albumToUpdate.CopyFrom(album);
...
}

MVC, UpdateModel OR delete depending on form field value?

I'm very new to this, so any help is appreciated.
I'll use the Dinners/RSVPs relationship for detailing my problem. One Dinner has many RSVPs.
On my Dinner edit page/view I want to be able to edit the Dinner information AND the RSVPs information.
I have that part working, based on the answer given here by James S:
int i = 0;
foreach (var r in Dinner.RSVPs) {
UpdateModel(r, "Dinner.RSVPs[" + i++ + "]");
}
But what if I want to Delete an RSVP based on the user clicking a checkbox next to the RSVP on the edit view? Something like this on the edit view:
<% int i = 0;
foreach (var rsvp in Model.RSVPs){%>
<%=Html.CheckBox("RemoveRSVP[" + i + "]") %>
<%= Html.TextBox("Dinner.RSVPs[" + i + "].Name", rsvp.Name) %>
<% i++;
}%>
I tried this, but it's not working, obviously:
Dinner dinner = dinnerRepository.GetDinner(id);
int i = 0;
foreach (var r in dinner.RSVPs) {
if (Boolean.Equals(RemoveRSVP[i], true){
dinner.RSVPs.Remove(r);
else
UpdateModel(r, "Dinner.RSVPs[" + i+ + "]");
i++;
}
I can't delete/remove an RSVP using UpdateModel can I?
Let me know if anything isn't clear.
Thanks.
I tried this, but it's not working, obviously:
Is your difficulty in actually making the delete go through? Or is it in processing the form to detect which ones should be deleted? e.g. which line doesn't work:
dinner.RSVPs.Remove(r);
or
if (Boolean.Equals(RemoveRSVP[i], true)
?
For #1
If your repository is backed by Linq 2 Sql and RSVP is an entity, you will usually have to cause DeleteOnSubmit() to be called in order for the record to be deleted from the database--just calling Remove() on the association will not be enough. You probably will add one of the following to your DinnerRepository to do this:
DinnerRepository.DeleteRsvp(RSVP item)
DinnerRepository.DeleteRsvp(Dinner dinner, RSVP rsvp)
Alternately, if you want LINQ to perform the delete automatically, you can edit the DBML as XML (right click, open with, XML Editor) and add the following attribute to the entity association:
<Association Name="..." ... DeleteOnNull="true" />
For #2
I usually construct this type of "repeating entity-delete checkbox" form so that the posted values are a list of the entity IDs I want to delete. To facilitate this I use an alternate CheckBox helper:
public static class HtmlExtensions
{
/// <summary>
/// Alternate CheckBox helper allowing direct specification of "name", "value" and "checked" attributes.
/// </summary>
public static string CheckBox(this HtmlHelper html, string name, string value, bool isChecked)
{
string tag = String.Format("<input type=\"checkbox\" name=\"{0}\" value=\"{1}\" {2}/>",
html.AttributeEncode(name),
html.AttributeEncode(value),
isChecked ? "checked=\"checked\" " : ""
);
return tag;
}
}
and create the checkboxes like so:
<%= Html.CheckBox("RemoveRsvpIds", rsvp.RsvpId.ToString(), false) %>
and consume the list like so:
public ActionResult TheFormInQuestion(int dinnerId, int[] removeRsvpIds)
{
var dinner = DinnerRepository.GetDinner(dinnerId);
foreach (var rsvp in dinner.RSVPs)
{
if (removeRsvpIds.Contains(rsvp.RsvpId))
{
// Perform Delete
}
else
{
// Perform Update
}
}
// The rest
}
I can't delete/remove an RSVP using UpdateModel can I?
The purpose of UpdateModel() is to automagically copy property values from the posted form onto an already-existing object--not to create new or destroy existing entities. So no, not really. It wouldn't be the expected behavior.
I am not totally familiar with the NerdDinner code but don't they use Linq to SQL for their backend? If that is the case I would think you could tackle this in a traditional web approach and append the record ID to the value of each check box in a list of items to be deleted. Then you could catch the collection of IDs on the server side and do a DeleteAllOnSubmit by passing a selection query of entities to the delete call? Let me know if you need more detail.

Resources