Skip to content

Commit abdfa5c

Browse files
committed
add singleOrDefault expression tree builder
1 parent 37ddbd9 commit abdfa5c

File tree

2 files changed

+47
-20
lines changed

2 files changed

+47
-20
lines changed
Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,42 @@
1+
using System;
2+
using System.Reflection;
13
using System.Linq;
4+
using System.Linq.Expressions;
25
using JsonApiDotNetCore.Extensions;
36
using Microsoft.EntityFrameworkCore;
47

5-
public class GenericDataAccess
8+
namespace JsonApiDotNetCore.Data
69
{
7-
public DbSet<T> GetDbSet<T>(DbContext context) where T : class
10+
public class GenericDataAccess
811
{
9-
return context.Set<T>();
10-
}
12+
public DbSet<T> GetDbSet<T>(DbContext context) where T : class
13+
{
14+
return context.Set<T>();
15+
}
1116

12-
public IQueryable<T> IncludeEntity<T>(IQueryable<T> queryable, string includedEntityName) where T : class
13-
{
14-
return queryable.Include(includedEntityName);
17+
public IQueryable<T> IncludeEntity<T>(IQueryable<T> queryable, string includedEntityName) where T : class
18+
{
19+
return queryable.Include(includedEntityName);
20+
}
21+
22+
public T SingleOrDefault<T>(object query, string param, string value)
23+
{
24+
var queryable = (IQueryable<T>) query;
25+
var currentType = queryable.ElementType;
26+
var property = currentType.GetProperty(param);
27+
28+
if (property == null)
29+
{
30+
throw new ArgumentException($"'{param}' is not a valid property of '{currentType}'");
31+
}
32+
33+
var prm = Expression.Parameter(currentType, property.Name);
34+
var left = Expression.Convert(Expression.PropertyOrField(prm, property.Name), typeof(string));
35+
var right = Expression.Constant(1, property.PropertyType);
36+
var body = Expression.Equal(left, right);
37+
var where = Expression.Lambda<Func<T, bool>>(body, prm);
38+
39+
return queryable.SingleOrDefault(where);
40+
}
1541
}
1642
}

JsonApiDotNetCore/Data/ResourceRepository.cs

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,14 @@ public List<object> Get()
2525

2626
public object Get(string id)
2727
{
28-
if (_context.Route is RelationalRoute)
29-
{
30-
return GetRelated(id, _context.Route as RelationalRoute);
31-
}
32-
return GetEntityById(_context.Route.BaseModelType, id, null);
28+
var route = _context.Route as RelationalRoute;
29+
return route != null ? GetRelated(id, route) : GetEntityById(_context.Route.BaseModelType, id, null);
3330
}
3431

3532
private object GetRelated(string id, RelationalRoute relationalRoute)
3633
{
37-
// HACK: this would rely on lazy loading to work...will probably fail
3834
var entity = GetEntityById(relationalRoute.BaseModelType, id, relationalRoute.RelationshipName);
39-
return relationalRoute.BaseModelType.GetProperties().FirstOrDefault(pi => pi.Name.ToCamelCase() == relationalRoute.RelationshipName.ToCamelCase()).GetValue(entity);
35+
return relationalRoute.BaseModelType.GetProperties().FirstOrDefault(pi => pi.Name.ToCamelCase() == relationalRoute.RelationshipName.ToCamelCase())?.GetValue(entity);
4036
}
4137

4238
private IQueryable GetDbSetFromContext(string propName)
@@ -47,21 +43,26 @@ private IQueryable GetDbSetFromContext(string propName)
4743

4844
private object GetEntityById(Type modelType, string id, string includedRelationship)
4945
{
50-
// HACK: I _believe_ by casting to IEnumerable, we are loading all records into memory, if so... find a better way...
51-
// Also, we are making a BIG assumption that the resource has an attribute Id and not ResourceId which is allowed by EF
46+
// get generic dbSet
5247
var dataAccessorInstance = Activator.CreateInstance(typeof(GenericDataAccess));
53-
var dataAccessorMethod = dataAccessorInstance.GetType().GetMethod("GetDbSet");
54-
var genericMethod = dataAccessorMethod.MakeGenericMethod(modelType);
55-
var dbSet = genericMethod.Invoke(dataAccessorInstance, new [] {((DbContext) _context.DbContext) });
48+
var dataAccessorGetDbSetMethod = dataAccessorInstance.GetType().GetMethod("GetDbSet");
49+
var genericGetDbSetMethod = dataAccessorGetDbSetMethod.MakeGenericMethod(modelType);
50+
var dbSet = genericGetDbSetMethod.Invoke(dataAccessorInstance, new [] {((DbContext) _context.DbContext) });
5651

52+
// include relationships if requested
5753
if (!string.IsNullOrEmpty(includedRelationship))
5854
{
5955
var includeMethod = dataAccessorInstance.GetType().GetMethod("IncludeEntity");
6056
var genericIncludeMethod = includeMethod.MakeGenericMethod(modelType);
6157
dbSet = genericIncludeMethod.Invoke(dataAccessorInstance, new []{ dbSet, includedRelationship.ToProperCase() });
6258
}
6359

64-
return (dbSet as IEnumerable<dynamic>).SingleOrDefault(x => x.Id.ToString() == id);
60+
// get the SingleOrDefault value by Id
61+
var dataAccessorSingleOrDefaultMethod = dataAccessorInstance.GetType().GetMethod("SingleOrDefault");
62+
var genericSingleOrDefaultMethod = dataAccessorSingleOrDefaultMethod.MakeGenericMethod(modelType);
63+
var entity = genericSingleOrDefaultMethod.Invoke(dataAccessorInstance, new[] { dbSet, "Id", id });
64+
65+
return entity;
6566
}
6667

6768
public void Add(object entity)

0 commit comments

Comments
 (0)