Answered Abstract base class for common columns?

  • Wednesday, May 30, 2007 1:38 PM
     
     

    Hello,

    I have one question regarding LINQ inheritance. It is very common scenario to have some common columns that are used in all tables(objects), so it is very usefully to define some base object where this common columns are stored.

     

    Example:

     

    public abstract class EntityBase

    {

          int m_iID = 0;

     

          [Column(IsPrimaryKey = true)]

          public int ID

          { // ..Implentation     }

    }

     

    [Table]

    public class Address : EntityBase

    {

          string m_strStreet = "";

          string m_strZIP = "";

     

          [Column]

          public string Street

          { // ..Implentation     }

          [Column]

          public string ZIP

          { // ..Implentation     }

    }

     

    [Table]

    public class Person : EntityBase

    {

          string m_strFirstName = "";

          string m_strLastName = "";

     

          [Column]

          public string FirstName

          { // ..Implentation     }

          [Column]

          public string LastName

          { // ..Implentation     }

    }

     

    But this doesn’t work. When I run, I get error:

    Data member 'Int32 ID' of type 'TesterApp.EntityBase' is not part of the mapping for type 'Person'. Is the member above the root of an inheritance hierarchy?

     

    I can see only 2 workarounds here:

    1.) Add all columns from EntityBase to all other objects. This is not a solution, because this is what I want to solve.

    2.) Use InheritanceMapping, but then I must add Discriminator column to all objects and the value of discriminator will be the same for particular table.

     

    I really don’t see any reason why this is not working because here is still 1 to 1 mapping (each object has exactly one distinct table).

    Any hint?

     

    Thanks,

    edvin

     

All Replies

  • Monday, June 04, 2007 1:29 AM
     
     Answered

    The current implementation of LINQ to SQL requires that the table and inheritance mapping be defined in the base class rather than in child consuming classes. I can understand your frustration with this situation, but that is how it is designed. I suspect it requires this due to the use of reflection. Consider in your situation where you try to instantiate a person object and set the ID property, reflection would need to know that ID is defined in the EntityBase class rather than the consuming Person class. It becomes trickier when you try to use the Storage attribute and set the private field rather than the private property. In that case, reflection would not only need a handle of the EntityBase class, but also the m_iId field. To avoid worrying about this complexity, the LINQ to SQL team opted to work from the bottom up rather than the top down. (Matt, please correct me if I'm wrong).

     

    Jim Wooley

    http://linqinaction.net

    http://devauthority.com/blogs/jwooley

  • Wednesday, June 06, 2007 7:49 AM
     
     

    Hi,

    thanks for your reply. But I don’t think that this is a big implementation problem. Until now I have used my own object to relation database library and this was (and is) working without any problems. When you instantiate Person object you know that it belongs to the Person table (Table attribute defined on the Person object. In my library I have virtual method GetTableName, but it is the same) and then you just check all field/properties with Column attribute. Even for example, if EntityBase is not abstract and it has its own table it will also work. EntityBase will be saved to EntityBase table with only one attribute (ID). When you have a project with more then 10 tables this can be really useful.

     

    I must say that I’m quite new to LINQ, I took some days reading information on net and it’s look perfect to me, but when I start a new project in real life I have found a lot of limitation, some thing are so annoying that a can only wait for next release to see if there will be any improvement. Maybe ADO.Entity is more powerfull, but it will come out after Orcas, so I supposed that it is still in early release and I’m just afraid to touch it. .....and when I think about this, my first question is why 2 technologies for the same problem….

     

    edvin

  • Saturday, June 23, 2007 9:46 PM
     
     

    If you use an xml file for mapping instead of the attributes then you can create an abstract base class with common members on it. To generate the .xml file you can use SqlMetal as follows...

     

    sqlmetal /server:{your server} /database:{your database} 

             /map:{something}.xml /code:{something}.cs

     

    You'll end up with an XML file that describes the mapping between database tables/columns and classes/members, and a .cs file with the entities created for you.

     

    Hope this helps,

     

    Morgan

  • Friday, July 20, 2007 10:19 AM
     
     

    Hi,

    thanks for your reply. I have created very simple project and try to use external mapping but it doesn’t work. First I used files directly generated by SqlMetal and it was working. Then I have created base class and I move _ID field member and ID property to base class. When I run the project I get  Unexpected null 'MetaDataMember'.  If I move to base class only _ID field member it seems to work, but I didn’t done a lot of tests, because this is not what I want. I would like to move property and field member, so that I’m able to remove a lot of duplicated code and make business  objects to look  “more like real objects” not only a table copy.

     

    I hope this will work in next release???

    edvin

  • Thursday, December 04, 2008 2:45 PM
     
     

     

    Try it:

     

    public class EntityBase

    {

          int m_iID = 0;

     

          [Column(IsPrimaryKey = true)]

          public virtual int ID

          { // ..Implentation     }

    }

     

    [Table]

    public class Address : EntityBase

    {

          string m_strStreet = "";

          string m_strZIP = "";

     

     [Column(IsPrimaryKey = true)]

          public override int ID

     {

    get { return base.ID; }

    set { base.ID = value; }

     }

     

          [Column]

          public string Street

          { // ..Implentation     }

          [Column]

          public string ZIP

          { // ..Implentation     }

    }

     

    [Table]

    public class Person : EntityBase

    {

          string m_strFirstName = "";

          string m_strLastName = "";

     

     [Column(IsPrimaryKey = true)]

          public override int ID

     {

    get { return base.ID; }

    set { base.ID = value; }

     }

     

          [Column]

          public string FirstName

          { // ..Implentation     }

          [Column]

          public string LastName

          { // ..Implentation     }

    }