Results 1 to 6 of 6

Thread: General approach to databases

Threaded View

  1. #1

    Thread Starter
    PowerPoster
    Join Date
    Apr 2007
    Location
    The Netherlands
    Posts
    5,070

    General approach to databases

    Hi,

    I am often asked to create a very simple database front-end for friends or family, and the structure of the database (and application) is usually very similar.

    Right now I am always creating a class that handles all database interactions with some shared functions. For example, there's a function to run a general query and return a table, a function to save/load various items (depending on the application of course). The point is: the code is nearly always the same except for some slight variations.

    For example, I was asked to create an application that manages a load of pictures. So, I wrote the class and made a function to load an image (which in turn calls the function to execute a query with the correct SQL query). I made a function to save an image to the database, etc.
    A short while later, I was asked to create an application that manages a load of documents. The principle was exactly the same except instead of images there was now documents...

    To cut a long story short; I am getting tired of writing (or copy/pasting) the same class every time, I'm sure there must be a way to get this 'automated'.



    So I've been trying this for some time, but haven't even gotten close.

    Right now, I have a class (Database) that I simply pass the location of an Access database file to. The important part is that it maintains an OleDbConnection object, and has some functions like ExecuteScalar, ExecuteNonQuery, etc, which basically accept a string and run the query.


    My goal now is that, for each similar application, I can create a class that is simply a 'container' of all attributes of the corresponding database table, which it exposes through properties.
    For example, suppose I have a simple table 'Person' with attributes ID, NAME and AGE, the class would look like this:
    vb.net Code:
    1. Public Class PersonEntity
    2.  
    3.     Private _Id As Integer
    4.     Public Property Id() As Integer
    5.         Get
    6.             Return _Id
    7.         End Get
    8.         Set(ByVal value As Integer)
    9.             _Id = value
    10.         End Set
    11.     End Property
    12.  
    13.     Private _Name As String
    14.     Public Property Name() As String
    15.         Get
    16.             Return _Name
    17.         End Get
    18.         Set(ByVal value As String)
    19.             _Name = value
    20.         End Set
    21.     End Property
    22.  
    23.     Private _Age As Integer
    24.     Public Property Age() As Integer
    25.         Get
    26.             Return _Age
    27.         End Get
    28.         Set(ByVal value As Integer)
    29.             _Age = value
    30.         End Set
    31.     End Property
    32.  
    33. End Class
    Each property corresponds to a table attribute. I suppose I need some way to connect the properties with the attributes; perhaps via a custom property Attribute like so?
    vb.net Code:
    1. Private _Name As String
    2.     <DatabaseField("NAME")> _
    3.     Public Property Name() As String
    4.         Get
    5.             Return _Name
    6.         End Get
    7.         Set(ByVal value As String)
    8.             _Name = value
    9.         End Set
    10.     End Property

    I created an interface IEntity that requires the class to implement at least an ID property (more might be added later).


    Anyway, besides this class (there might be many of these classes of course), I will also need a 'manager' class that manages loading and saving of instances of these classes.

    My 'vision' is that I could do this:
    vb.net Code:
    1. Dim db As New Database("file here")   'my Database class
    2. Dim pm As New PersonManager(db)
    3.  
    4. Dim pe As PersonEntity = pm.LoadById(3)
    5. pe.Name = "John"
    6. pe.Age = 35
    7. pm.Save(pe)
    what this should do is create a new PersonManager, which manages PersonEntities. Then the LoadById function returns a new PersonEntity object. This object will have its properties populated by running a database query ("select * from Persons where ID = 3" or whatever). Then, the Name and Age property are modified, and the new object is saved back to the database.


    Obviously this should work without me having to write the LoadById and Save functions manually for each and every Manager. The goal is that I have a base manager (possibly a generic class?) which would automatically implement the load and save functions.
    The base manager would look something like this
    vb.net Code:
    1. Public MustInherit Class TableManager(Of T As IEntity)
    2.  
    3.     Public Sub New(ByVal db As Database)
    4.         _Database = db
    5.     End Sub
    6.  
    7.     Private _Database As Database
    8.     Public ReadOnly Property Database() As Database
    9.         Get
    10.             Return _Database
    11.         End Get
    12.     End Property
    13.  
    14.     Public Function Load(ByVal id As Integer) As T
    15.         '???
    16.     End Function
    17.  
    18.     Public Sub Save(ByVal entity As T)
    19.         '???
    20.     End Sub
    21.  
    22. End Class
    (Note that T is constrained, it must implement IEntity)

    And the PersonManager class would then simply be
    vb.net Code:
    1. Public Class PersonManager
    2.     Inherits TableManager(Of PersonEntity)
    3.  
    4.     Public Sub New(ByVal db As Database)
    5.         MyBase.New(db)
    6.     End Sub
    7.  
    8. End Class


    The obvious problem: what to do with the Load and Save methods?

    I am running into a few 'major' problems when trying to implement them and starting to think it may not even be possible*...

    This is how I imagined it, but it's obviously not going to work that easy:
    vb.net Code:
    1. Public Function Load(ByVal id As Integer) As T
    2.         Dim t As New T
    3.  
    4.         Dim reader As IDataReader = Me.Database.ExecuteReader("select * from ??? where ID = " & id)
    5.  
    6.         While reader.Read
    7.             t.Id = id
    8.             t.??? = ???
    9.             t.??? = ???
    10.         End While
    11.  
    12.         Return t
    13.     End Function

    Problems:
    1. I don't know the table name. This could be solved by supplying the table name in the constructor I suppose.
    2. a) I don't know which attributes the reader will be reading except the ID
    2. b) I don't know which properties T has.
    (3. I cannot create an instance of T, but I am sure I did this before; I just need to constrain T so that it must have a constructor I believe?)




    Now, for problem 2... The Entity classes (such as PersonEntity) have a list of properties which correspond to database fields. The name of those fields is listed in the Attribute above the property. I am pretty sure I could use reflection or something to retrieve a list of the properties and their attributes. Then, I would have a list of the properties and theoretically I should be able to set their values. I can load the values from the database because I also know the field names.

    But how to do this?


    I suppose I could also do it without generics, where the Load function returns an IEntity object (and the save function accepts one), but the problem then is even worse: I cannot create an instance of IEntity, but I cannot know which type I should create a new instance of.
    I could supply the type in the constructor though, so that would yield something like this
    vb.net Code:
    1. Public MustInherit Class TableManager
    2.  
    3.     Private _t As Type
    4.  
    5.     Public Sub New(ByVal db As Database, ByVal t As Type)
    6.         _Database = db
    7.         _t = t
    8.     End Sub
    9.  
    10.     Private _Database As Database
    11.     Public ReadOnly Property Database() As Database
    12.         Get
    13.             Return _Database
    14.         End Get
    15.     End Property
    16.  
    17.     Public Function Load(ByVal id As Integer) As IEntity
    18.         Dim t As New 'instance of type _t   how???
    19.  
    20.         Dim reader As IDataReader = Me.Database.ExecuteReader("select * from ??? where ID = " & id)
    21.  
    22.         While reader.Read
    23.             t.Id = id
    24.             t.??? = ???
    25.             t.??? = ???
    26.         End While
    27.  
    28.         Return t
    29.     End Function
    30.  
    31.     Public Sub Save(ByVal entity As IEntity)
    32.         '???
    33.     End Sub
    34.  
    35. End Class
    and the PersonManager would be
    vb.net Code:
    1. Public Class PersonManager
    2.     Inherits TableManager
    3.  
    4.     Public Sub New(ByVal db As Database)
    5.         MyBase.New(db, GetType(PersonEntity))
    6.     End Sub
    7.  
    8. End Class


    I think that's enough for a single post... I'm sure many more problems will arise, but I hope someone can at least give me some ideas.



    *However, I am sure it is possible in some way. I used to do a little work for some company and they used this exact 'structure' (that's where I got the idea in the first place). I could ask them how they did it I suppose, but I doubt they would tell me. I could use Reflector to take a peek myself but somehow that feels much more like stealing then asking advice on a forum. If you disagree please let me know. I wouldn't feel comfortable stealing their code and using it as my own. I don't have this problem though if I merely explain the idea and ask others for help, right?

    They used the last approach I mentioned though, passing the type to the constructor, without generics. Not sure if that matters.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width