3

Question Summary:

Why would a "Multiplicity constraint violated" be able to be worked around by the introduction of the following line of code?

var numModifiedObjects =createdObject.ObjectSpace.ModifiedObjects.Count; 

Is there a setting that would make the work around unnecessary?

Background of the problem followed by the work around:

In my winforms xaf, EF6.2 Code First project I have the following business object which is self referencing.

[NavigationItem("Main")] public class H2Category : IHCategory { public H2Category() { Children = new BindingList<H2Category>(); } [Browsable(false)] public Int32 ID { get; protected set; } public String Name { get; set; } public H2Category Parent { get; set; } public virtual IList<H2Category> Children { get; set; } [NotMapped, Browsable(false), RuleFromBoolProperty("H2CategoryCircularReferences", DefaultContexts.Save, "Circular refrerence detected. To correct this error, set the Parent property to another value.", UsedProperties = "Parent")] public Boolean IsValid { get { H2Category currentObj = Parent; while (currentObj != null) { if (currentObj == this) { return false; } currentObj = currentObj.Parent; } return true; } } IBindingList ITreeNode.Children => Children as IBindingList; ITreeNode IHCategory.Parent { get => Parent as IHCategory; set => Parent = value as H2Category; } ITreeNode ITreeNode.Parent => Parent as ITreeNode; } 

and in the dbContext OnModelCreating I use

 modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>(); modelBuilder.Entity<H2Category>().HasMany(x => x.Children).WithOptional(x => x.Parent); 

Parent_Id);

The database looks correct in the SQL Server Object Explorer the SQL Server Object Explorer

and the data looks correct.

data table

In the XAF Treeview, when the user adds a child record, I want the new child's parent to be set to the current record.

To do this I am using a NewObjectViewController

public partial class H2CategoryController : ViewController { private NewObjectViewController controller; public H2CategoryController() { InitializeComponent(); TargetObjectType = typeof(H2Category); } protected override void OnActivated() { controller = Frame.GetController<NewObjectViewController>(); controller.ObjectCreated += controller_ObjectCreated; base.OnActivated(); } private void controller_ObjectCreated(object sender, ObjectCreatedEventArgs e) { var createdObject = e.CreatedObject as H2Category; var currentObject = View.CurrentObject as H2Category; // inspection here shows that currentObject and createdObject are different but they have the same proxy var s = $"created object Id is {createdObject.ID} current object id is {currentObject.ID}"; Console.WriteLine(s); var propertyCollectionSource = (View as ListView)?.CollectionSource as PropertyCollectionSource; if (!(propertyCollectionSource?.MasterObject is H2Category master)) return; createdObject.Parent = master; createdObject.Name = "t"; master.Children.Add(createdObject); Console.WriteLine(master.ID); } protected override void OnDeactivated() { controller.ObjectCreated += controller_ObjectCreated; base.OnDeactivated(); } } 

As soon as the user starts typing into the new record the following error appears

