views:

80

answers:

2

I've been learning the ASP.NET MVC framework using the Apress book "Pro ASP.NET MVC Framework" by Steven Sanderson. To that end I have been trying out a few things on a project that I am not that familar with but are things that I thing I should be doing, namely:

  • Using repository pattern to access my database and populate my domain/business objects.
  • Use an interface for the repository so it can be mocked in a test project.
  • Use inversion of control to create my controllers

I have an MVC web app, domain library, test library.

In my database my domain items have an Id represented as an int identity column. In my domain classes the setter is internal so only the repository can set it.

So my quandries/problems are:

  1. Effectively all classes in the domain library can set the Id property, not good for OOP as they should be read-only.
  2. In my test library I create a fake repository. However since it's a different assembly I can't set the Id properties on classes.

What do others do when using a database data store? I imagine that many use an integer Id as unique identifier in the database and would then need to set it the object but not by anything else.

+1  A: 

Can't you set your objects' IDs during construction and make them read-only, rather than setting IDs through a setter method?

Or do you need to set the ID at other times. If that's the case, could you explain why?

EDIT:

Would it be possible to divorce the ID and the domain object? Does anything other than the repository need to know the ID?

Remove the ID field from your domain object, and have your repository implementations track object IDs using a private Dictionary. That way anyone can create instances of your domain objects, but they can't do silly things with the IDs.

That way, the IDs of the domain objects are whatever the repository implementation decides they are - they could be ints from a database, urls, or file names.

If someone creates a new domain object outside of the repository and say, tried to save it to your repository, you can look up the ID of the object and save it as appropriate. If the ID isn't there, you can either throw an exception to say you need to create the object using a repository method, or create a new ID for it.

Is there anything that would stop you from using this pattern?

Alex Humphrey
This solves the problem of the unit testing library. However any code in the domain library (assuming the constructor is internal) can create a class and pass in any old Id value.You could quite easily get the situation where you have in the DB a domain item with ID 5. You then get a coding mistake that says new DomainItem(5), sets all its values and then tells the repository to save it and overwriting the previous item. So effectively break OOP.
Peter
@Peter, you're right of course - I've added another suggestion - let me know what you think.
Alex Humphrey
Hmm... that sounds like an interesting idea. The only thing I would then ask is if you create a new object via a repository method how would the repository then identify it when later asked to save it since it didn't set any value on the object itself.
Peter
@Peter - on creation of a domain object the repository would add an entry to the dictionary, e.g. dictionary[domainObject] = id;. Whenever the repository is asked to save an object, it tries to find the object in the dictionary. If it can it uses the ID to save it. If it can't, it can throw an exception or create a new ID for the object. Does that sound like it would work for you?
Alex Humphrey
That does sound like a good plan. I guess I was hoping against hope that I was missing some C# 4.0 goodness. Effectively you'd need a static GetTemporaryId method which would get the next Id that could be used for new domain objects; I guess that's not so bad. The only thing then is that the stored procedure used to save would return the new Id value which can't be set on the object passed to the repository for saving leaving the only option to replace the object reference by re-creating it - which is a bit errgh!
Peter
A: 

Hi

you can use the InternalsVisibleTo attribute. It will allow the types from an assembly to be visible from the tests (provided they are in different assemblies).

Otherwise you can leave the property read-only for the external objects but in the same time have a constructor which has an ID parameter and sets the ID property. Then you can call that constructor.

Hope this helps.

Pavel Nikolov
This answers (2) which is great but leaves (1). I'm think that the repository pattern just can't work if you have an Id value for a domain object because C# doesn't have syntax support. What you'd want to say is: public int Id { get; private : IMyRepositoryInterface set; } - This would mean that the setter is private to the class except for the interface specified. So your concrete implementation of the repository would be able to set it but nothing else in the assembly. This still doesn't stop someone implementing the interface in order to fiddle, but it makes the intention very explicit.
Peter