views:

92

answers:

2

My current setup:
I have an entity object with some properties, among them an int Id. To communicate with the database, I've created a repository with, among others, the following method:

public int Add(TEntity entity)
{
    ObjectSet.AddObject(entity);
    return entity.Id;
}

(It's a generic repository, that requires that TEntity is a class, and that it implements IEntity, which is just an interface with an int Id property.)

My problem:
Now, when I want to create a new entity for adding to the repository, I need to give it an id. However, that doesn't allow EF to automatically generate the id for me, since it will already have a value.

Possible solutions:
I have only been able to think of these two possibilities.

  1. Make the Id property nullable
  2. Pass an EntryInputModel to the repository instead, and then do the mapping there.
    Currently I'm binding to EntryInputModel in my Controller, and mapping it to an Entry using AutoMapper. If I need to change this, I also need to re-think the dependencies in my project, since the ...InputModel and ...ViewModel classes are currently only available to my Web application.

Which is preferable? Are there more ways to counter this?

A: 

you can have generated unique id's that are not generated by the datastore/EF, allowing you to define them before passing the object into EF... (think of Guid's)

Tim Mahy
However, I'd like to use `int`s for id, to be able to use them in url's without loosing so much readability.
Tomas Lycken
+1  A: 

If you're using SQL Server as the backend, my recommended solution would be to make the ID column an INT IDENTITY on the database, and a not-nullable column in your entity object.

When adding a new entity, assign the ID some arbitrary value, like -1 or something, or no value at all, even. The actual ID will be generated automatically by SQL Server when you insert the new entity (

EntityContext.AddToEntities(newEntity);
EntityContext.SaveChanges();

and it will be automagically be signalled back to EF so you'll be able to use it right away once it's inserted:

int newEntityID = newEntity.ID;

Works like a charm - at least in my test apps :-)

marc_s
I haven't actually created the DB yet. How do I reflect this behavior in a unit test of the Add() method on the repository?
Tomas Lycken
@Tomas: if you need to mock this, you need to set the identity field to a valid value when the .SaveChanges() is called
marc_s
@marc: As you see in the Add method above, I actually don't have the call to SaveChanges() in there. Does that mean that it's impossible to get the new id back from it?
Tomas Lycken
@Tomas: if you use server-generated ID's (IDENTITY fields), those will only ever be generated when the data is actually inserted, e.g. when you call `SaveChanges()`
marc_s
@marc: I think the issue is actually myself being stupid. What I should do is to pass the entity to the repository and add it, then call SaveChanges on my UnitOfWork (which in this case is a wrapper around the ObjectContext) and then just use the entity's id property where I need it. Since it's not in the DB, EF (or rather SQL Server) will assign new id regardless of its current one when I save it. Right? In other words, in my tests I only need to make sure that the AddObject method is called from Add, and that SaveChanges propagates properly. Do I need to test anything else in the controller?
Tomas Lycken
@marc, I have another follow-up question: I'm using the code-only/model-first approach, and I can't make EF map the column to be marked IDENTITY as you suggest. Could you please take a look at this post to see if you have any suggestions? http://stackoverflow.com/questions/2603148/map-a-column-to-be-identity-in-db-with-ef4-code-only
Tomas Lycken
That problem is now solved. Thanks a lot for your help on this one!
Tomas Lycken