Вставить / обновить с помощью сервисов RIA. - PullRequest
1 голос
/ 29 июля 2010

Я создаю приложение Silverlight 4 с использованием Entity Framework, RIA Services и инструментария MVVM-Light.Приложение работает со сложным графом объектов, который имеет следующую структуру:

  • Задание 1 -> * Ресурсы
  • Задание 1 -> * Рабочие планы
  • Рабочий план 1-> * WorkplanItems
  • Ресурс 1 -> * Назначение
  • WorkplanItems 1 -> * Назначение

Я хотел бы иметь возможность загрузить задание (используя ВключитьАтрибуты / директива) и это работает нормально.загрузить полный график из корня конкретной работы.Однако, когда я отправляю изменения обратно, я получаю сообщение об ошибке

Entity for operation '0' has multiple parents

Из этого сообщения я понял, что атрибут Composition в моих метаданных должен позволять мне отправлять этов виде полного графика, а затем корректно обработать обновление всех объектов на сервере за один прием из моего приложения silverlight.Моя цель - не отправлять изменения с каждым изменением, а позволить пользователю вносить изменения в задание и связанные с ним части, а затем отправлять их или отменять изменения.

Пожалуйста, дайте мне знатьесли вам известны какие-либо проблемы, которые я пропустил в этом?Вот метаданные, как я настроил:

// The MetadataTypeAttribute identifies AssignmentMetadata as the class
// that carries additional metadata for the Assignment class.
[MetadataTypeAttribute(typeof(Assignment.AssignmentMetadata))]
public partial class Assignment
{

    // This class allows you to attach custom attributes to properties
    // of the Assignment class.
    //
    // For example, the following marks the Xyz property as a
    // required property and specifies the format for valid values:
    //    [Required]
    //    [RegularExpression("[A-Z][A-Za-z0-9]*")]
    //    [StringLength(32)]
    //    public string Xyz { get; set; }
    internal sealed class AssignmentMetadata
    {

        // Metadata classes are not meant to be instantiated.
        private AssignmentMetadata()
        {
        }

        public decimal CostBudgeted { get; set; }

        public decimal CostRemaining { get; set; }

        public decimal HoursBudgeted { get; set; }

        public decimal HoursRemaining { get; set; }

        public bool IsComplete { get; set; }

        public int ItemID { get; set; }

        public Job Job { get; set; }

        public int JobID { get; set; }

        public Resource Resource { get; set; }

        public int ResourceID { get; set; }

        public Workplan Workplan { get; set; }

        public int WorkplanID { get; set; }

        public WorkplanItem WorkplanItem { get; set; }
    }
}

// The MetadataTypeAttribute identifies JobMetadata as the class
// that carries additional metadata for the Job class.
[MetadataTypeAttribute(typeof(Job.JobMetadata))]
public partial class Job
{

    // This class allows you to attach custom attributes to properties
    // of the Job class.
    //
    // For example, the following marks the Xyz property as a
    // required property and specifies the format for valid values:
    //    [Required]
    //    [RegularExpression("[A-Z][A-Za-z0-9]*")]
    //    [StringLength(32)]
    //    public string Xyz { get; set; }
    internal sealed class JobMetadata
    {

        // Metadata classes are not meant to be instantiated.
        private JobMetadata()
        {
        }

        [Display(AutoGenerateField = false)]
        [Include]
        [Composition]
        public EntityCollection<Assignment> Assignments { get; set; }

        [Display(Name="Client Job", Order=2, Description="Is this a client job?")]
        [DefaultValue(true)]
        public bool IsRealJob { get; set; }

        [Display(AutoGenerateField = false)]
        [Include]
        public JobDetail JobDetail { get; set; }

        [Display(AutoGenerateField = false)]
        public int JoblID { get; set; }

        [Display(Name="Job Title", Order=1, Description="What should this job be called?")]
        public string Title { get; set; }

        [Display(AutoGenerateField = false)]
        [Include]
        [Composition]
        public EntityCollection<WorkplanItem> WorkplanItems { get; set; }