 Multiplicity constraint violated. The role 'H2Category_Children_Source' of the relationship 'SBD.GL.Module.BusinessObjects.H2Category_Children' has multiplicity 1 or 0..1. at System.Data.Entity.Core.Objects.EntityEntry.AddDetectedRelationship[T](Dictionary`2 relationships, T relatedObject, RelatedEnd relatedEnd) at System.Data.Entity.Core.Objects.EntityEntry.AddRelationshipDetectedByGraph(Dictionary`2 relationships, Object relatedObject, RelatedEnd relatedEndFrom, Boolean verifyForAdd) at System.Data.Entity.Core.Objects.EntityEntry.DetectChangesInRelationshipsOfSingleEntity() at System.Data.Entity.Core.Objects.ObjectStateManager.DetectChangesInNavigationProperties(IList`1 entries) at System.Data.Entity.Core.Objects.ObjectStateManager.DetectChanges() at System.Data.Entity.Core.Objects.ObjectContext.DetectChanges() at DevExpress.ExpressApp.EF.EFObjectSpace.DetectChanges() at DevExpress.ExpressApp.EF.EFObjectSpace.GetModifiedObjects() at DevExpress.ExpressApp.EF.EFObjectSpace.get_ModifiedObjects() at DevExpress.ExpressApp.Win.SystemModule.CustomCollectModifiedObjectsEventArgs.CollectModifiedObjects(Boolean checkObjectSpaceIsModified) at DevExpress.ExpressApp.Win.SystemModule.LockController.GetModifiedObjects() at DevExpress.ExpressApp.Win.SystemModule.LockController.CheckLocking(LockController controller) at DevExpress.ExpressApp.Win.SystemModule.LockController.CheckLocking(Object obj) at DevExpress.ExpressApp.Win.SystemModule.LockController.ViewObjectSpace_ObjectChanged(Object sender, ObjectChangedEventArgs e) at System.EventHandler`1.Invoke(Object sender, TEventArgs e) at DevExpress.ExpressApp.BaseObjectSpace.OnObjectChanged(ObjectChangedEventArgs args) at DevExpress.ExpressApp.EF.EFObjectSpace.SetModified(Object obj, ObjectChangedEventArgs args) at DevExpress.ExpressApp.BaseObjectSpace.SetModified(Object obj, IMemberInfo memberInfo) at DevExpress.ExpressApp.DetailView.Editor_ControlValueChanged(Object sender, EventArgs e) at System.EventHandler.Invoke(Object sender, EventArgs e) at DevExpress.ExpressApp.Editors.PropertyEditor.OnControlValueChanged() at DevExpress.ExpressApp.Win.Editors.DXPropertyEditor.Editor_EditValueChanged(Object sender, EventArgs e) at System.EventHandler.Invoke(Object sender, EventArgs e) at DevExpress.XtraEditors.Repository.RepositoryItem.RaiseEditValueChangedCore(EventArgs e) at DevExpress.XtraEditors.Repository.RepositoryItem.RaiseEditValueChanged(EventArgs e) at DevExpress.XtraEditors.BaseEdit.RaiseEditValueChanged() at DevExpress.XtraEditors.BaseEdit.OnEditValueChanged() at DevExpress.XtraEditors.TextEdit.OnEditValueChanged() at DevExpress.XtraEditors.BaseEdit.OnEditValueChanging(ChangingEventArgs e) at DevExpress.XtraEditors.TextEdit.OnEditValueChanging(ChangingEventArgs e) at DevExpress.XtraEditors.BaseEdit.set_EditValue(Object value) at DevExpress.XtraEditors.TextEdit.OnMaskBox_ValueChanged(Object sender, EventArgs e) at DevExpress.XtraEditors.Mask.MaskBox.RaiseEditTextChanged() at DevExpress.XtraEditors.Mask.MaskBox.MaskStrategy.SimpleStrategy.DoAfterTextChanged(EventArgs e) at DevExpress.XtraEditors.Mask.MaskBox.OnTextChanged(EventArgs e) at System.Windows.Forms.TextBoxBase.WmReflectCommand(Message& m) at System.Windows.Forms.TextBoxBase.WndProc(Message& m) at System.Windows.Forms.TextBox.WndProc(Message& m) at DevExpress.XtraEditors.Mask.MaskBox.BaseWndProc(Message& m) at DevExpress.XtraEditors.Mask.MaskBox.MaskStrategy.SimpleStrategy.DoWndProc(Message& m) at DevExpress.XtraEditors.Mask.MaskBox.WndProc(Message& m) at DevExpress.XtraEditors.TextBoxMaskBox.WndProc(Message& msg) at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m) at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m) at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam) 

error message

I note that the debug values look correct when I set the parent debug values

But am confused that all the H2Category objects have the same proxy value.

I have asked about the issue at Dev Express but ask here as well as it is Entity Framework related.

[Update -Work Around]

I added the IObjectSpaceLink interface to the business object. This exposes the ObjectSpace as a property of the business object.

When I put a break in the controller_ObjectCreated method I was able to see that the ModifiedObjects property was showing an error

ModifiedObjects error

I found that if I assign createdObject.ObjectSpace.ModifiedObjects.Count to a variable then the problem does not occur.

 private void controller_ObjectCreated(object sender, ObjectCreatedEventArgs e) { var createdObject = e.CreatedObject as H2Category; var numModifiedObjects =createdObject.ObjectSpace.ModifiedObjects.Count; var currentObject = View.CurrentObject as H2Category; var s = $"created object Id is {createdObject.ID} current object id is {currentObject.ID}"; var propertyCollectionSource = (View as ListView)?.CollectionSource as PropertyCollectionSource; if (!(propertyCollectionSource?.MasterObject is H2Category master)) return; var m = e.ObjectSpace.GetObject(master); createdObject.Parent = m; createdObject.Name = "t"; m.Children.Add(createdObject); numModifiedObjects = createdObject.ObjectSpace.ModifiedObjects.Count; Console.WriteLine(master.ID); } 

I tried disabling Proxy creation but it did not help.

exception

3
  • 1
    What do you mean by "they have the same proxy"? Entity objects don't "have" a proxy, they "are" proxies (if proxy generation is enabled). Commented Jan 7, 2019 at 22:04
  • In the locals section of the debug screen shot, the master, current and createdObjects show the same Value in the Value column. But they are different objects. Thus I thought a proxy was something they had not something they are. Ah, now I see a proxy is like a type. Thank you. stackoverflow.com/questions/7189386/… Commented Jan 7, 2019 at 22:53
  • learn.microsoft.com/en-us/ef/ef6/fundamentals/proxies Commented Jan 8, 2019 at 6:07

1 Answer 1

0

From the Dev Express support answer.

The relationship works incorrectly in my project because of a mistake in my H2Category class. The Parent property must be declared virtual.

Also, my ICategoryController conflicts with the built-in TreeNodeController. This controller also subscribes to the NewObjectViewController.ObjectCreated event and links a newly created ITreeNode object with a parent node selected in the source list view. By default, objects are linked when changes are saved. To link the new object immediately upon creation, set the NewObjectViewController.LinkNewObjectToParentImmediately property to true.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.