我正在使用Entity Framework,RIA Services和MVVM-Light工具包创建Silverlight 4应用程序。该应用程序处理复杂的对象图,其中包含以下结构:
我希望能够加载作业(使用Include属性/指令),这样可以正常工作。从特定作业的根目录加载完整图表。但是,当我提交更改时,我收到错误
Entity for operation '0' has multiple parents
我的理解是this post我的元数据上的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; }
}
}
答案 0 :(得分:2)
我已经找到解决问题的方法。一旦你理解了一些内部工作和警告,这实际上非常强大:
可能还有更多我仍然不完全理解;但是,这在我的环境中有效。我想找出一种更好的方法,但是当我尝试应用一种常见的附加方法时,我似乎遇到了不同的问题。
以下是我为更新方法提出的建议:
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; }
}
}
如果有人有其他提示/想法,请添加它们。随着我的了解,我会继续发帖。