        [Display(AutoGenerateField = false)]
        [Include]
        [Composition]
        public EntityCollection<Workplan> Workplans { get; set; }


        [Display(AutoGenerateField = false)]
        [Include]
        [Composition]
        public EntityCollection<Resource> Resources { get; set; }
    }
}

// The MetadataTypeAttribute identifies JobDetailMetadata as the class
// that carries additional metadata for the JobDetail class.
[MetadataTypeAttribute(typeof(JobDetail.JobDetailMetadata))]
public partial class JobDetail
{

    // This class allows you to attach custom attributes to properties
    // of the JobDetail class.
    //
    // For example, the following marks the Xyz property as a
    // required property and specifies the format for valid values:
    //    [Required]
    //    [RegularExpression("[A-Z][A-Za-z0-9]*")]
    //    [StringLength(32)]
    //    public string Xyz { get; set; }
    internal sealed class JobDetailMetadata
    {

        // Metadata classes are not meant to be instantiated.
        private JobDetailMetadata()
        {
        }

        [Display(Name="Client", Order=1,Description="Name of the Client")]
        public string Client { get; set; }

        [Display(Name = "Client Fee", Order = 5, Description = "Client Fee from Engagement Letter")]
        [DisplayFormat(DataFormatString="C",NullDisplayText="<Not Set>",ApplyFormatInEditMode=true)]
        public Nullable<decimal> ClientFee { get; set; }

        [Display(AutoGenerateField=false)]
        public int ClientIndex { get; set; }

        [Display(AutoGenerateField = true)]
        public string EFOLDERID { get; set; }

        [Display(Name = "Engagement Name", Order = 4, Description = "Friendly name of the Engagement")]
        public string Engagement { get; set; }

        [Display(Name = "Eng Type", Order = 3, Description = "Type of Work being done")]
        public string EngagementType { get; set; }

        [Display(AutoGenerateField = false)]
        public Job Job { get; set; }

        [Display(AutoGenerateField = false)]
        public int JobID { get; set; }

        [Display(AutoGenerateField = false)]
        public int PEJobID { get; set; }

        [Display(Name = "Service", Order = 2, Description = "Service Type")]
        public string Service { get; set; }

        [Display(Name = "Timing of the Work", Order = 6, Description = "When will this work occur?")]
        public string Timing { get; set; }
    }
}

// The MetadataTypeAttribute identifies PendingTimesheetMetadata as the class
// that carries additional metadata for the PendingTimesheet class.
[MetadataTypeAttribute(typeof(PendingTimesheet.PendingTimesheetMetadata))]
public partial class PendingTimesheet
{

    // This class allows you to attach custom attributes to properties
    // of the PendingTimesheet class.
    //
    // For example, the following marks the Xyz property as a
    // required property and specifies the format for valid values:
    //    [Required]
    //    [RegularExpression("[A-Z][A-Za-z0-9]*")]
    //    [StringLength(32)]
    //    public string Xyz { get; set; }
    internal sealed class PendingTimesheetMetadata
    {

        // Metadata classes are not meant to be instantiated.
        private PendingTimesheetMetadata()
        {
        }

        public decimal PendingHours { get; set; }

        public string UserName { get; set; }

        public DateTime WorkDate { get; set; }

        [Include]
        public Workplan Workplan { get; set; }

        public int WorkplanID { get; set; }
    }
}

// The MetadataTypeAttribute identifies ResourceMetadata as the class
// that carries additional metadata for the Resource class.
[MetadataTypeAttribute(typeof(Resource.ResourceMetadata))]
public partial class Resource
{

    // This class allows you to attach custom attributes to properties
    // of the Resource class.
    //
    // For example, the following marks the Xyz property as a
    // required property and specifies the format for valid values:
    //    [Required]
    //    [RegularExpression("[A-Z][A-Za-z0-9]*")]
    //    [StringLength(32)]
    //    public string Xyz { get; set; }
    internal sealed class ResourceMetadata
    {

