tags:

views:

229

answers:

6

In order to separate concerns, on my current project, I've decided to completely separate my DAL and BLL/Business objects in separate assemblies. I would like to keep my business objects as simple structures without any logic to keep things extremely simple. I would like if I could keep my Business Logic separate from my DAL also. So my application will tell my DAL to load my objects, my DAL will run off to the database and get the data, populate the object with the data and then pass it back to my BLL.

Question - how can I have my DAL in a separate assembly and push data into the read only fields?

  • If I set the getter as protected then inherited objects can access it which isn't really what I want as I'd be returning the inherited object types, not the original object types.
  • If I set the getter as internal, then my DAL must reside in the same assembly as my BLL which I don't want.
  • If I set the getter as public, then anyone can read/write to it when it should be read only.

Edit: I note that I can have a return type of ObjectBase but actually be returning an object or collection of objects that are derived form ObjectBase so to the outside world (outside my DAL) the properties would be read-only, but my derived types (only accessible inside my DAL) the properties are actually read/write.

+6  A: 

You can set the read only property via a constructor.

John MacIntyre
This is what I've been thinking, but I have to say that I don't like this any better either. It's putting a band-aid on the issue, it's not fixing the underlying problem.
BobTheBuilder
Immutable objects can be a pain to work with when there is no benefit. i.e. using them in a web app.
Chuck Conway
@Charles agree, you can find plenty of arguments for my answer ...
eglasius
@Charles Conway - But don't forget, the business logic layer is probably in a seperate dll which may not only be used in a webapp. Personally, when I create a BLL, I use it in other derived tools as well.
John MacIntyre
Having seen Ben's answer below, I see that use of a constructor in a controlled setting is actually pretty useful. If you use it in an inherited object you can actually encapsulate the constructor inside the DAL so that the BLL can't actually see it. So this may actually work.
BobTheBuilder
@BobTheBuilder - what exactly is the underlying problem that you see?
Jeff Sternal
@Jeff Sternal - I don't like the fact that any developer can instantiate the object and set fields that *shouldn't* be settable except under certain very special circumstances, for instance when loading data from a database or deserialization of an object. These fields are computed in the database and as such you shouldn't be able to manipulate them under normal circumstances - for the purpose of API usage, they should be immutable.
BobTheBuilder
@BobTheBuilder that still doesn't explain what made u think that setting them on the constructor isn't fixing the problem --- it allows what u just said: set some fields when loading the object that can't be changed.
eglasius
@Jeff Sternal - I was thinking about it from the wrong angle; Having the constructor on my base object isn't what I wanted. I realize now that I can have a derived object inside my DAL with a constructor that sets the properties that the BLL only has read access to. It was a matter of my mis-interpreting where this constructor should go - it should be on my derived object, not my base object.
BobTheBuilder
A: 

How about just live with it?

Implement with those guidelines, but don't add such a hard constraint in your model. Lets say you do so, but then come another req where you need to serialize it or do something else, and then you are tied with it.

As you said in other comment, you want pieces that are interchangeable ... so, basically you don't want something that's tied into specific relations.


Update 1: Perhaps "just live with it" was too simplistic, but I still have to stress out that you shouldn't go too deep into these things. Using simple guidelines, keeping your code clean and SOLID its the best you can do at the beginning. It won't get in the way of progress while refactoring when everything is more settled isn't hard.

Make no mistake, I am not at all a person that goes writing code without any thinking on it. But, I have gone with such approaches and only in a handful cases they pay off --- without any indication that you wouldn't have a similar result by going simple and evolving it.

IMHO this one does not fit into important architecture concerns that need to be addressed at the very beginning.

Pre-emptive follow up: beware if you can't trust your team into following simple guidelines. Also make sure to begin with some structure, pick a couple scenarios that set a structure in with real stuff, the team will know their way much better when there is something simple there.

eglasius
Because "just living with it" is the wrong answer. You shouldn't be able to set values on fields based on computed fields in the database. I do understand the serialization issues, but there must be patterns for these scenarios. I'm sorry, but I'm going to have to downvote "just live with it", that's just not the attitude I take to programming. I would rather do something right than just live with it. Perhaps my SoC model needs review if this can't be done.
BobTheBuilder
I can see both sides... there are times that the 'ideal' design is too extreme, a well-communicated convention can suffice in many cases. That said--provided that the repository pattern is followed for the DAL--finding a way to limit the `set` of properties to repositories seems perfectly reasonable.
STW
@BobTheBuilder - added an update.
eglasius
+1  A: 

This is a situation without a silver-bullet; the simplest options are limited or don't meet your requirements and the thorough solutions either begin to have smells or begin to veer away from simplicity.

Perhaps the simplest option is one that I haven't seen mentioned here: keeping the fields / properties private and passing them as out / ByRef parameters to the DAL. While it wouldn't work for large numbers of fields it would be simple for a small number.

