开发者

How should I design my object model so that my DAL can populate read-only fields?

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.


You can set the read only property via a constructor.


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.


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;
        }
    }
}


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.


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