One of the limitations on WCF RIA Services is not support inheritance though making EF inheritance feature impossible to use with LinqToEntities. When you try to set a property of a base class you get this error:
Entity type 'Square' does not contain a public property named 'ShapeId'.
I found that this error is actually a problem with Silverlight reflection. Because of the new restrictions on reflection the change tracking code is unable to get the value of the property. But there is a workaround for it. Let’s illustrate the problem. I have this object model:
There are several things you have to do to make this model work. First you need to put the correct annotations on your model.
1: public class RootEntity
2: {
3: [Key]
4: public Guid Id
5: {
6: get;
7: set;
8: }
9:
10: public Guid ChildId
11: {
12: get;
13: set;
14: }
15:
16: public string Name
17: {
18: get;
19: set;
20: }
21:
22: [Composition]
23: [Association("RootEntity_ChildEntity", "ChildId", "Id")]
24: public ChildEntity Child
25: {
26: get;
27: set;
28: }
29: }
30:
31: public class ChildEntity
32: {
33: public ChildEntity()
34: {
35: this.SubChildren = new List<SubChildEntity>();
36: }
37:
38: [Key]
39: public Guid Id
40: {
41: get;
42: set;
43: }
44:
45: [Composition]
46: [Association("ChildEntity_SubChildEntity", "Id", "ChildEntityId")]
47: public IList<SubChildEntity> SubChildren
48: {
49: get;
50: private set;
51: }
52: }
53:
54: public class SubChildEntity
55: {
56: public SubChildEntity()
57: {
58: }
59:
60: [Key]
61: public Guid Id
62: {
63: get;
64: set;
65: }
66:
67: public Guid ChildEntityId
68: {
69: get;
70: set;
71: }
72:
73: [Include]
74: [Association("SubChildEntity_Shape", "Id", "SubChildEntityId")]
75: public Shape Shape
76: {
77: get;
78: set;
79: }
80: }
81:
82: [KnownType(typeof(Square))]
83: [KnownType(typeof(Circle))]
84: public abstract class Shape
85: {
86: [Key]
87: public Guid ShapeId
88: {
89: get;
90: set;
91: }
92:
93: public Guid SubChildEntityId
94: {
95: get;
96: set;
97: }
98:
99: public int CenterX
100: {
101: get;
102: set;
103: }
104:
105: public int CenterY
106: {
107: get;
108: set;
109: }
110: }
111:
112: public class Circle : Shape
113: {
114: public int Radius
115: {
116: get;
117: set;
118: }
119: }
120:
121: public class Square : Shape
122: {
123: public int RoudingRadius
124: {
125: get;
126: set;
127: }
128:
129: public bool RoundCorners
130: {
131: get;
132: set;
133: }
134: }
You have to notice the Composition attribute and the KnownType attribute.
Second you have to put methods to the CUD the detail objects on the server. This will force the EntitySets on the DomainContext to allow all kinds of changes. Also notice the fixup I have to do on the Ids that are involved in the associations in the InsertRootEntityMethod.
1: [EnableClientAccess]
2: public class RootEntityService : DomainService
3: {
4: private static List<RootEntity> dataSource = new List<RootEntity>();
5:
6: public IQueryable<RootEntity> GetRootEntities()
7: {
8: var q = from r in dataSource
9: //from src in r.Child.SubChildren
10: select r;
11:
12: return q.AsQueryable();
13: }
14:
15: public IQueryable<ChildEntity> GetChildEntities(Guid parentId)
16: {
17: var q = from r in dataSource
18: where r.Id == parentId
19: select r.Child;
20:
21: return q.AsQueryable();
22: }
23:
24: public IQueryable<SubChildEntity> GetSubChildren(Guid parentId)
25: {
26: var q = from r in dataSource
27: from s in r.Child.SubChildren
28: where r.Id == parentId
29: select s;
30:
31: return q.AsQueryable();
32: }
33:
34: public IQueryable<Shape> GetShapes(Guid parentId)
35: {
36: var q = from r in dataSource
37: from sc in r.Child.SubChildren
38: where r.Id == parentId
39: select sc.Shape;
40:
41: return q.AsQueryable();
42: }
43:
44: public void InsertRootEntity(RootEntity value)
45: {
46: if (dataSource.Find(entity => entity.Id == value.Id) != null)
47: {
48: throw new InvalidOperationException("Cannot add two entities with the same key");
49: }
50:
51: dataSource.Add(value);
52: value.ChildId = value.Child.Id;
53: foreach (var sub in value.Child.SubChildren)
54: {
55: sub.ChildEntityId = value.Child.Id;
56: sub.Shape.SubChildEntityId = sub.Id;
57: }
58: }
59:
60: public void UpdateRootEntity(RootEntity value)
61: {
62: var desiredEntity = dataSource.Find(entity => entity.Id == value.Id);
63: if (desiredEntity == null)
64: {
65: throw new InvalidOperationException("The given entity is not part of the data source");
66: }
67:
68: dataSource.Remove(desiredEntity);
69: dataSource.Add(value);
70:
71: value.ChildId = value.Child.Id;
72: }
73:
74: public void DeleteEntity(RootEntity value)
75: {
76: var desiredEntity = dataSource.Find(entity => entity.Id == value.Id);
77: if (desiredEntity == null)
78: {
79: throw new InvalidOperationException("The given entity is not part of the data source");
80: }
81:
82: dataSource.Remove(desiredEntity);
83: }
84:
85: public void InsertChildEntity(ChildEntity value)
86: {
87: }
88:
89: public void UpdateChildEntity(ChildEntity value)
90: {
91: }
92:
93: public void DeleteChildEntity(ChildEntity value)
94: {
95: }
96:
97: public void InsertSubChildEntity(SubChildEntity value)
98: {
99: }
100:
101: public void DeleteSubChildEntity(SubChildEntity value)
102: {
103: }
104:
105: public void UpdateSubChildEntity(SubChildEntity value)
106: {
107: }
108:
109: public void InsertCircle(Circle circle)
110: {
111: }
112:
113: public void UpdateCircle(Circle circle)
114: {
115: }
116:
117: public void DeleteCircle(Circle circle)
118: {
119: }
120:
121: public void InsertShape(Shape Shape)
122: {
123: }
124:
125: public void UpdateShape(Shape Shape)
126: {
127: }
128:
129: public void DeleteShape(Shape Shape)
130: {
131: }
132:
133: public override System.Collections.IEnumerable Query(QueryDescription queryDescription, out IEnumerable<System.ComponentModel.DataAnnotations.ValidationResult> validationErrors, out int totalCount)
134: {
135: return base.Query(queryDescription, out validationErrors, out totalCount);
136: }
137:
138: public override bool Submit(ChangeSet changeSet)
139: {
140: return base.Submit(changeSet);
141: }
142:
143: public override void Initialize(DomainServiceContext context)
144: {
145: base.Initialize(context);
146: }
147:
148: public override object Invoke(DomainOperationEntry operation, object[] parameters, out IEnumerable<System.ComponentModel.DataAnnotations.ValidationResult> validationErrors)
149: {
150: return base.Invoke(operation, parameters, out validationErrors);
151: }
152:
153: protected override bool AuthorizeChangeSet(ChangeSet changeSet)
154: {
155: return base.AuthorizeChangeSet(changeSet);
156: }
157:
158: protected override bool ExecuteChangeSet(ChangeSet changeSet)
159: {
160: return base.ExecuteChangeSet(changeSet);
161: }
162:
163: protected override bool ValidateChangeSet(ChangeSet changeSet)
164: {
165: return base.ValidateChangeSet(changeSet);
166: }
167:
168: protected override bool PersistChangeSet(ChangeSet changeSet)
169: {
170: return base.PersistChangeSet(changeSet);
171: }
172:
173: protected override bool ResolveChangeSet(ChangeSet changeSet)
174: {
175: return base.ResolveChangeSet(changeSet);
176: }
177:
178: protected override bool Resolve(object current, object original, object store, ResolveOption resolveOption)
179: {
180: return base.Resolve(current, original, store, resolveOption);
181: }
182:
183: protected override int Count<T>(IQueryable<T> query)
184: {
185: return base.Count<T>(query);
186: }
187:
188: protected override void OnError(DomainServiceErrorInfo errorInfo)
189: {
190: base.OnError(errorInfo);
191: }
192: }
Finally and very important you need to apply this workaround on the client side:
1: public partial class Square
2: {
3: public new Guid ShapeId
4: {
5: get
6: {
7: return base.ShapeId;
8: }
9:
10: set
11: {
12: base.ShapeId = value;
13: }
14: }
15:
16: public new int CenterX
17: {
18: get
19: {
20: return base.CenterX;
21: }
22:
23: set
24: {
25: base.CenterX = value;
26: }
27: }
28:
29: public new int CenterY
30: {
31: get
32: {
33: return base.CenterY;
34: }
35:
36: set
37: {
38: base.CenterY = value;
39: }
40: }
41: }
And you will be able to run this code on client side:
1: RootEntity rootEntity = new RootEntity();
2: rootEntity.Id = Guid.NewGuid();
3: rootEntity.Name = "A";
4:
5: ChildEntity child = new ChildEntity();
6: child.Id = Guid.NewGuid();
7: rootEntity.Child = child;
8:
9: SubChildEntity subChild = new SubChildEntity() { Id = Guid.NewGuid() };
10: child.SubChildren.Add(subChild);
11:
12: Square square = new Square { ShapeId = Guid.NewGuid(), CenterX = 10, CenterY = 10 };
13: subChild.Shape = square;
14:
15: this.domainContext.RootEntities.Add(rootEntity);
16:
17: var submitOperation = this.domainContext.SubmitChanges();
18: submitOperation.Completed += (o, ee) =>
19: {
20: var op = (SubmitOperation)o;
21: if (op.HasError)
22: {
23: this.errors.Add(op.Error);
24: }
25: };
I was able to do the update using this code:
1: bool flag2 = false;
2: bool flag3 = false;
3: bool flag4 = false;
4:
5: private void btRunTest2_Click(object sender, RoutedEventArgs e)
6: {
7: try
8: {
9: var op = this.domainContext.Load(this.domainContext.GetRootEntitiesQuery());
10:
11: op.Completed += (o, ee) =>
12: {
13: var entity = this.domainContext.RootEntities.First();
14: var op2 = this.domainContext.Load(this.domainContext.GetChildEntitiesQuery(entity.Id));
15: op2.Completed += (oo, eee) =>
16: {
17: flag2 = true;
18: OnEditEntity();
19: };
20:
21: var op3 = this.domainContext.Load(this.domainContext.GetSubChildrenQuery(entity.Id));
22: op3.Completed += (oo, eee) =>
23: {
24: flag3 = true;
25: OnEditEntity();
26: };
27:
28: var op4 = this.domainContext.Load(this.domainContext.GetShapesQuery(entity.Id));
29: op4.Completed += (oo, eee) =>
30: {
31: flag4 = true;
32: OnEditEntity();
33: };
34: };
35: }
36: catch (Exception ex)
37: {
38: this.errors.Add(ex);
39: }
40: }
41:
42: private void OnEditEntity()
43: {
44: if (flag2 && flag3 && flag4)
45: {
46: var entity = this.domainContext.RootEntities.First();
47: entity.Name = "B";
48: this.domainContext.SubmitChanges();
49: }
50: }
Notice that ALL related entitysets must have the entities loaded for the associations to be correctly initialized.
Have fun,
No comments:
Post a Comment