DSL Tools #11 - Preventing the User from Deleting Instances of a Domain Class
NOTE: The procedure described here is based on the domain model described in this previous post.
---
The next thing that I wanted to achieve in my DSL was to prevent the user from deleting manually the instances of certain domain classes.
On my example, since all the attributes are created automatically (by code) when each entity is added (also automatically), I don't want the user to be able to delete these attributes. That would create inconsistencies between this model and the model from where the entities were previously read.
Searching the Web for a solution, I came across 2 different articles about this:
Article #1
Article #2
The two articles define different approaches to the problem. The difference is basically the user experience you'll achieve depending when the solution you select.
Personally, I like to give clear feedback about why he can't do something very obvious. Also I tend to prefer the solution that requires less coding. So I went with the second.
1. Preventing the User from Deleting Embedding Relationships
If you look at the EntityHasNormalAttributes this is an embedding relationship that will be presented to the user has a compartment in the entity shape, containing all its normal attributes.
You prevent the user from deleting normal attributes if you prevent him from deleting this kind of relationship.
For that, you create a partial class for the EntityHasNormalAttributes and override the OnDeleting method. When you want to prevent the delete from happening, you need to throw an exception (InvalidOperationException is recommended). Like this:
1: using System;
2: using Microsoft.VisualStudio.Modeling;
3:
4: namespace MyDSL
5: {
6: /// <summary>
7: /// Provides custom behavior for the EntityHasNormalAttributes domain class.
8: /// </summary>
9: public partial class EntityHasNormalAttributes
10: {
11: #region Public Methods
12:
13: /// <summary>
14: /// Called when an instance of this domain class is being deleted.
15: /// </summary>
16: protected override void OnDeleting()
17: {
18: // Default behavior
19:
20: base.OnDeleting();
21:
22: // You can't delete normal attributes from entities
23:
24: if ((this.Entity != null) && (!this.Entity.IsDeleting))
25: {
26: throw new InvalidOperationException(Properties.Resources.RES_Error_CannotDeleteAttribute);
27: }
28: }
29:
30: #endregion
31: }
32: }
Notice that the exception is only thrown if Entity.IsDeleting is false. This means that you only want to prevent the relationship from being removed if you're not deleting the entity itself.
2. Preventing the User from Deleting Reference Relationships
My domain model also includes a reference relationship that I want to create at runtime (when the user "imports" the model) and prevent from being removed latter by him (manually).
In this case I want to prevent the connector from being removed. So the implementation is very similar: override IsDeleting in ModuleReferenceUsesEntities:
1: using System;
2:
3: namespace MyDSL
4: {
5: /// <summary>
6: /// Provides custom behavior for the ModuleReferenceUsesEntities domain class.
7: /// </summary>
8: public partial class ModuleReferenceUsesEntities
9: {
10: #region Public Methods
11:
12: /// <summary>
13: /// Called when an instance of this domain class is being deleted.
14: /// </summary>
15: protected override void OnDeleting()
16: {
17: // Default behavior
18:
19: base.OnDeleting();
20:
21: // You cannot delete connections between module references and entities
22: // If we're deleting the module reference, then it's ok
23: // If we're deleting the entity, then it's ok
24:
25: if (this.ModuleReference == null || this.ModuleReference.IsDeleting)
26: {
27: return;
28: }
29: if (this.Entity == null || this.Entity.IsDeleting)
30: {
31: return;
32: }
33:
34: throw new InvalidOperationException(Properties.Resources.RES_Error_CannotDeleteModuleReferenceEntityConnection);
35: }
36:
37: #endregion
38: }
39: }
Notice now that the exception is only throw when neither the module reference or the entity is being removed. That is the case when the user tries to delete the connector itself between these two domain classes.
---
NOTE: The procedure described here is based on the domain model described in this previous post.