简体   繁体   中英

How to have multiple lists in an Entity of the same type?

I have an entity that needs to have multiple lists of another entity. Each of these lists will be comprised of the same type of entity however, and this seems to confuse the framework. I've read this question:

Multiple collections of same type in entity framework

and followed the suggestion of differentiating the different lists by inheriting different types for each of the lists of items. This does not seem to do anything though.

The error I get is:

Exception:Thrown: "Invalid column name 'ApprovalStage_Id1'. Invalid column name 'ApprovalStage_Id2'. Invalid column name 'ApprovalStage_Id3'." (System.Data.SqlClient.SqlException) A System.Data.SqlClient.SqlException was thrown: "Invalid column name 'ApprovalStage_Id1'. Invalid column name 'ApprovalStage_Id2'. Invalid column name 'ApprovalStage_Id3'." Time: 2/9/2015 3:22:05 PM Thread:Worker Thread[11116]

And here are my entities. It's a bit dense, but basically the main object is ComplexApprovalProcess , which has some number of ApprovalStages in a single list (that's all fine). The problem comes in with the fact that each ApprovalStage has three lists of Approver , 'Approvers', 'Viewers', and 'Advisors'. When Entity tries to save these entities it throws the error above. Like I said, I tried differentiating these by inheriting three other classes from Approver , so as you can see they are now collections of ReqApprover , Advisor and Viewer , but it still throws the error above just like it did before. Any ideas why Entity is getting confused on this? Each Approver entity should just have a reference back to the ApprovalStage it belongs to, but Entity seems to think it should be referencing three different ApprovalStages and tries to dynamically find those columns, which don't exist. Thanks.

The Entities:

public class ComplexApprovalProcess
{
    [Key]
    public long Id { get; set; }

    [InverseProperty("ApprovalProcessId")]
    public List<ApprovalStage> Stages { get; set; }

    [ForeignKey("Form")]
    public long FormId { get; set; }

    public int CurrentStage { get; set; }

    public FormBase Form { get; set; }

    bool Approved { get; set; }

    bool Denied { get; set; }

    private bool CheckCompleted() {

        foreach (ApprovalStage stage in this.Stages)
        {
            if (stage.Completed == false)
            {
                //if any stage is incomplete, the process is not complete
                return false;
            }
        }
        //no stages incomplete means all stages complete
        return true;
    }

    private bool AdvanceStage()
    { 
        //check the completion condition of the current stage, if completed, advance to next stage
        ApprovalStage current = Stages.Where(m => m.StageOrder == this.CurrentStage).FirstOrDefault();

        if (current != null)
        {
            //check if stage is completed
            if (current.CheckCompletion())
            {
                //check if stage is approved
                if (current.Approved)
                {
                    //check if process contains additional stages
                    if (this.Stages.Count > this.CurrentStage)
                    { 
                        //Move to next stage
                        this.CurrentStage += 1;
                        ApprovalStage next = Stages.Where(m => m.StageOrder == this.CurrentStage).FirstOrDefault();

                        if (next != null)
                        {
                            next.StartStage();
                        }
                        else
                        {
                            throw new Exception("Huh?");
                        }
                    }
                }
            }
        }
        else
        {
            throw new Exception("Wut");
        }
        return false;
    }

    public static ComplexApprovalProcess CreateCheckRequestApprovalProcess(FormBase form)
    {
        UsersModel user = null;

        ComplexApprovalProcess process = new ComplexApprovalProcess();

        using (TechnologyProjectPlanContext db = new TechnologyProjectPlanContext())
        {
            int id = SessionVar.Get<int>(SessionVar.USERID);

            user = db.UsersModels.Where(m => m.Id == id).FirstOrDefault();
        }

        process.Form = form;

        ApprovalStage InitialReview = new ApprovalStage();
        InitialReview.StageOrder = 1;
        InitialReview.Approvers = new List<ReqApprover>();
        InitialReview.Advisors = new List<Advisor>();
        InitialReview.Viewers = new List<Viewer>();
        InitialReview.Form = form;

        InitialReview.ApprovalProcess = process;
        InitialReview.Approvers.Add(new ReqApprover(user, form, InitialReview));
        InitialReview.Advisors.Add(new Advisor(user, form, InitialReview));
        InitialReview.Viewers.Add(new Viewer(user, form, InitialReview));
        InitialReview.StageName = "Initial Review";

        ApprovalStage MiddleApproval = new ApprovalStage();
        MiddleApproval.StageOrder = 2;
        MiddleApproval.Approvers = new List<ReqApprover>();
        MiddleApproval.Advisors = new List<Advisor>();
        MiddleApproval.Viewers = new List<Viewer>();
        MiddleApproval.Form = form;

        MiddleApproval.ApprovalProcess = process;
        MiddleApproval.Approvers.Add(new ReqApprover(user, form, MiddleApproval));
        MiddleApproval.Advisors.Add(new Advisor(user, form, MiddleApproval));
        MiddleApproval.Viewers.Add(new Viewer(user, form, MiddleApproval));
        MiddleApproval.StageName = "Middle Approval";


        ApprovalStage FinalApproval = new ApprovalStage();
        FinalApproval.StageOrder = 3;
        FinalApproval.Approvers = new List<ReqApprover>();
        FinalApproval.Advisors = new List<Advisor>();
        FinalApproval.Viewers = new List<Viewer>();
        FinalApproval.Form = form;

        FinalApproval.ApprovalProcess = process;
        FinalApproval.Approvers.Add(new ReqApprover(user, form, FinalApproval));
        FinalApproval.Advisors.Add(new Advisor(user, form, FinalApproval));
        FinalApproval.Viewers.Add(new Viewer(user, form, FinalApproval));
        FinalApproval.StageName = "Final Approval";

        process.Stages = new List<ApprovalStage>();
        process.Stages.AddRange(new ApprovalStage[] { InitialReview, MiddleApproval, FinalApproval });

        //set default values
        process.Approved = false;
        process.Denied = false;

        process.CurrentStage = 1;
        process.Stages[0].StartStage();
        return process;
    }

