Skip to content

Commit da7847f

Browse files
committed
An EfCoreRepository implementation for EFCore 1.1.0
1 parent 7e9cd1d commit da7847f

8 files changed

Lines changed: 660 additions & 0 deletions
Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
using Microsoft.EntityFrameworkCore;
2+
using SharpRepository.Repository;
3+
using SharpRepository.Repository.Caching;
4+
using SharpRepository.Repository.FetchStrategies;
5+
using System;
6+
using System.Linq;
7+
8+
namespace SharpRepository.EfCoreRepository
9+
{
10+
public class EfCoreCompoundKeyRepositoryBase<T> : LinqCompoundKeyRepositoryBase<T> where T : class
11+
{
12+
protected DbSet<T> DbSet { get; private set; }
13+
protected DbContext Context { get; private set; }
14+
15+
internal EfCoreCompoundKeyRepositoryBase(DbContext dbContext, ICompoundKeyCachingStrategy<T> cachingStrategy = null)
16+
: base(cachingStrategy)
17+
{
18+
Initialize(dbContext);
19+
}
20+
21+
private void Initialize(DbContext dbContext)
22+
{
23+
Context = dbContext;
24+
DbSet = Context.Set<T>();
25+
}
26+
27+
protected override void AddItem(T entity)
28+
{
29+
// no generating primary keys
30+
DbSet.Add(entity);
31+
}
32+
33+
protected override void DeleteItem(T entity)
34+
{
35+
DbSet.Remove(entity);
36+
}
37+
38+
protected override void UpdateItem(T entity)
39+
{
40+
var entry = Context.Entry<T>(entity);
41+
42+
if (entry.State == EntityState.Detached)
43+
{
44+
object[] keys;
45+
46+
if (GetPrimaryKeys(entity, out keys))
47+
{
48+
// check to see if this item is already attached
49+
// if it is then we need to copy the values to the attached value instead of changing the State to modified since it will throw a duplicate key exception
50+
// specifically: "An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key."
51+
var attachedEntity = Context.Set<T>().Find(keys);
52+
if (attachedEntity != null)
53+
{
54+
Context.Entry(attachedEntity).CurrentValues.SetValues(entity);
55+
return;
56+
}
57+
}
58+
}
59+
60+
// default
61+
entry.State = EntityState.Modified;
62+
}
63+
64+
protected override void SaveChanges()
65+
{
66+
Context.SaveChanges();
67+
}
68+
69+
protected override IQueryable<T> BaseQuery(IFetchStrategy<T> fetchStrategy = null)
70+
{
71+
var query = DbSet.AsQueryable();
72+
73+
return fetchStrategy == null ? query : fetchStrategy.IncludePaths.Aggregate(query, (current, path) => current.Include(path));
74+
}
75+
76+
// we override the implementation fro LinqBaseRepository becausee this is built in and doesn't need to find the key column and do dynamic expressions, etc.
77+
protected override T GetQuery(params object[] keys)
78+
{
79+
return DbSet.Find(keys);
80+
}
81+
82+
public override void Dispose()
83+
{
84+
Dispose(true);
85+
GC.SuppressFinalize(this);
86+
}
87+
88+
protected virtual void Dispose(bool disposing)
89+
{
90+
if (!disposing) return;
91+
if (Context == null) return;
92+
93+
Context.Dispose();
94+
Context = null;
95+
}
96+
}
97+
98+
public class EfCoreCompoundKeyRepositoryBase<T, TKey, TKey2> : LinqCompoundKeyRepositoryBase<T, TKey, TKey2> where T : class
99+
{
100+
protected DbSet<T> DbSet { get; private set; }
101+
protected DbContext Context { get; private set; }
102+
103+
internal EfCoreCompoundKeyRepositoryBase(DbContext dbContext, ICompoundKeyCachingStrategy<T, TKey, TKey2> cachingStrategy = null)
104+
: base(cachingStrategy)
105+
{
106+
Initialize(dbContext);
107+
}
108+
109+
private void Initialize(DbContext dbContext)
110+
{
111+
Context = dbContext;
112+
DbSet = Context.Set<T>();
113+
}
114+
115+
protected override void AddItem(T entity)
116+
{
117+
DbSet.Add(entity);
118+
}
119+
120+
protected override void DeleteItem(T entity)
121+
{
122+
DbSet.Remove(entity);
123+
}
124+
125+
protected override void UpdateItem(T entity)
126+
{
127+
var entry = Context.Entry<T>(entity);
128+
129+
if (entry.State == EntityState.Detached)
130+
{
131+
TKey key;
132+
TKey2 key2;
133+
134+
if (GetPrimaryKey(entity, out key, out key2))
135+
{
136+
// check to see if this item is already attached
137+
// if it is then we need to copy the values to the attached value instead of changing the State to modified since it will throw a duplicate key exception
138+
// specifically: "An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key."
139+
var attachedEntity = Context.Set<T>().Find(key, key2);
140+
if (attachedEntity != null)
141+
{
142+
Context.Entry(attachedEntity).CurrentValues.SetValues(entity);
143+
144+
return;
145+
}
146+
}
147+
}
148+
149+
// default
150+
entry.State = EntityState.Modified;
151+
}
152+
153+
protected override void SaveChanges()
154+
{
155+
Context.SaveChanges();
156+
}
157+
158+
protected override IQueryable<T> BaseQuery(IFetchStrategy<T> fetchStrategy = null)
159+
{
160+
var query = DbSet.AsQueryable();
161+
162+
return fetchStrategy == null ? query : fetchStrategy.IncludePaths.Aggregate(query, (current, path) => current.Include(path));
163+
}
164+
165+
// we override the implementation fro LinqBaseRepository becausee this is built in and doesn't need to find the key column and do dynamic expressions, etc.
166+
protected override T GetQuery(TKey key, TKey2 key2)
167+
{
168+
return DbSet.Find(key, key2);
169+
}
170+
171+
public override void Dispose()
172+
{
173+
Dispose(true);
174+
GC.SuppressFinalize(this);
175+
}
176+
177+
protected virtual void Dispose(bool disposing)
178+
{
179+
if (!disposing) return;
180+
if (Context == null) return;
181+
182+
Context.Dispose();
183+
Context = null;
184+
}
185+
}
186+
187+
public class EfCoreCompoundKeyRepositoryBase<T, TKey, TKey2, TKey3> : LinqCompoundKeyRepositoryBase<T, TKey, TKey2, TKey3> where T : class
188+
{
189+
protected DbSet<T> DbSet { get; private set; }
190+
protected DbContext Context { get; private set; }
191+
192+
internal EfCoreCompoundKeyRepositoryBase(DbContext dbContext, ICompoundKeyCachingStrategy<T, TKey, TKey2, TKey3> cachingStrategy = null)
193+
: base(cachingStrategy)
194+
{
195+
Initialize(dbContext);
196+
}
197+
198+
private void Initialize(DbContext dbContext)
199+
{
200+
Context = dbContext;
201+
DbSet = Context.Set<T>();
202+
}
203+
204+
protected override void AddItem(T entity)
205+
{
206+
DbSet.Add(entity);
207+
}
208+
209+
protected override void DeleteItem(T entity)
210+
{
211+
DbSet.Remove(entity);
212+
}
213+
214+
protected override void UpdateItem(T entity)
215+
{
216+
var entry = Context.Entry<T>(entity);
217+
218+
if (entry.State == EntityState.Detached)
219+
{
220+
TKey key;
221+
TKey2 key2;
222+
TKey3 key3;
223+
224+
if (GetPrimaryKey(entity, out key, out key2, out key3))
225+
{
226+
// check to see if this item is already attached
227+
// if it is then we need to copy the values to the attached value instead of changing the State to modified since it will throw a duplicate key exception
228+
// specifically: "An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key."
229+
var attachedEntity = Context.Set<T>().Find(key, key2, key3);
230+
if (attachedEntity != null)
231+
{
232+
Context.Entry(attachedEntity).CurrentValues.SetValues(entity);
233+
return;
234+
}
235+
}
236+
}
237+
238+
// default
239+
entry.State = EntityState.Modified;
240+
}
241+
242+
protected override void SaveChanges()
243+
{
244+
Context.SaveChanges();
245+
}
246+
247+
protected override IQueryable<T> BaseQuery(IFetchStrategy<T> fetchStrategy = null)
248+
{
249+
var query = DbSet.AsQueryable();
250+
251+
return fetchStrategy == null ? query : fetchStrategy.IncludePaths.Aggregate(query, (current, path) => current.Include(path));
252+
}
253+
254+
// we override the implementation fro LinqBaseRepository becausee this is built in and doesn't need to find the key column and do dynamic expressions, etc.
255+
protected override T GetQuery(TKey key, TKey2 key2, TKey3 key3)
256+
{
257+
return DbSet.Find(key, key2, key3);
258+
}
259+
260+
public override void Dispose()
261+
{
262+
Dispose(true);
263+
GC.SuppressFinalize(this);
264+
}
265+
266+
protected virtual void Dispose(bool disposing)
267+
{
268+
if (!disposing) return;
269+
if (Context == null) return;
270+
271+
Context.Dispose();
272+
Context = null;
273+
}
274+
}
275+
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
using Microsoft.EntityFrameworkCore;
2+
using SharpRepository.Repository;
3+
using SharpRepository.Repository.Configuration;
4+
using SharpRepository.Repository.Ioc;
5+
using System;
6+
7+
namespace SharpRepository.EfCoreRepository
8+
{
9+
public class EfCoreConfigRepositoryFactory : ConfigRepositoryFactory
10+
{
11+
public EfCoreConfigRepositoryFactory(IRepositoryConfiguration config)
12+
: base(config)
13+
{
14+
}
15+
16+
public override ICompoundKeyRepository<T> GetCompoundKeyInstance<T>()
17+
{
18+
throw new NotImplementedException();
19+
}
20+
21+
public override IRepository<T> GetInstance<T>()
22+
{
23+
return new EfCoreRepository<T>(GetDbContext());
24+
}
25+
26+
public override IRepository<T, TKey> GetInstance<T, TKey>()
27+
{
28+
return new EfCoreRepository<T, TKey>(GetDbContext());
29+
}
30+
31+
public override ICompoundKeyRepository<T, TKey, TKey2> GetInstance<T, TKey, TKey2>()
32+
{
33+
return new EfCoreRepository<T, TKey, TKey2>(GetDbContext());
34+
}
35+
36+
public override ICompoundKeyRepository<T, TKey, TKey2, TKey3> GetInstance<T, TKey, TKey2, TKey3>()
37+
{
38+
return new EfCoreRepository<T, TKey, TKey2, TKey3>(GetDbContext());
39+
}
40+
41+
private DbContext GetDbContext()
42+
{
43+
var connectionString = RepositoryConfiguration["connectionString"];
44+
45+
// check for required parameters
46+
if (RepositoryDependencyResolver.Current == null && string.IsNullOrEmpty(connectionString))
47+
{
48+
throw new ConfigurationErrorsException("The connectionString attribute is required in order to use the EfCoreRepository via the configuration file, unless you set the RepositoryDependencyResolver to use an Ioc container.");
49+
}
50+
51+
Type dbContextType = null;
52+
53+
var tmpDbContextType = RepositoryConfiguration["dbContextType"];
54+
if (!String.IsNullOrEmpty(tmpDbContextType))
55+
{
56+
dbContextType = Type.GetType(tmpDbContextType);
57+
}
58+
59+
// TODO: look at dbContextType (from Enyim.Caching configuration bits) and how it caches, see about implementing cache or expanding FastActivator to take parameters
60+
DbContext dbContext = null;
61+
62+
// if there is an IOC dependency resolver configured then use that one to get the DbContext, this will allow sharing of context across multiple repositories if the IOC is configured that way
63+
if (RepositoryDependencyResolver.Current != null)
64+
{
65+
dbContext = dbContextType == null
66+
? RepositoryDependencyResolver.Current.Resolve<DbContext>()
67+
: (DbContext)RepositoryDependencyResolver.Current.Resolve(dbContextType);
68+
69+
// if the Ioc container doesn't throw an error but still returns null we need to alert the consumer
70+
if (dbContext == null)
71+
{
72+
throw new RepositoryDependencyResolverException(typeof(DbContext));
73+
}
74+
}
75+
else // the default way of getting a DbContext if there is no Ioc container setup
76+
{
77+
var options = new DbContextOptionsBuilder();
78+
dbContext = dbContextType == null
79+
? new DbContext(options.Options)
80+
: (DbContext)Activator.CreateInstance(dbContextType, connectionString);
81+
}
82+
83+
return dbContext;
84+
}
85+
}
86+
}

0 commit comments

Comments
 (0)