Skip to content

Commit dc8453e

Browse files
committed
fix tests...lots of ugly code in here. need to refactor
1 parent 1fa4605 commit dc8453e

File tree

3 files changed

+88
-5
lines changed

3 files changed

+88
-5
lines changed

src/JsonApiDotNetCore/Builders/ContextGraphBuilder.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,9 +192,19 @@ protected virtual List<RelationshipAttribute> GetRelationships(Type entityType)
192192
hasManyThroughAttribute.LeftProperty = throughProperties.SingleOrDefault(x => x.PropertyType == entityType)
193193
?? throw new JsonApiSetupException($"{hasManyThroughAttribute.ThroughType} does not contain a navigation property to type {entityType}");
194194

195+
// ArticleTag.ArticleId
196+
var leftIdPropertyName = hasManyThroughAttribute.GetIdentityPropertyName(hasManyThroughAttribute.LeftProperty);
197+
hasManyThroughAttribute.LeftIdProperty = throughProperties.SingleOrDefault(p => p.Name == leftIdPropertyName)
198+
?? throw new JsonApiSetupException($"{hasManyThroughAttribute.ThroughType} does not contain an identity property {leftIdPropertyName}");
199+
195200
// Article → ArticleTag.Tag
196201
hasManyThroughAttribute.RightProperty = throughProperties.SingleOrDefault(x => x.PropertyType == hasManyThroughAttribute.Type)
197202
?? throw new JsonApiSetupException($"{hasManyThroughAttribute.ThroughType} does not contain a navigation property to type {hasManyThroughAttribute.Type}");
203+
204+
// ArticleTag.TagId
205+
var rightIdPropertyName = hasManyThroughAttribute.GetIdentityPropertyName(hasManyThroughAttribute.RightProperty);
206+
hasManyThroughAttribute.RightIdProperty = throughProperties.SingleOrDefault(p => p.Name == rightIdPropertyName)
207+
?? throw new JsonApiSetupException($"{hasManyThroughAttribute.ThroughType} does not contain an identity property {rightIdPropertyName}");
198208
}
199209
}
200210

src/JsonApiDotNetCore/Data/DefaultEntityRepository.cs

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -199,9 +199,7 @@ private void AttachHasManyPointers(TEntity entity)
199199
private void AttachHasMany(HasManyAttribute relationship, IList pointers)
200200
{
201201
foreach (var pointer in pointers)
202-
{
203202
_context.Entry(pointer).State = EntityState.Unchanged;
204-
}
205203
}
206204

207205
private void AttachHasManyThrough(TEntity entity, HasManyThroughAttribute hasManyThrough, IList pointers)
@@ -218,14 +216,48 @@ private void AttachHasManyThrough(TEntity entity, HasManyThroughAttribute hasMan
218216
_context.Entry(pointer).State = EntityState.Unchanged;
219217

220218
var throughInstance = Activator.CreateInstance(hasManyThrough.ThroughType);
221-
_context.Entry(throughInstance).State = EntityState.Added;
222-
223219
hasManyThrough.LeftProperty.SetValue(throughInstance, entity);
224220
hasManyThrough.RightProperty.SetValue(throughInstance, pointer);
221+
225222
throughRelationshipCollection.Add(throughInstance);
226223
}
227224
}
228225

