views:

639

answers:

4

Ok, so I'm trying to make a nice superclass for data-access objects that can generate a tsql query to search all of the subclass's public string properties. I want to use reflection to get the type of the subclass and then iterate through all of the public string properties on the object, since those property names are the same as the database column names. Then I can use those property names to generate a tsql query.

[caveat: hey, id rather use nhibernate but no way I could convince these guys to use that]

[Also I could solve this with generics, but I think they find generics scary because they are VB.net guys, (sorry if I hurt your feelings VB.net peeps ;( )]

Ok so the base object is something like this:

public abstract class RepositoryBase
{
   public static IList<RepositoryBase> Search()
   {
        //get all public properties of the inheriting subclass
        // I already have the rest of the search code
   }
}

Is this even possible, or advisable?

While I was typing this, I was like "screw it, I'll just do it with generics".

Thanks for reading!

+1  A: 

I think most repository patterns use interfaces rather then abstract classes like this...

 public class SqlRepository : IRepository {

        DB _db;

        public SqlRepository(DB dataContext) {
            //override the current context
            //with the one passed in
            _db = dataContext;

        } 

        public IQueryable<RepositoryBase> Search() {

           ...

I don't think that I have ever seen the pattern written that way. I suppose it's possible but I don't think you will be able to accomplish what you think yuo're trying to accomplish. Check it out... http://social.msdn.microsoft.com/Forums/en-US/csharplanguage/thread/19a968ce-7d8b-4cf9-b0f0-292464f1e708/

Is there any reason you can't use interfaces instead?

matt_dev
Well, I could use an interface, but I'd rather do it one abstract base class rather than redefining it in each class. That way I can just test the base class code, and have it in a centralized location. Though I may use a combination of interfaces and abstract base classes to solve it though.
Mark Rogers
+1  A: 

Well, assuming you still want an answer: no. In the static method you're not in the context of an object. You have no idea who has called you (without constructing a StackTrace) and even if you did, you don't know that the caller is the object for whom properties should be enumerated.

If it were an instance method, you could just call GetType().

HTH, Kent

Kent Boogaart
+1  A: 

When you call a static method "via" a derived class, the compiler resolves the method so the IL actually contains the base class. For instance:

public class Base
{
    static void Foo() {}
}

public class Derived : Base {}

class Test
{
    static void Main()
    {
        Derived.Foo();
    }
}

The call in Main will actually end up compiled as Base.Foo() in the IL (as least when compiled from C#). So you can't tell at execution time what the original call was.

It does sound like generics is the way to go.

Jon Skeet
A: 

I am just working (VB) in what you propose. This code works (translated from spanish so it could contain typos): For example it could be used this way:

Dim CustList As List(Of CUSTOMER) = CUSTOMER.SELEC("Name = 'Peter'", "Name DESC")
Dim myOrders As List(Of ORDER) = CustList(0).Orders
CustList(0).Name = "John"


Public Interface ITables 'used solely to unify all the table types
    '    ReadOnly Property PrimaryKey() As String is better to shadow it from SuperClass TableType
End Interface

Public Class TableType(Of T As ITables)'this T is what you are looking for
    Public PrimaryKey As String
    Public Shared Function SELEC(Optional ByVal sWhere As String = "", Optional ByVal sOrderBy As String = "") As List(Of T)
        'shared/static method to fill and return a typed List with the DB rows
        'can be called using for example Type.SELEC()
        Dim oConn As New OdbcConnection(My.Settings.ConnectionString)
        Dim oComm As OdbcCommand = oConn.CreateCommand
        oComm.CommandText = "SELECT * FROM " & GetType(T).Name
        If sWhere.Length > 0 Then oComm.CommandText &= " WHERE " & sWhere : If sOrderBy.Length > 0 Then oComm.CommandText &= " ORDER BY " & sOrderBy
        Dim oListRet As New List(Of T)
        oConn.Open()
        Dim oDR As OdbcDataReader = oComm.ExecuteReader
        Dim oneRow As T
        Do While oDR.Read
            oneRow = Activator.CreateInstance(GetType(T))
            For i = 0 To oDR.FieldCount - 1
                Dim value = oDR.Item(i)
                If TypeOf value Is DBNull Then value = Activator.CreateInstance(oDR.GetFieldType(i)) ' default value
                oneRow.GetType.GetProperty(oDR.GetName(i)).SetValue(oneRow, value, Nothing)
            Next
            oListRet.Add(oneRow)
        Loop
        oDR.Close()
        oConn.Close()
        oConn.Dispose()
        Return oListRet
    End Function

    Public Function UPDATE(Optional ByVal sWhere As String = "") As Integer
        'not shared but one for all tables
        'working on this, almost finished
    End Function
    Shared Function fnPropAttribute(ByVal oProp As PropertyInfo, ByVal sAttrName As String) As String
        'working on this. Returns for example the value of the attribute 'Category' of a field
        Dim attributes As AttributeCollection = TypeDescriptor.GetProperties(oProp.DeclaringType)(oProp.Name).Attributes
        Dim myAttribute As CategoryAttribute = CType(attributes(GetType(need to know wth to put here)), CategoryAttribute)
        Return myAttribute.Category
    End Function
End Class 'TableType


Public Class Tables
    Public Class CUSTOMER
        Inherits TableType(Of CUSTOMER)
        Implements ITables
        Public Shadows Const PrimaryKey As String = "idCust"

        'this returns the List(Of Orders) with my idCust
        Public ReadOnly Property ORDERs() As List(Of ORDER)
            Get
                Return ORDER.SELEC("idCust = " & Me.idCust)
            End Get
        End Property

        Dim _idCust As Integer

        'this field's Attributes will be used in UPDATE, INSERT, etc
        'Category 'Columns' is to distingish between DB fields and other possible properties
        'Description is the field's ODBCType
        <Category("Columns"), Description("CHAR")> _
        Public Property idCust() As Integer
            Get
                Return _idCust
            End Get
            Set(ByVal value As Integer)
                _idCust = value
            End Set
        End Property

        Dim _Name As String
        <Category("Columns"), Description("CHAR")> _
        Public Property Name() As String
            Get
                Return _Name
            End Get
            Set(ByVal value As String)
                _Name = value
            End Set
        End Property
        'etc...
    End Class 'Customer
    Public Class ORDER
        Inherits TableType(Of ORDER)
        Implements ITables
        Public Shadows Const PrimaryKey As String = "idOrder"

        Dim _idOrder As Integer
        <Category("Columns"), Description("CHAR")> _
        Public Property idOrder() As Integer
            Get
                Return _idOrder
            End Get
            Set(ByVal value As Integer)
                _idOrder = value
            End Set
        End Property

        Dim _idCust As Integer
        <Category("Columns"), Description("CHAR")> _
        Public Property idCust() As Integer
            Get
                Return _idCust
            End Get
            Set(ByVal value As Integer)
                _idCust = value
            End Set
        End Property

        Dim _Artic As String
        <Category("Columns"), Description("CHAR")> _
        Public Property Artic() As String
            Get
                Return _Artic
            End Get
            Set(ByVal value As String)
                _Artic = value
            End Set
        End Property
        'etc...
    End Class 'Order
End Class 'Tables
Ivan Ferrer Villa