8

I'm registering a new client in my database, and when that operation finishes I want to show a detail view for that new client. The problem is I'm not finding the way to send to the Details action method the ID of the recently added client.

I'm working on this code:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Register(ClientModel credential)
{
    if(ModelState.IsValid)
    {
        database.Clients.Add(credential);
        database.SaveChanges();

        return RedirectToAction("Details", //Here is my problem, because sending credential.ID gives me an error as far as credential doesn't have an ID due it's not a database record.;
    }
    else
    {
        return View();
    }
}

This is mi ClientModel:

[Table("Clients")]
public class ClientModel
{
    [Key]
    public Int16 ID { get; set; }

    [Required]
    public String Name { get; set; }

    public String Address { get; set; }

    [EmailAddress]
    public String EmailAddress { get; set; }

    [Phone]
    public String Phone { get; set; }

    public virtual ICollection<UserModel> Users { get; set; }
}

Could someone bring light to my path?

EDIT:

Got an answer, which I would like to discuss:

return RedirectToAction("Details", new { ID = credential.ID });

This solution worked, but now the question could be, is this the better solution, or should I use 2 classes as commented in @Basic's answer?

tereško
  • 56,151
  • 24
  • 92
  • 147
  • Not the same issue, but helpful anyway. –  Jan 31 '14 at 00:34
  • I'm 99% sure that it is the same issue - either that or I've misunderstood your question. As a more explicit example, have a look at [this question](http://stackoverflow.com/a/8435544/156755) which says the same. Of course, it's possible I've misunderstood you - in which case, can you clarify why your question is different? Thanks. _P.S. No need to apologise for not understanding (your comment on my answer). We all started out knowing nothing._ – Basic Jan 31 '14 at 00:52
  • I agree with @Basic. I read both questions three times and I can't see how the question is different. If you're convinced that they are, please explain. For now, I'm voting to mark it as a dupe. – Adi Inbar Jan 31 '14 at 03:04
  • It's not a duplicate, because the answers there doesn't solve my issue, as they do all your comments, and answers here. So, the question is either not the same or not be addressed in the same way. –  Jan 31 '14 at 18:28

2 Answers2

8

When you call SaveChanges(), credential.ID, if an auto number in the database and setup in the EF model, should be populated. You could try reloading an element like mentioned in Entity Framework 4.1 DbSet Reload.

In your scenario above, you are adding a new client, so you need to convert the client model to client like:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Register(ClientModel credential)
{
    var client = new Client
    {
       Name = model.Name,
       EmailAddress = model.EmailAddress,
       .
       .
    };

    db.Clients.Add(client);
    db.SaveChanges();

    model.ID = client.ID;

    return View(model);
}

And then the ID will be returned with the model.

Community
  • 1
  • 1
Brian Mains
  • 49,697
  • 35
  • 139
  • 249
  • Actually, the fact is that _credential_ doesn't have and ID because their values are get from a registration form where there is no ID field to complete, due that it's job for the database. Could that be the problem? Should I use other _model_ for this value? **EDIT**: I've added the model up there. –  Jan 31 '14 at 00:03
  • Oh I see, updated above. – Brian Mains Jan 31 '14 at 00:45
  • Please check my last comment on the answer from @Basic –  Jan 31 '14 at 18:33
  • Keep Client and ClientModel separate, Client should be generated from EF, and ClientModel will contain the properties you need with the necessary MVC attributes applied (Display, Validation, etc.). Client is therefore a data transfer object (DTO pattern). Follow my approach above and it will work. – Brian Mains Jan 31 '14 at 18:56
  • Can you complete the method header for me to clarify your answer, please? It is not better to just work with `Client` instead of `ClientModel`? I could send _a client_ to the database, and let Entity Framework recognize that should be a _a client with an ID_, add it and gives me back a `ClientModel` class? So the parameter should be `Client client` instead of `ClientModel credential`. –  Jan 31 '14 at 19:05
  • Updated. I would keep clientmodel as the input coming back, and client only for the purposes of editing/reading. When data is being saved, the ClientModel class is posted back, the data copied to the Client class and saved. For read, a Client object is read, and returned through ClientModel. – Brian Mains Jan 31 '14 at 19:12
  • Have you read my last edit on the question? I'm using an anonymous type object to solve the issue, saving the `Client` declaration and assignment. On the other hand, using a `Client` class for methods like `Edit()`, `Detail()`, etc. could give my code semantic meaning, is that your point? –  Jan 31 '14 at 19:18
4

Once you call database.SaveChanges();, the ID field on the model will be populated with the correct Id. So the simple answer to your question is...

Int16 NewId = credential.ID;

As an aside, unless you're absolutely certain you'll never need to cope with more than 32,767 of any type of record, you're usually better off to use Int for all Id fields (or even Long) - that way you don't need to remember which are Int16 and which are Int. The few bytes you save by halving the length of some Id fields are absolutely negligible unless you're on an embedded system.

Basic
  • 25,223
  • 23
  • 108
  • 188
  • So what should I send to the `Detais` method? _credential.ID_, _NewId_, or should I search for the added item into the database? **INFO**: Sorry for the inconveniences, but I'm new in this. I've been watching tutorials, and reading reviews for the last 2 weeks. –  Jan 31 '14 at 00:31
  • Ok, it _should_ work like this... You create a new model with no Identity specified. You add it to the appropriate table on your context and then call `SaveChanges()`. `SaveChanges()` stores the record in the database. In the process, it's assigned an Id automatically by the DB. Entity framework realises the local copy you have is out of date and updates it from the database, _including the new `Id`_. You then use the Id field for your redirect. In my example, `NewId` and `credential.ID` should be the same. If this is not happening, there's a problem with your EF setup or DB setup. – Basic Jan 31 '14 at 00:47
  • Have a look at the database table using SQL Management Studio. Is the ID field set to Auto Increment? Does inserting a record by hand (using SMS) assign an ID automatically? – Basic Jan 31 '14 at 00:48
  • So should I create a `Client` class, as a copy without _ID_ of my `ClientModel` class for work inside the application, letting the _-Model_ classes being only used by the framework. Doing this maybe I should make `ClientModel` inherits from `Client`, am I wrong? –  Jan 31 '14 at 18:30
  • You can have classes inheriting from each other but there's no need. You create an instance of the model and just don't set the value of the Id. You call `SaveChanges()`. The Id is now populated on the model instance you just created. – Basic Jan 31 '14 at 19:41
  • Sounds fairly easy, instead of having multiple classes. –  Jan 31 '14 at 19:51