226+
private void UpdateHasManyThrough(TEntity entity)
227+
{
228+
var relationships = _jsonApiContext.HasManyRelationshipPointers.Get();
229+
foreach (var relationship in relationships)
230+
{
231+
if(relationship.Key is HasManyThroughAttribute hasManyThrough)
232+
{
233+
// create the collection (e.g. List<ArticleTag>)
234+
// this type MUST implement IList so we can build the collection
235+
// if this is problematic, we _could_ reflect on the type and find an Add method
236+
// or we might be able to create a proxy type and implement the enumerator
237+
var throughRelationshipCollection = Activator.CreateInstance(hasManyThrough.ThroughProperty.PropertyType) as IList;
238+
hasManyThrough.ThroughProperty.SetValue(entity, throughRelationshipCollection);
239+
foreach (var pointer in relationship.Value)
240+
{
241+
_context.Entry(pointer).State = EntityState.Unchanged;
242+
243+
var throughInstance = Activator.CreateInstance(hasManyThrough.ThroughType);
244+
_context.Entry(throughInstance).State = EntityState.Added;
245+
246+
hasManyThrough.LeftIdProperty.SetValue(throughInstance, entity.Id);
247+
hasManyThrough.LeftProperty.SetValue(throughInstance, entity);
248+
hasManyThrough.RightProperty.SetValue(throughInstance, pointer);
249+
250+
var pointerId = (pointer as Identifiable<TId>) // known limitation, just need to come up with a solution...
251+
?? throw new JsonApiException(500, $"Cannot update the HasManyThrough relationship '{hasManyThrough.PublicRelationshipName}'. Id type must match the parent resource id type.");
252+
253+
hasManyThrough.RightIdProperty.SetValue(throughInstance, pointerId.Id);
254+
255+
throughRelationshipCollection.Add(throughInstance);
256+
}
257+
}
258+
}
259+
}
260+
229261
/// <summary>
230262
/// This is used to allow creation of HasOne relationships when the
231263
/// independent side of the relationship already exists.
@@ -252,7 +284,7 @@ public virtual async Task<TEntity> UpdateAsync(TId id, TEntity entity)
252284
foreach (var relationship in _jsonApiContext.RelationshipsToUpdate)
253285
relationship.Key.SetValue(oldEntity, relationship.Value);
254286

255-
AttachRelationships(entity);
287+
UpdateHasManyThrough(entity);
256288

257289
await _context.SaveChangesAsync();
258290

src/JsonApiDotNetCore/Models/HasManyThroughAttribute.cs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,21 @@ public HasManyThroughAttribute(string publicName, string internalThroughName, Li
9595
/// </example>
9696
public PropertyInfo LeftProperty { get; internal set; }
9797

98+
/// <summary>
99+
/// The identity property back to the parent resource.
100+
/// </summary>
101+
///
102+
/// <example>
103+
/// In the `[HasManyThrough("tags", nameof(ArticleTags))]` example
104+
/// this would point to the `Article.ArticleTags.ArticleId` property
105+
///
106+
/// <code>
107+
/// public int ArticleId { get; set; }
108+
/// </code>
109+
///
110+
/// </example>
111+
public PropertyInfo LeftIdProperty { get; internal set; }
112+
98113
/// <summary>
99114
/// The navigation property to the related resource from the join type.
100115
/// </summary>
@@ -110,6 +125,32 @@ public HasManyThroughAttribute(string publicName, string internalThroughName, Li
110125
/// </example>
111126
public PropertyInfo RightProperty { get; internal set; }
112127

128+
/// <summary>
129+
/// The identity property back to the related resource from the join type.
130+
/// </summary>
131+
///
132+
/// <example>
133+
/// In the `[HasManyThrough("tags", nameof(ArticleTags))]` example
134+
/// this would point to the `Article.ArticleTags.TagId` property
135+
///
136+
/// <code>
137+
/// public int TagId { get; set; }
138+
/// </code>
139+
///
140+
/// </example>
141+
public PropertyInfo RightIdProperty { get; internal set; }
142+
143+
/// <summary>
144+
/// Get the name of the identity property from the navigation property.
145+
/// </summary>
146+
///
147+
/// <example>
148+
/// In the `[HasManyThrough("tags", nameof(ArticleTags))]` example
149+
/// this would translate to the `Article.ArticleTags.Tag` property
150+
/// to TagId. This is then used to set the Left and RightIdProperties.
151+
/// </example>
152+
public virtual string GetIdentityPropertyName(PropertyInfo property) => property.Name + "Id";
153+
113154
/// <summary>
114155
/// The join entity property on the parent resource.
115156
/// </summary>

0 commit comments

Comments
 (0)