        // Metadata classes are not meant to be instantiated.
        private ResourceMetadata()
        {
        }

        [Include]
        [Composition]
        public EntityCollection<Assignment> Assignments { get; set; }

        [Include]
        public Job Job { get; set; }

        public int JobID { get; set; }

        public decimal Rate { get; set; }

        public int ResourceID { get; set; }

        public string Title { get; set; }

        public string UserName { get; set; }
    }
}

// The MetadataTypeAttribute identifies WorkplanMetadata as the class
// that carries additional metadata for the Workplan class.
[MetadataTypeAttribute(typeof(Workplan.WorkplanMetadata))]
public partial class Workplan
{

    // This class allows you to attach custom attributes to properties
    // of the Workplan class.
    //
    // For example, the following marks the Xyz property as a
    // required property and specifies the format for valid values:
    //    [Required]
    //    [RegularExpression("[A-Z][A-Za-z0-9]*")]
    //    [StringLength(32)]
    //    public string Xyz { get; set; }
    internal sealed class WorkplanMetadata
    {

        // Metadata classes are not meant to be instantiated.
        private WorkplanMetadata()
        {
        }

        [Include]
        [Composition]
        public EntityCollection<Assignment> Assignments { get; set; }

        public string Description { get; set; }

        [Include]
        public Job Job { get; set; }

        public int JobID { get; set; }

        public EntityCollection<PendingTimesheet> PendingTimesheets { get; set; }

        public Nullable<int> PETaskID { get; set; }

        public decimal TtlCost { get; set; }

        public decimal TtlHours { get; set; }

        public DateTime WorkEnd { get; set; }

        public int WorkplanID { get; set; }

        [Include]
        [Composition]
        public EntityCollection<WorkplanItem> WorkplanItems { get; set; }

        public DateTime WorkStart { get; set; }
    }
}

// The MetadataTypeAttribute identifies WorkplanItemMetadata as the class
// that carries additional metadata for the WorkplanItem class.
[MetadataTypeAttribute(typeof(WorkplanItem.WorkplanItemMetadata))]
public partial class WorkplanItem
{

    // This class allows you to attach custom attributes to properties
    // of the WorkplanItem class.
    //
    // For example, the following marks the Xyz property as a
    // required property and specifies the format for valid values:
    //    [Required]
    //    [RegularExpression("[A-Z][A-Za-z0-9]*")]
    //    [StringLength(32)]
    //    public string Xyz { get; set; }
    internal sealed class WorkplanItemMetadata
    {

        // Metadata classes are not meant to be instantiated.
        private WorkplanItemMetadata()
        {
        }

        [Include]
        [Composition]
        public EntityCollection<Assignment> Assignments { get; set; }

        public string Description { get; set; }

        public int ItemID { get; set; }

        [Include]
        public Job Job { get; set; }

        public int JobID { get; set; }

        public string Notes { get; set; }

        public short Ordinal { get; set; }

        [Include]
        public Workplan Workplan { get; set; }

        public int WorkplanID { get; set; }
    }
}

1 Ответ

2 голосов
/ 29 июля 2010

Я выяснил решение моей проблемы. На самом деле это действительно мощно, если вы понимаете некоторые из этих внутренних работ и предостережений:

  1. Примените только атрибуты [Композиция] и [Включить] к корню графа вашего объекта.
  2. Ваш корневой объект Композиции - это то, что отправляется и начинается процесс обновления.
  3. EntityState вашего корневого объекта может фактически не представлять Обновление (как было в моем случае).
  4. Что когда-либо снова присоединяет ваш график, все объекты присоединяются, и их состояние устанавливается на одно и то же значение (вставлено, обновлено, удалено и т. Д.)
  5. После повторного присоединения графика вам необходимо обновить каждую сущность в соответствии с отправленным набором изменений.

