Я выяснил решение моей проблемы. На самом деле это действительно мощно, если вы понимаете некоторые из этих внутренних работ и предостережений:
- Примените только атрибуты [Композиция] и [Включить] к корню графа вашего объекта.
- Ваш корневой объект Композиции - это то, что отправляется и начинается процесс обновления.
- EntityState вашего корневого объекта может фактически не представлять Обновление (как было в моем случае).
- Что когда-либо снова присоединяет ваш график, все объекты присоединяются, и их состояние устанавливается на одно и то же значение (вставлено, обновлено, удалено и т. Д.)
- После повторного присоединения графика вам необходимо обновить каждую сущность в соответствии с отправленным набором изменений.
Может быть, я еще до конца не понимаю; Тем не менее, это работает в моей среде. Я хотел бы найти лучший способ сделать это, но, похоже, я сталкиваюсь с различными проблемами, когда пытаюсь применить общий метод присоединения.
Вот то, что я придумал для моего метода обновления:
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; }
}
}
Если у кого-то есть другие советы / идеи, пожалуйста, добавьте их. Я буду продолжать публиковать больше, как я узнаю больше.