views:

748

answers:

3

Hello SO:

I am learning ASP.NET MVC. I am following one of the basic tutorials on asp.net. Since I do not always follow the tutorials to the letter, I decided to use a GUID as the identity column instead of an integer. Everything was working fine until I got up to the point of adding a new record to the database through the MVC application. When I added the new record, it inserted a blank GUID instead of a generated one. Here is the segment of code-behind that handles the insertion:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create([Bind(Exclude = "id")]Movie movieToCreate)
{
    try
    {
     _entities.AddToMovieSet(movieToCreate);
     _entities.SaveChanges();

     return RedirectToAction("Index");
    }
    catch
    {
     return View();
    }
}

The [Bind(Exclude = "id")] line 'ignores' the ID column, since it is autogenerated. In the tutorial, the ID is auto-incremented but I think that is because it is an integer. I tried adding a line to this method:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create([Bind(Exclude = "id")]Movie movieToCreate)
{
    try
    {
     movieToCreate.id = Guid.NewGuid();
     _entities.AddToMovieSet(movieToCreate);
     _entities.SaveChanges();

     return RedirectToAction("Index");
    }
    catch
    {
     return View();
    }
}

But the id is still an empty GUID. Can anyone provide me with some information on why this is and perhaps how to resolve it?

Thanks!

+1  A: 

You could use a custom ModelBinder. I learned about those over here.

public class MyClassBinder : DefaultModelBinder {
 protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType) {
  var model = (Movie)base.CreateModel(controllerContext, bindingContext, modelType);
  model.id = Guid.NewGuid();
  return model;
 }
}

And your action controller would be:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult MyAction(Movie movieToCreate) {
 // movie should have a new guid in the id
 _entities.AddToMovieSet(movieToCreate);
 _entities.SaveChanges();
}

And you would need to register the binder in Global.asax:

protected void Application_Start() {
 RegisterRoutes(RouteTable.Routes);
 ModelBinders.Binders.Add(typeof(Movie), new MyClassBinder());
}
swilliams
Will I create this binder inside of its own file? If so, where should this be placed? In the same directory as my controllers? Thanks for the information and link!
Anders
I put my custom binders in a folder called Binders in the Models folder of my MVC Application.
Ben Robbins
Excellent, thank you for this!
Anders
I generally create an Extensions folder in my project and put stuff like that and HtmlHelper extensions in there.
swilliams
A: 

The SQL Server EF provider in .NET 3.5 SP1 cannot retrieve server-generated GUIDs, since archaic versions of SQL Server couldn't support that. So you need to generate the GUID client-side, until this limitation is fixed.

You can do this in a model binder, as swilliams suggests, but IMHO binders should only bind data in the request. So I do it on my model tier. But either way will work.

Craig Stuntz
Could you elaborate on your method? As I am learning I would like to know all the options I have available to me. Thanks!
Anders
Simple, really. I set the ID in code (with Guid.NewGuid()) before calling SaveChanges() when inserting a new record. All of this is done inside a model assembly which is separate from the assembly containing controllers and views.
Craig Stuntz
So basically what I did, by removing the [Bind(Execute)] from the Movie parameter?
Anders
+1  A: 

What is the type of ID field in SQL database? Is it uniqueidentifier? If not, change it to uniqueidentifier and make it autogenerated. Check out this article.

Sorantis
He has already done that; read the question carefully. The SQL Server provider for the EF won't forward the autogenerated GUIDs to the client.
Craig Stuntz
Ouch...My bad..
Sorantis