Может быть, я еще до конца не понимаю; Тем не менее, это работает в моей среде. Я хотел бы найти лучший способ сделать это, но, похоже, я сталкиваюсь с различными проблемами, когда пытаюсь применить общий метод присоединения.

Вот то, что я придумал для моего метода обновления:

public void UpdateJob(Job currentJob)
    {
        //this.ObjectContext.Jobs.AttachAsModified(currentJob, this.ChangeSet.GetOriginal(currentJob));

        // compositional update process
        foreach (Assignment a in this.ChangeSet.GetAssociatedChanges(currentJob, j => j.Assignments))
        {
            ChangeOperation op = this.ChangeSet.GetChangeOperation(a);
            switch (op)
            {
                case ChangeOperation.Insert:
                    InsertAssignment(a);
                    break;
                case ChangeOperation.Update:
                    UpdateAssignment(a);
                    break;
                case ChangeOperation.Delete:
                    DeleteAssignment(a);
                    break;
                case ChangeOperation.None:
                    if (a.EntityState == EntityState.Detached)
                        this.ObjectContext.Assignments.Attach(a);
                    System.Data.Objects.ObjectStateEntry ose;
                    if (this.ObjectContext.ObjectStateManager.TryGetObjectStateEntry(a.EntityKey, out ose))
                        this.ObjectContext.ObjectStateManager.ChangeObjectState(a, EntityState.Unchanged);
                    break;
            }
        }

        foreach (WorkplanItem wpi in this.ChangeSet.GetAssociatedChanges(currentJob, j => j.WorkplanItems))
        {
            ChangeOperation op = this.ChangeSet.GetChangeOperation(wpi);
            switch (op)
            {
                case ChangeOperation.Insert:
                    InsertWorkplanItem(wpi);
                    break;
                case ChangeOperation.Update:
                    UpdateWorkplanItem(wpi);
                    break;
                case ChangeOperation.Delete:
                    DeleteWorkplanItem(wpi);
                    break;
                case ChangeOperation.None:
                    if (wpi.EntityState == EntityState.Detached)
                        this.ObjectContext.WorkplanItems.Attach(wpi);
                    System.Data.Objects.ObjectStateEntry ose;
                    if (this.ObjectContext.ObjectStateManager.TryGetObjectStateEntry(wpi.EntityKey, out ose))
                        this.ObjectContext.ObjectStateManager.ChangeObjectState(wpi, EntityState.Unchanged);
                    break;
            }
        }

        foreach (Workplan wp in this.ChangeSet.GetAssociatedChanges(currentJob, j => j.Workplans))
        {
            ChangeOperation op = this.ChangeSet.GetChangeOperation(wp);
            switch (op)
            {
                case ChangeOperation.Insert:
                    InsertWorkplan(wp);
                    break;
                case ChangeOperation.Update:
                    UpdateWorkplan(wp);
                    break;
                case ChangeOperation.Delete:
                    DeleteWorkplan(wp);
                    break;
                case ChangeOperation.None:
                    if (wp.EntityState == EntityState.Detached)
                        this.ObjectContext.Workplans.Attach(wp);
                    System.Data.Objects.ObjectStateEntry ose;
                    if (this.ObjectContext.ObjectStateManager.TryGetObjectStateEntry(wp.EntityKey, out ose))
                        this.ObjectContext.ObjectStateManager.ChangeObjectState(wp, EntityState.Unchanged);
                    break;
            }
        }

        foreach (Resource res in this.ChangeSet.GetAssociatedChanges(currentJob, j => j.Resources))
        {
            ChangeOperation op = this.ChangeSet.GetChangeOperation(res);
            switch (op)
            {
                case ChangeOperation.Insert:
                    InsertResource(res);
                    break;
                case ChangeOperation.Update:
                    UpdateResource(res);
                    break;
                case ChangeOperation.Delete:
                    DeleteResource(res);
                    break;
                case ChangeOperation.None:
                    if (res.EntityState == EntityState.Detached)
                        this.ObjectContext.Resources.Attach(res);
                    System.Data.Objects.ObjectStateEntry ose;
                    if (this.ObjectContext.ObjectStateManager.TryGetObjectStateEntry(res.EntityKey, out ose))
                        this.ObjectContext.ObjectStateManager.ChangeObjectState(res, EntityState.Unchanged);
                    break;
            }
        }

        ChangeOperation detailop = this.ChangeSet.GetChangeOperation(currentJob.JobDetail);
        switch (detailop)
        {
            case ChangeOperation.Insert:
                InsertJobDetail(currentJob.JobDetail);
                break;
            case ChangeOperation.Update:
                UpdateJobDetail(currentJob.JobDetail);
                break;
            case ChangeOperation.Delete:
                DeleteJobDetail(currentJob.JobDetail);
                break;
            case ChangeOperation.None:
                System.Data.Objects.ObjectStateEntry ose;
                if (this.ObjectContext.ObjectStateManager.TryGetObjectStateEntry(currentJob.JobDetail.EntityKey, out ose))
                        this.ObjectContext.ObjectStateManager.ChangeObjectState(currentJob.JobDetail, EntityState.Unchanged);
                break;
        }

        if (currentJob.EntityState == EntityState.Detached)
            this.ObjectContext.Jobs.Attach(currentJob);

        ChangeOperation jobop = this.ChangeSet.GetChangeOperation(currentJob);
        switch (jobop)
        {
            case ChangeOperation.Insert:
                InsertJob(currentJob);
                break;
            case ChangeOperation.Update:
                // Since this is the compositional root, we need to make sure there really is a change
                var origJob = this.ChangeSet.GetOriginal(currentJob);
                if (origJob != null)
                {
                    this.ObjectContext.Jobs.AttachAsModified(currentJob,
                        origJob);
                }
                else
                {
                    this.ObjectContext.ObjectStateManager.ChangeObjectState(
                        currentJob, EntityState.Unchanged);
                }
                break;
            case ChangeOperation.Delete:
                DeleteJob(currentJob);
                break;
            case ChangeOperation.None:
                this.ObjectContext.ObjectStateManager.ChangeObjectState(currentJob, EntityState.Unchanged);
                break;
        }

    }

Кроме того, вот изменения, которые мне пришлось внести в классы метаданных для моих сущностей.

// The MetadataTypeAttribute identifies AssignmentMetadata as the class
// that carries additional metadata for the Assignment class.
[MetadataTypeAttribute(typeof(Assignment.AssignmentMetadata))]
public partial class Assignment
{

    // This class allows you to attach custom attributes to properties
    // of the Assignment class.
    //
    // For example, the following marks the Xyz property as a
    // required property and specifies the format for valid values:
    //    [Required]
    //    [RegularExpression("[A-Z][A-Za-z0-9]*")]
    //    [StringLength(32)]
    //    public string Xyz { get; set; }
    internal sealed class AssignmentMetadata
    {

        // Metadata classes are not meant to be instantiated.
        private AssignmentMetadata()
        {
        }

        public decimal CostBudgeted { get; set; }

        public decimal CostRemaining { get; set; }

        public decimal HoursBudgeted { get; set; }

        public decimal HoursRemaining { get; set; }

        public bool IsComplete { get; set; }

        public int ItemID { get; set; }

        public Job Job { get; set; }

        public int JobID { get; set; }

        public Resource Resource { get; set; }

        public int ResourceID { get; set; }

        public Workplan Workplan { get; set; }

        public int WorkplanID { get; set; }

        public WorkplanItem WorkplanItem { get; set; }
    }
}

// The MetadataTypeAttribute identifies JobMetadata as the class
// that carries additional metadata for the Job class.
[MetadataTypeAttribute(typeof(Job.JobMetadata))]
public partial class Job
{

    // This class allows you to attach custom attributes to properties
    // of the Job class.
    //
    // For example, the following marks the Xyz property as a
    // required property and specifies the format for valid values:
    //    [Required]
    //    [RegularExpression("[A-Z][A-Za-z0-9]*")]
    //    [StringLength(32)]
    //    public string Xyz { get; set; }
    internal sealed class JobMetadata
    {