(I haven't tested it, but I think it's worth exploring).

public class MyObject()
{
    private int _Id;
    public int Id { get { return _Id; } } // Read-only

    public string Name { get; set; }

    // This method is essentially a more descriptive constructor, using the repository pattern for seperation of Domain and Persistance
    public static MyObject GetObjectFromRepo(IRepository repo)
    {
        MyObject result = new MyObject();
        return repo.BuildObject(result, out _Id);            
    }
}

public class MyRepo : IRepository
{
    public MyObject BuildObject(MyObject objectShell, out int id)
    {
        string objectName;
        int objectId;

        // Retrieve the Name and Value properties
        objectName = "Name from Database";
        objectId = 42;
        //

        objectShell.Name = objectName;
        Console.WriteLine(objectShell.Id); // <-- 0, as it hasn't been set yet
        id = objectId; // Setting this out parameter indirectly updates the value in the resulting object
        Console.WriteLine(objectShell.Id); // <-- Should now be 42
    }
}


It's also worth noting that trying to keep your domain / business objects to the bare-minimum can involve more than you think. If you intend to databind to them then you'll need to implement IPropertyNotifyChanged, which prevents you from using automatically-implemented properties. You should be able to keep it fairly clean, but you will have to make some sacrifices for basic functionality.

STW
Having given this some thought, I'm not sure I like the passing of ref properties in this instance, it feels muddy and potentially confusing - even though I understand what your code is getting at, it adds unnecessary confusion for future developers that may be maintaining this code.
BobTheBuilder
It's true, ref properties always have a bit of a smell and are never intuitive. However I've been evaluating solutions to this exact problem and this is the only way I've found that explicitly prevents external objects from setting values and doesn't leave any dependencies on specific repositories.All other solutions either aren't enforced at compile-time (like setting an 'IsLocked' property on the object and throwing an exception if an attempt is made to set the property after locking the object), or they simply give up on the requirement.
STW
+1  A: 

This keeps your SoC model nicely, it doesn't add in too much complexity, it prevents writing to read-only fields and you could use a very similar model for serialization concerns. Your read-only fields can still be written to by your DAL, as could your serializer if used in a similar fashion - it means that conscious effort must be taken by a developer to write to a read-only field which prevents unintentional misuse.

Model Project

namespace Model
{
 public class DataObject
 {
  public int id { get; protected set; }
  public string name { get; set; }
 } 
}

Data Project

namespace Data
{
 class DALDataObject : DataObject
 {
  public DALDataObject(int id, string name)
  {
   this.id = id;
   this.name = name;
  }
 }
 public class Connector
 {
  public static DataObject LoadDataObject(int objectId)
  {
   return new DALDataObject(objectId, string.Format("Dummy object {0}", objectId));
  }
  public static IEnumerable<DataObject> LoadDataObjects(int startRange, int endRange)
  {
   var list = new List<DataObject>();
   for (var i = startRange; i < endRange; i++)
    list.Add(new DALDataObject(i, string.Format("Dummy object {0}", i)));

   return list;
  }
 }
}
BenAlabaster
Hmm, I'm still not really sure I like the constructor that you've used, however, it hides the constructor from the outside world for all intensive purposes so I think I like this approach. I like the fact that this model addresses serialization and keeps the SoC and I like that read-only fields are actually kept as such. Thank you +1
BobTheBuilder
@BobTheBuilder - It would be nice if you could override the protected setter in the base object, but unfortunately that's not possible. Consequently there are only two ways I know of to set the properties: via a constructor or method. The most succinct way being the constructor.
BenAlabaster
@BobTheBuilder - At least this method means that you have to consciously decide that I know these properties are protected and that I must do this in order to write to them. So it prevents unintentional errors.
BenAlabaster
A: 

In my opinion, the best way to handle this is to have the business objects and the DAL in the same assembly separated by namespace. This separates the concerns logically and allows you to use internal setters. I can't think of any benefit to separating them into their own assemblies because one is useless without the other.

Jamie Ide
That's one viewpoint I suppose. However, it is my view that the logic layer shouldn't require a data layer to be useful; the data layer shouldn't require the logic layer to be useful; the object layer shouldn't require either a logic layer or a data layer as it should just be data; but the data layer and the logic layer will both require the object layer because they need the objects to work on. Therefore the objects should be in their own assembly, the logic should be in its own assembly and the data layer should be in its own assembly.
BobTheBuilder
Put those pieces together and you've built an application that hopefully is useful. Logically separating the pieces by namespace fulfills separation of concerns. Separating by assembly makes it significantly more work to build an application and has no practical benefits.
Jamie Ide
In my opinion though, the DAL should be a completely separate assembly, this way when your data store needs to be swapped out you only need to replace a single DLL, you don't need to recompile anything else.
BobTheBuilder
A: 

Any reason you can't use reflection?

Jaco Pretorius