tags:

views:

263

answers:

11

I have an object called User, with properties like Username, Age, Password email etc.

I initialize this object in my ado.net code like:

private User LoadUser(SqlDataReader reader)
{
      User user = new User();

      user.ID = (int)reader["userID"];
      // etc
}

Now say I create a new object that inherits from User, like:

public class UserProfile : User
{
     public string Url {get;set;}
}

Now I need to create a method that will load the userprofile, so currently I am doing:

public UserProfile LoadUserProfile(SqlDataReader reader)
{
         UserProfile profile = new UserProfile();

         profile.ID = (int)reader["userID"];
         // etc. copying the same code from LoadUser(..)
         profile.Url = (string) reader["url"];
}

Is there a more OOP approach to this so I don't have to mirror my code in LoadUserProfile() from LoadUser()?

I wish I could do this:

UserProfile profile = new UserProfile();

profile = LoadUser(reader);

// and then init my profile related properties

Can something like this be done?

+5  A: 

Move the LodUser method to the User Base class and make it virtual. Then, in the UserProfile method, you override this method.

public class User
{
   public virtual void Load(SqlDataReader reader)
   {
      this.Id = reader["Id"];
      //.. whatever else
   }
}

public class UserProfile : User
{
   public string ExtraProp {get;set;}
   public override void Load(SqlDataReader reader)
   {
      base.Load(reader);
      this.ExtraProp = reader["ExtraProp"];
   }
}

Then you can just do something like:

UserProfile up = new UserProfile();
up.Load(myReader);
BFree
A: 

Make LoadUser virtual and public. In UserProfile override it.

Kirill V. Lyadvinsky
+3  A: 

i dont really understand why user profile inherits from User, logically to me it doesnt make much sense.

what makes sense to me is

public class UserProfile
{
     User user;
     public string Url {get;set;}
}

then you could have something like

public UserProfile LoadUserProfile(SqlDataReader reader)
{
         User user = LoadUser(reader);
         UserProfile profile = new UserProfile(user);

          //load profile stuff from reader...
         return profile;
}
Stan R.
To me it would make more sense if UserProfile was a member of the User class, since I am assuming a User's profile contains information about the User, not the other way around.
instanceofTom
@Tnay, yes..actually I agree with you. I just wanted to use his methods and what he needed.
Stan R.
user and userprofile were mere examples, but example I agree!
mrblah
Is an answer that changes the problem really an answer? The User/Profile example might break down, but the problem of reading values into a subclass is a legitimate question.
dahlbyk
+1  A: 

I would change it to:

 class User 
 {
      public User(SqlDataReader reader)
      {
          Initialize(reader);
      }

      protected virtual void Initialize(SqlDataReader reader)
      {
           this.ID = (int)reader["userID"];
       // etc
      }
 }

 class UserProfile : User
 {
      public UserProfile(SqlDataReader reader) : base(reader) {}

      protected override void Initialize(SqlDataReader reader)
      {
           base.Initialize(reader); // Initialize "user" variables
           this.MyProperty = (int)reader["myProperty"];
      }
 }

This way, each class initializes its own values (handled from the constructor), and you only have the code in one place.

Reed Copsey
Won't base.Initialize(reader) be called by constructor base(reader) before we get to UserProfile's Initialize?
TheVillageIdiot
@TheVillageIdiot: No, because it's virtual and overridden.
R. Bemrose
thanks @Bemrose
TheVillageIdiot
+1  A: 

First of all why is your userprofile inheriting from user. UserProfile is not User. Convinence inheritance at its worst.

epitka
Why was this down voted?
epitka
A: 

The simple answer is to override the LoadUser function and call base.LoadUser(reader) at the top of the override function definition in UserProfile class then grab the rest of the data as needed for the UserProfile class.

As a general rule, this setup should not use inheritance, but aggregation. However for the purpose of illustrating the point, it will do.

JStriedl
A: 

Make a constructor that takes a reader:

public User(DbDataReader reader) {
   ID = reader.GetInt32(reader.GetOrdinal("userID"));
   // etc
}

Usage:

User adam = new User(reader);

Then you can just make the constructor in UserProfile call the base constructor:

public UserProfile(DbDataReader reader) : base(reader) {
   Url = reader.GetString(reader.GetOrdinal("url"));
}
Guffa
+2  A: 

If you don't want your User and UserProfile objects to know about SQL (persistence ignorance = good), you could use static/extension methods that operate on an existing object:

private static User LoadFromReader(this User user, SqlDataReader reader)
{
      user.ID = (int)reader["userID"];
      // etc

      return user;
}

public UserProfile LoadFromReader(this UserProfile profile, SqlDataReader reader)
{
     ((User)profile).LoadFromReader(reader);

     profile.Url = (string) reader["url"];
     // etc

     return profile;
}

Then the calling code could look like this:

UserProfile profile = new UserProfile().LoadFromReader(reader);
dahlbyk
It's worth mentioning that you don't necessarily have to use static/extension methods, you could use a new class/interface (which would be easy to mock) instead. The important part is separating the responsibilities of being a User and knowing how to read one from SQL.
dahlbyk
Instead of static methods I would use factories with factory interfaces.Also, doing "UserProfile profile = new UserProfile().LoadFromReader(reader)" feels funny.I would prefer: UserProfile profile = ProfileReader.LoadFromReader(reader);
tster
But if ProfileReader.LoadFromReader() and UserReader.LoadFromReader() both return new instances, then you can't use the latter to hydrate a UserProfile without some generic handwaving. The factory pattern can certainly be used to create User/UserProfile, but it doesn't address the actual issue of hydrating a subclass.
dahlbyk
A: 

I would have user profile be a member of the user class

public class User
{
//...all current members of the user class...

     UserProfile profile;
}

public class UserProfile
{

     public string Url {get;set;}

}

Other than that, I would do something along the lines of Reed Copsey's answer

instanceofTom
+1  A: 

I would go with a Factory class here. It would keep things separated to address Single Responsibility Principle:. I'm also basing my answer in that the building properties of User are all accessible from UserProfile. A constructor of UserProfile could accept a User and fetch all it's properties.

    public static class UserFactory
    {
        public static User LoadUser(SqlDataReader reader)
        {
            int id = (int)reader["userID"];
            return new User(id);
        }

        public static UserProfile LoadUserProfile(SqlDataReader reader)
        {
            User user = LoadUser(reader);
            // extra properties
            string url = (string)reader["url"];
            return new UserProfile(user, url);
        }
    }
bruno conde
A: 

It kind-of sounds like you want to do something like the following:

public class User { public Guid Id { get; set; } }
public class UserProfile : User { public string Url { get; set; } }

class MyReader
{
 private T LoadUser<T>(IDataReader reader)
  where T : User, new()
 {
  T user = new T();
  user.Id = reader.GetGuid(reader.GetOrdinal("userID"));
  return user;
 }

 public UserProfile LoadUserProfile(IDataReader reader)
 {
  UserProfile profile = LoadUser<UserProfile>(reader);
  profile.Url = (string)reader["url"];
  return profile;
 }
}
csharptest.net