        // Metadata classes are not meant to be instantiated.
        private JobMetadata()
        {
        }

        [Display(AutoGenerateField = false)]
        [Include]
        [Composition]
        public EntityCollection<Assignment> Assignments { get; set; }

        [Display(Name="Client Job", Order=2, Description="Is this a client job?")]
        [DefaultValue(true)]
        public bool IsRealJob { get; set; }

        [Display(AutoGenerateField = false)]
        [Include]
        [Composition]
        public JobDetail JobDetail { get; set; }

        [Display(AutoGenerateField = false)]
        public int JoblID { get; set; }

        [Display(Name="Job Title", Order=1, Description="What should this job be called?")]
        public string Title { get; set; }

        [Display(AutoGenerateField = false)]
        [Include]
        [Composition]
        public EntityCollection<WorkplanItem> WorkplanItems { get; set; }

        [Display(AutoGenerateField = false)]
        [Include]
        [Composition]
        public EntityCollection<Workplan> Workplans { get; set; }


        [Display(AutoGenerateField = false)]
        [Include]
        [Composition]
        public EntityCollection<Resource> Resources { get; set; }
    }
}

// The MetadataTypeAttribute identifies JobDetailMetadata as the class
// that carries additional metadata for the JobDetail class.
[MetadataTypeAttribute(typeof(JobDetail.JobDetailMetadata))]
public partial class JobDetail
{

    // This class allows you to attach custom attributes to properties
    // of the JobDetail class.
    //
    // For example, the following marks the Xyz property as a
    // required property and specifies the format for valid values:
    //    [Required]
    //    [RegularExpression("[A-Z][A-Za-z0-9]*")]
    //    [StringLength(32)]
    //    public string Xyz { get; set; }
    internal sealed class JobDetailMetadata
    {

        // Metadata classes are not meant to be instantiated.
        private JobDetailMetadata()
        {
        }

        [Display(Name="Client", Order=1,Description="Name of the Client")]
        public string Client { get; set; }

        [Display(Name = "Client Fee", Order = 5, Description = "Client Fee from Engagement Letter")]
        [DisplayFormat(DataFormatString="C",NullDisplayText="<Not Set>",ApplyFormatInEditMode=true)]
        public Nullable<decimal> ClientFee { get; set; }

        [Display(AutoGenerateField=false)]
        public int ClientIndex { get; set; }

        [Display(AutoGenerateField = true)]
        public string EFOLDERID { get; set; }

        [Display(Name = "Engagement Name", Order = 4, Description = "Friendly name of the Engagement")]
        public string Engagement { get; set; }

        [Display(Name = "Eng Type", Order = 3, Description = "Type of Work being done")]
        public string EngagementType { get; set; }

        [Display(AutoGenerateField = false)]
        public Job Job { get; set; }

        [Display(AutoGenerateField = false)]
        public int JobID { get; set; }

        [Display(AutoGenerateField = false)]
        public int PEJobID { get; set; }

        [Display(Name = "Service", Order = 2, Description = "Service Type")]
        public string Service { get; set; }

        [Display(Name = "Timing of the Work", Order = 6, Description = "When will this work occur?")]
        public string Timing { get; set; }
    }
}

// The MetadataTypeAttribute identifies PendingTimesheetMetadata as the class
// that carries additional metadata for the PendingTimesheet class.
[MetadataTypeAttribute(typeof(PendingTimesheet.PendingTimesheetMetadata))]
public partial class PendingTimesheet
{

    // This class allows you to attach custom attributes to properties
    // of the PendingTimesheet class.
    //
    // For example, the following marks the Xyz property as a
    // required property and specifies the format for valid values:
    //    [Required]
    //    [RegularExpression("[A-Z][A-Za-z0-9]*")]
    //    [StringLength(32)]
    //    public string Xyz { get; set; }
    internal sealed class PendingTimesheetMetadata
    {