    public void SaveToDb()
    {
        //make sure we have at least one stage and either a form reference (new form) or form id (old form) before moving forward
        if ((Stages != null && Stages.Count > 0) && (Form != null || FormId > 0))
        { 
            using (TechnologyProjectPlanContext  db = new TechnologyProjectPlanContext())
            {
                //first we have to save the process to get an Id
                //copy stages out so we can save without the fuss
                List<ApprovalStage> stages = this.Stages;

                this.Stages = null;

                db.ComplexApprovalProcesses.Add(this);
                db.SaveChanges();

                //'this' now has an Id

                //ok let's work it out from the bottom to the top, first separate out approvers from stages and save:
                foreach (ApprovalStage stage in stages)
                {
                    ICollection<ReqApprover> approvers = stage.Approvers;
                    ICollection<Advisor> advisors = stage.Advisors;
                    ICollection<Viewer> viewers = stage.Viewers;

                    stage.FormId = stage.Form.Id;
                    stage.Form = null;

                    stage.Approvers = null;
                    stage.Advisors = null;
                    stage.Viewers = null;

                    stage.ApprovalProcessId = this.Id;

                    db.ApprovalStages.Add(stage);
                    db.SaveChanges();

                    //stage now has an id;
                    //iterate through each set of approvers and save
                    foreach (Approver approver in approvers)
                    {
                        approver.FormId = stage.FormId;
                        approver.UserId = approver.User.Id;
                        approver.ApprovalStage_Id = stage.Id;

                        approver.Form = null;
                        approver.User = null;
                        approver.Stage = null;

                        db.Approvers.Add(approver);
                        db.SaveChanges();
                    }

                    foreach (Advisor approver in advisors)
                    {
                        approver.FormId = stage.FormId;
                        approver.UserId = approver.User.Id;
                        approver.ApprovalStage_Id = stage.Id;

                        approver.Form = null;
                        approver.User = null;
                        approver.Stage = null;

                        db.Approvers.Add(approver);
                    }

                    foreach (Viewer approver in viewers)
                    {
                        approver.FormId = stage.FormId;
                        approver.UserId = approver.User.Id;
                        approver.ApprovalStage_Id = stage.Id;

                        approver.Form = null;
                        approver.User = null;
                        approver.Stage = null;

                        db.Approvers.Add(approver);
                    }
                    db.SaveChanges();
                }
            }
        }
    }
}

public class ApprovalStage
{
    //Each stage requires at least one approver
    [Key]
    public long Id { get; set; }

    [ForeignKey("ApprovalProcess")]
    public long ApprovalProcessId { get; set; }

    [ForeignKey("Form")]
    public long FormId { get; set; }

    public FormBase Form { get; set; }

    public ComplexApprovalProcess ApprovalProcess { get; set; }

    public ICollection<ReqApprover> Approvers { get; set; } //These users are required to approve before the form can move to the next stage.

    public ICollection<Advisor> Advisors { get; set; } //These users can see the form and approve at this stage, but they are not required.

    public ICollection<Viewer> Viewers { get; set; } //These users can see the form, but cannot approve

    public string StageName { get; set; } //Name of stage e.g. Review, Final Approval, etc.  Gives a custom feel?

    public int StageOrder { get; set; }

    public bool Completed { get; set; }

    public bool Approved { get; set; }

    public bool Denied { get; set; }

    public bool CanAbstain { get; set; }

    public ApprovalStage()
    { 
        this.Approved = false;
        this.Denied = false;
        this.Completed = false;
    }
}

public class Approver
{
    [Key]
    public long Id { get; set; }

    [ForeignKey("User")]
    public int UserId { get; set; }

    public UsersModel User { get; set; }

    [ForeignKey("Form")]
    public long FormId { get; set; }

    public FormBase Form { get; set; }

    [ForeignKey("Stage")]
    public long ApprovalStage_Id { get; set; }

    public ApprovalStage Stage { get; set; }

    public bool Approved { get; set; }

    public bool Denied { get; set; }

    public bool Abstain { get; set; }

    public Approver() { }

    public Approver(UsersModel user, FormBase form, ApprovalStage stage)
    {
        this.Stage = stage;
        this.User = user;
        this.Approved = false;
        this.Denied = false;
        this.Abstain = false;
    }
}

Ok so I figured out the error I was making. I didn't think about the fact that once placed into the database, the different lists of the same object type would have no knowledge of which list they originated from. That is, an Approver, Advisor, and Viewer would all look the same to the framework after they've been inserted. I kept running into errors with the inheritance of Approvers, so I simply made 3 different classes and tables and copied the design to each of them and it works perfectly. Can't believe I spent so much time on this...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM