views:

197

answers:

3

Hi All, Yes, this is my fourth day in a row to ask a question about abstracts, sorry, I'll try and go answer some questions about SQLServer to return the favor to the community. Anyway...

How can I project the results of a Linq query into a abstract base class collection? Here is my my method from my RecruiterBase abstract class (there is also a corresponding CandidateBase abstract class):

public IQueryable<CandidateBase> GetCandidates()
{
  return from candidates in db.Candidates
         where candidates.RecruiterId == this.RecruiterId
         select candidates;
}

The above method will throw a compile time error that an implicit conversion cannot be made between Candidate and CandidateBase.

modifying db.Candidates to db.Candidates.Cast() lets everything compile but I get a runtime error that no coercion operator is defined between types Candidate and CandidateBase.

I can't do: select New CandidateBase { ... } as CandidateBase since the abstract can't be implemented.

Nor can I create an explicit conversion operator between Candidate and Candidate base because it would again require me to new up my abstract

Nor can I project my results into an anonymous object and then cast to CandidateBase as I get the same runtime coercion exception between the anonymous type and CandidateBase type.

This problem came about from yesterday's question, http://stackoverflow.com/questions/1337327/problem-with-covariant-return-types-from-an-abstract-method

The answer provided by Stan R was that I was making things to complicated. I went back, simplified everything (I left the implmentation in the base and removed it from the subs) and ended up with a working GetCanidates method implemented as such:

public IQueryable<CandidateBase> GetCandidates()
{
  return (from candidates in db.Candidates
         where candidates.RecruiterId == this.RecruiterId
         select new CandidateA
         {
           CandidateId = candidates.CandidateId,
           LastName = candidates.LastName,
           RecruiterId = candidates.RecruiterId
         }).Cast<CandidateBase>();
}

The above method compiles and works, and I'm not trying to look a gift horse in the mouth, but now I have a reference to my subtype in my base type (when I project the results into CandidateA) and that just seems odd. Feel free to vote my question down if the reference to the subtype from within the base type is okay.

Thanks.

Full class defs:

public abstract class RecruiterBase
    {
        public int RecruiterId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }

        public RecruiterBase()
        {
        }

        public RecruiterBase(int id)
        {
            DataClasses1DataContext db = new DataClasses1DataContext();
            Recruiter rc = db.Recruiters.SingleOrDefault(r => r.RecruiterId == id);

            this.RecruiterId = rc.RecruiterId;
            this.FirstName = rc.FirstName;
            this.LastName = rc.LastName;
        }

        public IQueryable<CandidateBase> GetCandidates()
        {
            DataClasses1DataContext db = new DataClasses1DataContext();
            return (from candidates in db.Candidates
                    where candidates.RecruiterId == this.RecruiterId
                    select new CandidateA
                    { 
                        CandidateId = candidates.CandidateId,
                        LastName = candidates.LastName,
                        FirstName = candidates.FirstName,
                        RecruiterId = candidates.RecruiterId
                    }
                    ).Cast<CandidateBase>();
        }
    }



public abstract class TempCandidateBase
    {
        public int CandidateId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public int? RecruiterId { get; set; }

        public CandidateBase()
        {
        }

        public CandidateBase(int id)
        {
            DataClasses1DataContext db = new DataClasses1DataContext();

            Candidate candidate = db.Candidates.SingleOrDefault(c => c.CandidateId == id);

            this.CandidateId = candidate.CandidateId;
            this.FirstName = candidate.FirstName;
            this.LastName = candidate.LastName;
            this.RecruiterId = candidate.RecruiterId;
        }
    }

public class RecruiterA : RecruiterBase
    {
        public RecruiterA()
            : base()
        {
        }

        public RecruiterA(int id)
            : base(id)
        {            
        }
    }


public class CandidateA : CandidateBase
    {
        public CandidateA()
            : base()
        {
        }

        public CandidateA(int id)
            : base(id)
        {            
        }        
    }
+1  A: 

You may need to define an ICandidate interface that Candidate and CandidateBase use, then you can return an IQueryable< ICandidate> instead.

John Fisher
A: 

Justin, it is not odd..it is indeed what inheritance is intended for. Your CandidateBase class provides a base for your Candidate classes and because it is abstract it means that it provides some logic that you don't have to worry about later on. I think it is better explained with an example.

Lets say you have 2 different Candidate classes and they both want to provide some functionality, lets say GetResume().. you may create an abstract class or an interface for this..in your case you made an abstract class as such

public class CandidateBase
{
   //some logic that you might need to share between Candidates
   //such as storing Name, Age..etc

   // your abstract method
   public abstract String GetResume();
}

now lets say CandidateA gets his Resume from a specific Web Service

public class CandidateA : CandidateBase
{
   public String GetResume()
   {
       //some logic to get Resume from some web service
       return resumeStr;
   }

}

now lets say you have CandidateB and you store his resume somewhere on disk.

public class CandidateB : CandidateBase
{
   public String GetResume()
   {
       //some logic to get Resume from disk
       return resumeStr;
   }

}

at some point your code when you call GetCandidates() you won't have to worry about which type your candidate is, you can still get their resume by calling GetResume() on CandidateBase.

btw Justin, if it really bothers you, you can always cast back to CandidateA after you call GetCandidates()

IQueryable<CandidateA> candidates = recruiterClass.GetCandidates().Cast<CandidateA>();

EDIT TO ADD

Justin I think this should fix your issue, let me know.

public abstract class CandidateBase
    {
        public int CandidateId { get; set; }
        public string LastName { get; set;}
        public string FirstName { get; set;}
        public string RecruiterId { get; set; }

        //the rest of your logic
    }

public class  RecruiterBase
    {
        // Constructors declared here

        // ----HERE IS WHERE I AM BREAKING DOWN----
        public IQueryable<T> GetCandidates<T>() where T:CandidateBase, new()
        {

            DataClasses1DataContext db = new DataClasses1DataContext();
            return (from candidates in db.Candidates
                    where candidates.RecruiterId == this.RecruiterId
                    select new T()
                    { 
                        CandidateId = candidates.CandidateId,
                        LastName = candidates.LastName,
                        FirstName = candidates.FirstName,
                        RecruiterId = candidates.RecruiterId
                    }
                    )

        }
    }

you can use this like this

IQueryable<CandidateA> candidates = recruiterClass.GetCandidates<CandidateA>();

I think this is a much cleaner solution, than all those Casts :)

Stan R.
I think I'm following you Stan but it's not so much the implementation of GetResume on the base. It's more the fact that with a reference to CandidateA in GetCandidates() in CandidateBase my base class now has to have knowledge of my implementation to work. In fact without the implementation of CandidateA I would not have been able to get my abstract class to with GetCandidate() to compile. That's what's throwing. Like I said, I'm not knocking your help, I'm just trying to get past my own ignorance.
Justin
Justin, there is no implementation at the base. notice its abstract, that says that any class that inherits the base needs to implement the abstract method. when you make something abstract you are requiring other classes that inherit it to implement it. your base class does not know what the implementation of CandidateA or CandidateB is, and it doesn't care.
Stan R.
Ahh, Justin. I think I am finally starting to see your problem. You don't want to create a specific Candidate in your Recruiter class, is that what it is? For example you don't want to create CandidateA specifically in your recruiter class.
Stan R.
You're right Stan, I apologize, I've been looking at this stuff so long (and have been trying dozens of things before posting here each day) that I misread your answer yesterday and thought my implementation should have been in the base (it figures I was able to make something that didnt make sense work).
Justin
Okay, I now have an abstract method in my base, the overridden method in my sub, and I can cast it back to CandidateA from CandidateBase. Everything I'm looking at finally makes sense, and all seems to be right with the world.
Justin
Justin, my new example should also work for you. It will create a different Candidate based on the class you provide as long as it inherits from CandidateBase.
Stan R.
Stan, you are right, the option you just provided is cleaner and keeps the implementation in the base which is good but I get a compiler error "that type or namespace 'CandidateBase' could not be found". Do my classes need to be reimplmented as generics?
Justin
no they dont, make sure you don't have any misspelling in both Class name and where T:CandidateBase, make sure you are referencing the namespace you need. You do not need to implement anything with Generics, that example should work as is.
Stan R.
Yeah just a transposed character. Everything works great and this was what I was effectively trying to accomplish when I started down this road. Thanks again.
Justin
A: 

I am a bit confused...there seemed to be some edits to the original question, but when/where those edits were made is not clear. Anyway, unless I am misunderstanding something, the Candidate entity from your DataContext should derive from CandidateBase. However, based on the error you are receiving and your solution to that error, it does not appear to be doing that.

I would update your mapping to make your Candidate class derive from the CandidateBase class. Once your actual Candidate entity that your DataContext returns properly derives from the CandidateBase class, then the following should be possible:

public IQueryable<CandidateBase> GetCandidates()
{
    var candidates = from candidates in db.Candidates
                     where candidates.RecruiterId == this.RecruiterId
                     select candidates;

    var based = candidates.Cast<CandidateBase>();
    return based;
}
jrista
The code above results in a "No coercion operator is defined" exception going from the (Linq genned) Candidates class and the CandidateBase class.
Justin
You will most probably have to do a manual mapping if you want to derive Candidates from the CandidateBase class. The visual designer will regenerate the code every time, wiping any edits you may make. If you take a POCO approach and write the classes yourself, then either generate or write the mapping file manually, you will have a lot more flexibility. The above code is possible if you can achieve the following class: public class Candidate: CandidateBase { ... }
jrista