        // Metadata classes are not meant to be instantiated.
        private PendingTimesheetMetadata()
        {
        }

        public decimal PendingHours { get; set; }

        public string UserName { get; set; }

        public DateTime WorkDate { get; set; }

        [Include]
        public Workplan Workplan { get; set; }

        public int WorkplanID { get; set; }
    }
}

// The MetadataTypeAttribute identifies ResourceMetadata as the class
// that carries additional metadata for the Resource class.
[MetadataTypeAttribute(typeof(Resource.ResourceMetadata))]
public partial class Resource
{

    // This class allows you to attach custom attributes to properties
    // of the Resource class.
    //
    // For example, the following marks the Xyz property as a
    // required property and specifies the format for valid values:
    //    [Required]
    //    [RegularExpression("[A-Z][A-Za-z0-9]*")]
    //    [StringLength(32)]
    //    public string Xyz { get; set; }
    internal sealed class ResourceMetadata
    {

        // Metadata classes are not meant to be instantiated.
        private ResourceMetadata()
        {
        }

        public EntityCollection<Assignment> Assignments { get; set; }

        public Job Job { get; set; }

        public int JobID { get; set; }

        public decimal Rate { get; set; }

        public int ResourceID { get; set; }

        public string Title { get; set; }

        public string UserName { get; set; }
    }
}

// The MetadataTypeAttribute identifies WorkplanMetadata as the class
// that carries additional metadata for the Workplan class.
[MetadataTypeAttribute(typeof(Workplan.WorkplanMetadata))]
public partial class Workplan
{

    // This class allows you to attach custom attributes to properties
    // of the Workplan class.
    //
    // For example, the following marks the Xyz property as a
    // required property and specifies the format for valid values:
    //    [Required]
    //    [RegularExpression("[A-Z][A-Za-z0-9]*")]
    //    [StringLength(32)]
    //    public string Xyz { get; set; }
    internal sealed class WorkplanMetadata
    {

        // Metadata classes are not meant to be instantiated.
        private WorkplanMetadata()
        {
        }

        public EntityCollection<Assignment> Assignments { get; set; }

        public string Description { get; set; }

        public Job Job { get; set; }

        public int JobID { get; set; }

        public EntityCollection<PendingTimesheet> PendingTimesheets { get; set; }

        public Nullable<int> PETaskID { get; set; }

        public decimal TtlCost { get; set; }

        public decimal TtlHours { get; set; }

        public DateTime WorkEnd { get; set; }

        public int WorkplanID { get; set; }

        public EntityCollection<WorkplanItem> WorkplanItems { get; set; }

        public DateTime WorkStart { get; set; }
    }
}

// The MetadataTypeAttribute identifies WorkplanItemMetadata as the class
// that carries additional metadata for the WorkplanItem class.
[MetadataTypeAttribute(typeof(WorkplanItem.WorkplanItemMetadata))]
public partial class WorkplanItem
{

    // This class allows you to attach custom attributes to properties
    // of the WorkplanItem class.
    //
    // For example, the following marks the Xyz property as a
    // required property and specifies the format for valid values:
    //    [Required]
    //    [RegularExpression("[A-Z][A-Za-z0-9]*")]
    //    [StringLength(32)]
    //    public string Xyz { get; set; }
    internal sealed class WorkplanItemMetadata
    {

        // Metadata classes are not meant to be instantiated.
        private WorkplanItemMetadata()
        {
        }

        public EntityCollection<Assignment> Assignments { get; set; }

        public string Description { get; set; }

        public int ItemID { get; set; }

        public Job Job { get; set; }

        public int JobID { get; set; }

        public string Notes { get; set; }

        public short Ordinal { get; set; }

        public Workplan Workplan { get; set; }

        public int WorkplanID { get; set; }
    }
}

Если у кого-то есть другие советы / идеи, пожалуйста, добавьте их. Я буду продолжать публиковать больше, как я узнаю больше.

...