如何将Linq结果投影到Abstract构造函数中?

时间:2009-08-27 18:39:40

标签: c# linq-to-sql abstract

是的,这是我连续第四天询问关于摘要的问题,对不起,我会尝试回答一些关于SQLServer的问题,以便回馈社区。总之...

如何将Linq查询的结果投影到抽象基类集合中?这是我的RecruiterBase抽象类的方法(还有一个相应的CandidateBase抽象类):

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

上述方法将抛出编译时错误,即无法在Candidate和CandidateBase之间进行隐式转换。

将db.Candidates修改为db.Candidates.Cast()可以编译所有内容,但是我遇到运行时错误,在Candidate和CandidateBase类型之间没有定义强制运算符。

我不能这样做:选择New CandidateBase {...}作为CandidateBase,因为摘要无法实现。

我也不能在候选人和候选人基础之间创建一个显式转换运算符,因为它会再次要求我新建我的摘要

我也无法将结果投射到匿名对象中,然后在匿名类型和CandidateBase类型之间获得相同的运行时强制异常时转换为CandidateBase。

这个问题来自昨天的问题, Problem with Covariant return types from an abstract method

Stan R提供的答案是我让事情变得复杂。我回去了,简化了所有内容(我将基础中的文件遗留下来并将其从子文件中删除)并最终实现了一个如此实现的GetCanidates方法:

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>();
}

上面的方法编译和工作,我不是想在口中看一个礼物马,但现在我在我的基本类型中引用了我的子类型(当我将结果投影到CandidateA中时)看起来很奇怪如果对基类型中的子类型的引用是可以的,请随意向我投票。

感谢。

全班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)
        {            
        }        
    }

3 个答案:

答案 0 :(得分:1)

您可能需要定义Candidate和CandidateBase使用的ICandidate接口,然后您可以返回IQueryable&lt; ICandidate&GT;代替。

答案 1 :(得分:0)

贾斯汀,这并不奇怪......这确实是继承的目的。您的CandidateBase类为您的候选类提供了一个基础,因为它是抽象的,这意味着它提供了一些您以后不必担心的逻辑。我认为用一个例子可以更好地解释。

假设你有两个不同的候选类,他们都想提供一些功能,比如说GetResume()..你可以为它创建一个抽象类或接口。在你的情况下你创建了一个抽象类

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();
}

现在让我们说CandidateA从特定的Web服务中获取他的简历

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

}

现在假设你有CandidateB并将他的简历存储在磁盘上。

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

}

在某些时候,当你调用GetCandidates()时你的代码你不必担心你的候选人是哪种类型,你仍然可以通过在CandidateBase上调用GetResume()获得他们的简历。

顺便说一下Justin,如果它真的困扰你,你可以在调用GetCandidates()

之后再回到CandidateA
IQueryable<CandidateA> candidates = recruiterClass.GetCandidates().Cast<CandidateA>();

编辑添加

贾斯汀我认为这应该解决你的问题,请告诉我。

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
                    }
                    )

        }
    }

你可以像这样使用

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

我认为这是一个比所有Casts更清晰的解决方案:)

答案 2 :(得分:0)

我有点困惑......似乎对原始问题进行了一些编辑,但是这些编辑的时间/地点尚不清楚。无论如何,除非我误解了某些内容,否则DataContext中的候选实体应该来自CandidateBase。但是,根据您收到的错误以及您对该错误的解决方案,它似乎没有这样做。

我会更新你的映射,使你的Candidate类派生自CandidateBase类。一旦您的DataContext正确返回的实际候选实体派生自CandidateBase类,那么以下内容应该是可能的:

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;
}