About two years ago, I wrote a post and an article in CodeProject about the repository pattern implementation with EF V1. After that, I got a lot of requests in my articles to make it compatible for EF4 POCO support. So here, I am going to do that. In this implementation of repository, I am going to add some new methods which have helped me regularly while working with EF4 POCO support.
Hope it will help you - here, BaseContext
is inherited from ObjectContext
and BaseDataContract
is base class of all my entities as POCO.
public enum State
{
Unchanged, Added, Modified, Deleted
}
[DataContractAttribute(IsReference = true)]
public abstract class BaseDataContract
{
public BaseDataContract()
{
}
[DataMember]
public State ObjectState { get; set; }
}
Here, all the methods responsible to do query, return result in ObjectQuery
which have been used for a special reason and that is ObjectQuery.EnablePlanCachingProperty
that indicates whether the query plan should be cached. Plan-caching caches information which is computed as part of putting together the query itself. By caching this, a subsequent execution of the same query (even if you change parameter values) will run much faster than the first one.
This information is cached per app-domain so you will generally benefit from the query cache across multiple client requests to the same web app and the like. Here all DoQuery
methods are responsible to queries and other query method like SelectAll
or Select
methods internally use these DoQuery
methods with various parameters. SelectByKey
is creating a LINQ expression using Expression tree for Primary key and while creating the repository on an entity, you can supply this Primary Key property as string
. While using the POCO as your entity, you can set attribute programming to serve this kind of job.
TrySameValueExist
does the same job by allowing you to set customize field and value comparison there. What we will do is create the expression for you and also add the PrimaryKey
comparison so that it excludes the object that you are querying for (and where PrimaryKey != currentObjectPrimaryKey
).
Add
and Delete
method simply call the AddObject
and DeleteObject
method of ObjectContext
. AddOrAttach
method is for special situations where you don’t know whether object is already added or not. It is expensive since it will do query to database to check the existence.
ChangeObjectStatetoAttach
is a method which updates the state of an entity and also goes for all its navigation/related child objects to change their state in recursive ways. To find out the navigation object, I need to add extension method of ObjectContext
which has been described later in Bulk delete feature. Here, I check the navigation property type whether that is generic type or not. In EF, Generic navigation type is used for Collection of entity in case of Many End and on the other hand normal property type for One or Zero End..This has been checked to determine whether it is database object or not. To call a Generic method:
MethodInfo method = this.GetType().GetMethod("ChangeCollectionObjectStatetoAttach");
MethodInfo generic = method.MakeGenericMethod(baseType);
generic.Invoke(this, new object[] { value, state });
And ChangeCollectionObjectStatetoAttach
method has been called in the same way with the parameter of parent, child/navigation property-value and state. Now it is time to take a look at the Repository Class here:
public class Repository<E, C> : IRepository<E, C>, IDisposable
where E : BaseDataContract
where C : BaseContext
{
private C _ctx;
private string _KeyProperty = "ID";
public string KeyProperty
{
get
{
return _KeyProperty;
}
set
{
_KeyProperty = value;
}
}
public C Session
{
get { return _ctx; }
set { _ctx = value; }
}
public Repository(C session)
{
_ctx = session;
}
public Repository(C session, string keyProperty) : this(session)
{
_KeyProperty = keyProperty;
}
#region IRepository<E,C> Members
public int Save()
{
return _ctx.SaveChanges();
}
public ObjectQuery<E> DoQuery(string entitySetName)
{
_ctx.ContextOptions.LazyLoadingEnabled = true;
return _ctx.CreateQuery<E>("[" + entitySetName + "]");
}
public ObjectQuery<E> DoQuery()
{
_ctx.ContextOptions.LazyLoadingEnabled = true;
return _ctx.CreateQuery<E>("[" + typeof(E).Name + "]");
}
public ObjectQuery<E> DoQuery(string entitySetName, ISpecification<E> where)
{
_ctx.ContextOptions.LazyLoadingEnabled = true;
return
(ObjectQuery<E>)_ctx.CreateQuery<E>("[" + entitySetName + "]")
.Where(where.EvalPredicate);
}
public ObjectQuery<E> DoQuery(ISpecification<E> where)
{
_ctx.ContextOptions.LazyLoadingEnabled = true;
return
(ObjectQuery<E>)_ctx.CreateQuery<E>("[" + typeof(E).Name + "]")
.Where(where.EvalPredicate);
}
public ObjectQuery<E> DoQuery(int maximumRows, int startRowIndex)
{
_ctx.ContextOptions.LazyLoadingEnabled = true;
return (ObjectQuery<E>)_ctx.CreateQuery<E>("[" +
typeof(E).Name + "]").Skip<E>(startRowIndex).Take(maximumRows);
}
public ObjectQuery<E> DoQuery(Expression<Func<E, object>> sortExpression)
{
if (null == sortExpression)
{
return ((IRepository<E, C>)this).DoQuery();
}
return (ObjectQuery<E>)
((IRepository<E, C>)this).DoQuery().OrderBy<E, object>(sortExpression);
}
public ObjectQuery<E> DoQuery(Expression<Func<E,
object>> sortExpression, int maximumRows, int startRowIndex)
{
if (sortExpression == null)
{
return ((IRepository<E, C>)this).DoQuery(maximumRows, startRowIndex);
}
return (ObjectQuery<E>)((IRepository<E, C>)this).DoQuery
(sortExpression).Skip<E>(startRowIndex).Take(maximumRows);
}
public ObjectQuery<E> SelectAll(string entitySetName)
{
return DoQuery(entitySetName);
}
public ObjectQuery<E> SelectAll()
{
try
{
return DoQuery();
}
catch (Exception)
{
throw;
}
}
public ObjectQuery<E> SelectAll
(string entitySetName, ISpecification<E> where)
{
return DoQuery(entitySetName, where);
}
public ObjectQuery<E> SelectAll(ISpecification<E> where)
{
return DoQuery(where);
}
public ObjectQuery<E> SelectAll(int maximumRows, int startRowIndex)
{
return DoQuery(maximumRows, startRowIndex);
}
public ObjectQuery<E> SelectAll(Expression<Func<E, object>> sortExpression)
{
if (null == sortExpression)
{
return DoQuery(sortExpression);
}
return DoQuery(sortExpression);
}
public ObjectQuery<E> SelectAll(Expression<Func<E,
object>> sortExpression, int maximumRows, int startRowIndex)
{
if (sortExpression == null)
{
return DoQuery(maximumRows, startRowIndex);
}
return DoQuery(sortExpression, maximumRows, startRowIndex);
}
public E SelectByKey(string Key)
{
_ctx.ContextOptions.LazyLoadingEnabled = true;
var xParam = Expression.Parameter(typeof(E), typeof(E).Name);
MemberExpression leftExpr = MemberExpression.Property(xParam, this._KeyProperty);
Expression rightExpr = Expression.Constant(Key);
BinaryExpression binaryExpr = MemberExpression.Equal(leftExpr, rightExpr);
Expression<Func<E, bool>> lambdaExpr =
Expression.Lambda<Func<E, bool>>(binaryExpr, new ParameterExpression[] { xParam });
ObjectQuery<E> resultCollection =
((IRepository<E, C>)this).SelectAll(new Specification<E>(lambdaExpr));
if (null != resultCollection && resultCollection.Count() > 0)
{
return resultCollection.First<E;
}
return null;
}
public bool TrySameValueExist(string fieldName, object fieldValue, string key)
{
var xParam = Expression.Parameter(typeof(E), typeof(E).Name);
MemberExpression leftExprFieldCheck = MemberExpression.Property(xParam, fieldName);
Expression rightExprFieldCheck = Expression.Constant(fieldValue);
BinaryExpression binaryExprFieldCheck =
MemberExpression.Equal(leftExprFieldCheck, rightExprFieldCheck);
MemberExpression leftExprKeyCheck = MemberExpression.Property(xParam, this._KeyProperty);
Expression rightExprKeyCheck = Expression.Constant(key);
BinaryExpression binaryExprKeyCheck =
MemberExpression.NotEqual(leftExprKeyCheck, rightExprKeyCheck);
BinaryExpression finalBinaryExpr =
Expression.And(binaryExprFieldCheck, binaryExprKeyCheck);
Expression<Func<E, bool>> lambdaExpr =
Expression.Lambda<Func<E,
bool>>(finalBinaryExpr, new ParameterExpression[] { xParam });
return ((IRepository<E, C>)this).TryEntity(new Specification<E>(lambdaExpr));
}
public bool TryEntity(ISpecification<E> selectSpec)
{
return _ctx.CreateQuery<E>("[" +
typeof(E).Name + "]").Any<E>(selectSpec.EvalPredicate);
}
public int GetCount()
{
return _ctx.CreateQuery<E>("[" + typeof(E).Name + "]").Count();
}
public int GetCount(ISpecification<E> selectSpec)
{
return _ctx.CreateQuery<E>("[" + typeof(E).Name + "]")
.Where(selectSpec.EvalPredicate).Count();
}
public void Delete(E entity)
{
_ctx.DeleteObject(entity);
}
public void Delete(object entity)
{
_ctx.DeleteObject(entity);
}
public void Add(E entity)
{
_ctx.AddObject(entity.GetType().Name, entity);
}
public List<String> AddOrAttach(E entity, EntityKey key = null)
{
try
{
_ctx.ContextOptions.LazyLoadingEnabled = false;
ObjectStateEntry entry = null;
if (null == key )
{
var entitySet = _ctx.GetEntitySet(entity.GetType());
key = _ctx.CreateEntityKey(entitySet.Name, entity);
}
if( null != key )
_ctx.ObjectStateManager.TryGetObjectStateEntry(key, out entry);
if (entry == null)
{
_ctx.AddObject(typeof(E).Name, entity);
_ctx.ObjectStateManager.ChangeObjectState
(entity, StateHelpers.GetEquivelantEntityState(entity.ObjectState));
}
else
{
_ctx.ApplyCurrentValues(typeof(E).Name, entity);
}
List<String> changedCols = new List<string>();
var ose = (entry != null)? entry : _ctx.ObjectStateManager.GetObjectStateEntry(entity);
foreach (var propName in ose.GetModifiedProperties())
{
string pNameValue = propName;
pNameValue += "|" +
ose.OriginalValues[propName] + "|" + ose.CurrentValues[propName];
changedCols.Add(pNameValue);
}
return changedCols;
}
catch (Exception)
{
throw;
}
}
public DbTransaction BeginTransaction()
{
if (_ctx.Connection.State != ConnectionState.Open)
{
_ctx.Connection.Open();
}
return _ctx.Connection.BeginTransaction();
}
public void ChangeStateOfNavigationObject(E entity, State state)
{
if (null != entity)
{
var navigationProperties = _ctx.GetNavigationProperty<C, E>();
if (null != navigationProperties)
{
foreach (var navigationProperty in navigationProperties)
{
var info = entity.GetType().GetProperty(navigationProperty.Name);
var value = info.GetValue(entity, null);
if (value != null)
{
if (((AssociationType)navigationProperty.RelationshipType).IsForeignKey
&&
navigationProperty.FromEndMember.RelationshipMultiplicity !=
RelationshipMultiplicity.Many)
{
Type t = value.GetType();
Type baseType;
if (!t.IsGenericType)
{
baseType = t.BaseType;
if (baseType.BaseType.Equals(typeof(BaseDataContract)))
{
MethodInfo method =
this.GetType().GetMethod("ChangeObjectStatetoAttach");
MethodInfo generic = method.MakeGenericMethod(baseType);
generic.Invoke(this, new object[] { value, state });
}
}
else
{
Type[] genericBases = t.GetGenericArguments();
baseType = genericBases[0];
if (baseType.BaseType.Equals(typeof(BaseDataContract)))
{
MethodInfo method =
this.GetType().GetMethod
("ChangeCollectionObjectStatetoAttach");
MethodInfo generic = method.MakeGenericMethod(baseType);
generic.Invoke(this, new object[] { value, state });
}
}
}
else
{
info.SetValue(entity, null, null);
}
}
}
}
}
}
public void ChangeObjectStatetoAttach<T>(T entity, State state) where T : BaseDataContract
{
if (null != entity)
{
var dataContract = entity as BaseDataContract;
dataContract.ObjectState = state;
EntityKey entityKey = _ctx.CreateEntityKey(
_ctx.GetEntitySet(dataContract.GetType()).Name, dataContract);
ObjectStateEntry storeEntity;
if (_ctx.ObjectStateManager.TryGetObjectStateEntry(entityKey, out storeEntity))
{
_ctx.ApplyCurrentValues(_ctx.GetEntitySet(typeof(T)).Name, entity);
}
else
{
_ctx.AddObject(_ctx.GetEntitySet(dataContract.GetType()).Name, dataContract);
}
_ctx.ObjectStateManager.ChangeObjectState(dataContract,
StateHelpers.
GetEquivelantEntityState
(dataContract.ObjectState));
}
}
public void ChangeCollectionObjectStatetoAttach<T>
(FixupCollection<T> entities, State state)
where T : BaseDataContract
{
if (null != entities)
{
for (int i = 0; i < entities.Count; i++)
{
ChangeObjectStatetoAttach<T>(entities[i], state);
}
}
}
#endregion
#region IDisposable Members
public void Dispose()
{
if (null != _ctx)
{
if (_ctx.Connection.State == ConnectionState.Open)
_ctx.Connection.Close();
_ctx.Dispose();
}
}
#endregion
}
Here is StateHelpers class
which has been used for retrieving the Object State that is compatible with Entity Framework.
public static class StateHelpers
{
public static EntityState GetEquivelantEntityState(State state)
{
switch (state)
{
case State.Added:
return EntityState.Added;
case State.Modified:
return EntityState.Modified;
case State.Deleted:
return EntityState.Deleted;
default:
return EntityState.Unchanged;
}
}
}
To work with some bulk operation and getting related entities on runtime, I have added some extension methods to find out related navigation info:
1: public static List<NavigationProperty> GetNavigationProperty<TObjectContext, T>(
2: this ObjectContext context)
3: where TObjectContext : ObjectContext
4: {
5: var containerName = context.DefaultContainerName;
6: var model = DataSpace.CSpace;
7: var workspace = context.MetadataWorkspace;
8: var container = workspace.GetEntityContainer(containerName, model);
9: EntitySetBase entitySet = context.GetEntitySet(typeof (T));
10:
11: if (entitySet == null)
12: return null;
13:
14:
15: var navigationProps = entitySet.ElementType.Members
16: .Where(m => m.BuiltInTypeKind == BuiltInTypeKind.NavigationProperty
17: )
18: .Cast<NavigationProperty>()
19: .ToList();
20:
21: return navigationProps;
22: }
23:
24: public static EntitySetBase GetEntitySet(this ObjectContext context, Type entityType)
25: {
26: if (context == null)
27: {
28: throw new ArgumentNullException("context");
29: }
30: if (entityType == null)
31: {
32: throw new ArgumentNullException("entityType");
33: }
34: EntityContainer container = context.MetadataWorkspace.GetEntityContainer
(context.DefaultContainerName, DataSpace.CSpace);
35: if (container == null)
36: {
37: return null;
38: }
39: EntitySetBase entitySet = container.BaseEntitySets.Where
(item => item.ElementType.Name.Equals(entityType.Name))
40: .FirstOrDefault();
41: return entitySet;
42: }
Also, some methods in context which I got from my very favorite Matthieu MEZIL post of bulk delete operation:
1: public class BaseContext : ObjectContext, IObjectContextWithBulkOperations
2: {
3: #region Constructors
4:
5: public BaseContext(string connectionString, string containerName)
6: : base(connectionString, containerName)
7: {
8: this.ContextOptions.LazyLoadingEnabled = false;
9: }
10:
11: public BaseContext(EntityConnection connection, string containerName)
12: : base(connection, containerName)
13: {
14: this.ContextOptions.LazyLoadingEnabled = false;
15: }
16:
17: #endregion
18:
19: #region IContext Members
20:
21: public string Save()
22: {
23: string validationErrors;
24: if (ValidateBeforeSave(out validationErrors))
25: {
26:
27: SaveChanges();
28: return "";
29: }
30: return "Data Not Saved due to Validation Errors: " + validationErrors;
31: }
32:
33: public IEnumerable<T> ManagedEntities<T>()
34: {
35: var oses =
36: ObjectStateManager.GetObjectStateEntries
(EntityState.Added | EntityState.Deleted | EntityState.Modified |
37: EntityState.Unchanged | EntityState.Detached);
38: return oses.Where(entry => entry.Entity is T)
39: .Select(entry => (T)entry.Entity);
40: }
41:
42: public bool ValidateBeforeSave(out string validationErrors)
43: {
44: bool isvalid = true;
45: validationErrors = "";
46: return isvalid;
47: }
48:
49: public void ChangeState<T>(State state, T entity) where T : class
50: {
51: ObjectStateManager.ChangeObjectState(entity,
StateHelpers.GetEquivelantEntityState(state));
52: }
53:
54: #endregion
55:
56: public void UpdateTrackedEntity<T>(T modifiedEntity) where T : class
57: {
58: var set = this.CreateObjectSet<T>();
59: set.ApplyCurrentValues(modifiedEntity);
60: }
61:
62: #region IObjectContextWithBulkOperations Member
63:
64: private List<Action> _bulkDeletedActions;
65: private List<Action> BulkDeletedActions
66: {
67: get
68: {
69: if (_bulkDeletedActions == null)
70: _bulkDeletedActions = new List<Action>();
71: return _bulkDeletedActions;
72: }
73: }
74:
75: private List<object> _bulkDeletedEntities;
76: public List<object> BulkDeletedEntities
77: {
78: get
79: {
80: if (_bulkDeletedEntities == null)
81: _bulkDeletedEntities = new List<object>();
82: return _bulkDeletedEntities;
83: }
84: }
85:
86: private Dictionary<Type, List<Func<object, bool>>> _bulkDeletedFuncs;
87: public Dictionary<Type, List<Func<object, bool>>> BulkDeletedFuncs
88: {
89: get
90: {
91: if (_bulkDeletedFuncs == null)
92: _bulkDeletedFuncs =
new Dictionary<Type, List<Func<object, bool>>>();
93: return _bulkDeletedFuncs;
94: }
95: }
96:
97: public void Delete<TBase, T>(ObjectSet<TBase>
entitySet, Expression<Func<T, bool>> predicate)
98: where T : class, TBase
99: where TBase : class
100: {
101: Delete<TBase, T>(entitySet, predicate, true);
102: }
103: public void DeleteLoadedEntities<TBase,
T>(ObjectSet<TBase> entitySet, Expression<Func<T, bool>> predicate)
104: where T : class, TBase
105: where TBase : class
106: {
107: if ((predicate = CalculatePredicate(entitySet, predicate)) != null)
108: Delete<TBase, T>(entitySet, predicate, false);
109: }
110:
111: private Expression<Func<T, bool>> CalculatePredicate<TBase,
T>(ObjectSet<TBase> entitySet, Expression<Func<T, bool>> oldPredicate)
112: where T : class, TBase
113: where TBase : class
114: {
115: IEnumerable<PropertyInfo> keyMembers =
entitySet.EntitySet.ElementType.KeyMembers.Select
(km => typeof(T).GetProperty(km.Name)).ToList();
116: IEnumerable<T> entitiesEnumerable =
ObjectStateManager.GetObjectStateEntries(EntityState.Added |
EntityState.Deleted | EntityState.Modified | EntityState.Unchanged)
117: .Select(ose => ose.Entity)
118: .OfType<T>();
119: ParameterExpression parameter = oldPredicate.Parameters.Single();
120: if (!entitiesEnumerable.Any())
121: return null;
122: return Expression.Lambda<Func<T, bool>>(
123: Expression.AndAlso(
124: oldPredicate.Body,
125: entitiesEnumerable.Select(e =>
126: keyMembers.Select(km =>
127: Expression.Equal(
128: Expression.MakeMemberAccess(parameter, km),
129: Expression.Constant(km.GetValue(e, null))))
130: .Aggregate((accumulate, clause) =>
131: Expression.AndAlso(accumulate, clause)))
132: .Aggregate((accumulate, clause) =>
133: Expression.OrElse(accumulate, clause)))
134: , oldPredicate.Parameters);
135: }
136:
137: private void Delete<TBase, T>(ObjectSet<TBase>
entitySet, Expression<Func<T, bool>>
predicate, bool propagateToFutureEntities)
138: where TBase : class
139: where T : class, TBase
140: {
141: ObjectQuery<T> objectQuery =
(ObjectQuery<T>)entitySet.OfType<T>().Where(predicate);
142: string selectSQLQuery = objectQuery.ToTraceString();
143: List<KeyValuePair<string, List<string>>>
froms = new List<KeyValuePair<string, List<string>>>();
144: Match fromMatch = Regex.Match(entitySet.OfType<T>().ToTraceString"
(FROM|JOIN)[ ]+((\\[[^\\]]+\\]).)*\\[([^\\]]+)\\]");
145: List<AssociationType> ssdlAsscociations =
MetadataWorkspace.GetItems(DataSpace.SSpace).OfType
<AssociationType>().ToList();
146: string firstFrom = null;
147: while (fromMatch.Success)
148: {
149: string fromValue = fromMatch.Groups[4].Value;
150: if (Regex.IsMatch(selectSQLQuery,
string.Format("(FROM|JOIN)[ ]+((\\[[^\\]]+\\]).)*\\[{0}\\]", fromValue)))
151: {
152: var index = (from ssdlAssociation in ssdlAsscociations
153: where ssdlAssociation.ReferentialConstraints.Any
(rc => fromValue == rc.ToProperties.First().DeclaringType.Name)
154: from table in froms.Select((f, i) => new { Table = f, Index = i })
155: where ssdlAssociation.ReferentialConstraints.Any
(rc => table.Table.Key ==
rc.FromProperties.First().DeclaringType.Name)
156: orderby table.Index
157: select new { Index = table.Index,
SSDLAssociation = ssdlAssociation,
FKs = table.Table }).FirstOrDefault();
158: if (index != null)
159: froms.Insert(index.Index, new KeyValuePair<string,
List<string>>(fromValue, (from fk in index.FKs.Value
160: let referentailConstraint = index.SSDLAssociation.
ReferentialConstraints.First(rc => index.FKs.Key ==
rc.FromProperties.First().DeclaringType.Name)
161: select referentailConstraint.ToProperties.ElementAt
(referentailConstraint.FromProperties.Select((p, pIndex) =>
new { p.Name, Index = pIndex }).First
(p => p.Name == fk).Index).Name).ToList()));
162: else
163: {
164: if (firstFrom == null)
165: firstFrom = fromValue;
166: froms.Add(new KeyValuePair<string, List<string>>
(fromValue, MetadataWorkspace.GetItems(DataSpace.SSpace).OfType
<EntityType>().First(et =>
et.Name == fromValue).KeyMembers.Select(km => km.Name).ToList()));
167: }
168: }
169: fromMatch = fromMatch.NextMatch();
170: }
171: StringBuilder delete = new StringBuilder();
172:
173: string selectSQLQueryWithoutSelect =
selectSQLQuery.Substring(selectSQLQuery.IndexOf("FROM"));
174: IEnumerator<EdmMember> keyMembersEnumerator = null;
175:
176: if (froms.Count > 1)
177: {
178: delete.Append("declare @DeleteIds table (");
179: StringBuilder keys = new StringBuilder();
180: keyMembersEnumerator = MetadataWorkspace.GetItems
(DataSpace.SSpace).OfType<EntityType>().
181: First(et => et.Name == firstFrom).KeyMembers.ToList().GetEnumerator();
182: keyMembersEnumerator.MoveNext();
183: for (; ; )
184: {
185: string keyName = keyMembersEnumerator.Current.Name;
186: keys.Append(keyName);
187: delete.Append(keyName);
188: delete.Append(" ");
189: delete.Append(keyMembersEnumerator.Current.TypeUsage.EdmType.Name);
190: Facet maxLength =
keyMembersEnumerator.Current.TypeUsage.Facets.FirstOrDefault
(f => f.Name == "MaxLength");
191: if (maxLength != null)
192: {
193: delete.Append("(");
194: delete.Append(maxLength.Value);
195: delete.Append(")");
196: }
197: if (keyMembersEnumerator.MoveNext())
198: {
199: keys.Append(", ");
200: delete.Append(", ");
201: }
202: else
203: break;
204: }
205: delete.Append(");\n");
206:
207: delete.Append("INSERT INTO @DeleteIds SELECT ");
208: delete.Append(keys.ToString());
209: delete.Append(" ");
210: delete.Append(selectSQLQueryWithoutSelect.Replace("@p__linq__", "@p"));
211: delete.Append(";\n");
212: }
213:
214: foreach (KeyValuePair<string, List<string>> from in froms)
215: {
216: delete.Append("DELETE FROM [");
217: delete.Append(from.Key);
218: delete.Append("] FROM ");
219:
220: if (froms.Count > 1)
221: {
222: delete.Append("[");
223: delete.Append(from.Key);
224: delete.Append("]");
225: delete.Append("INNER JOIN @deleteIds D ON ");
226:
227: keyMembersEnumerator.Reset();
228: keyMembersEnumerator.MoveNext();
229: int index = 0;
230: for (; ; )
231: {
232: delete.Append("[");
233: delete.Append(from.Key);
234: delete.Append("].");
235: delete.Append(from.Value[index++]);
236: delete.Append(" = D.");
237: delete.Append(keyMembersEnumerator.Current);
238:
239: if (keyMembersEnumerator.MoveNext())
240: delete.Append(" AND ");
241: else
242: break;
243: }
244: }
245: else
246: delete.Append(selectSQLQueryWithoutSelect.Substring(4).TrimStart());
247:
248: delete.Append(";\n");
249: }
250:
251: BulkDeletedActions.Add(() => ExecuteStoreCommand
(delete.ToString(), objectQuery.Parameters.Select(p => p.Value).ToArray()));
252:
253: Func<T, bool> predicateCompiled = predicate.Compile();
254: Func<object, bool> predicateCompiledObject = o =>
255: {
256: T t = o as T;
257: if (t == null)
258: return false;
259: return predicateCompiled(t);
260: };
261: if (propagateToFutureEntities)
262: {
263: List<Func<object, bool>> bulkDeletedFuncs;
264: if (BulkDeletedFuncs.TryGetValue(typeof(TBase), out bulkDeletedFuncs))
265: bulkDeletedFuncs.Add(predicateCompiledObject);
266: else
267: BulkDeletedFuncs.Add(typeof(TBase),
new List<Func<object, bool>>() { predicateCompiledObject });
268: }
269: EntityType entityType = MetadataWorkspace.GetItems(DataSpace.CSpace).
OfType<EntityType>().First(et => et.Name == typeof(T).Name);
270: var oneToOneSubEntityTypes = (from np in entityType.NavigationProperties
271: where np.FromEndMember.RelationshipMultiplicity ==
RelationshipMultiplicity.One && np.ToEndMember.
RelationshipMultiplicity == RelationshipMultiplicity.One
272: let otherEntityType = np.ToEndMember.GetEntityType()
273: let otherNavigationProperty =
otherEntityType.NavigationProperties.FirstOrDefault
(otherNP =>
otherNP.RelationshipType == np.RelationshipType)
274: select new
275: {
276: EntityType = otherEntityType,
277: ClrType = typeof(T).GetProperty(np.Name).PropertyType,
278: OtherNavigationPropertyName =
otherNavigationProperty == null ?
null : otherNavigationProperty.Name,
279: ReferencialConstraint =
((AssociationType)np.RelationshipType).
ReferentialConstraints.FirstOrDefault()
280: }).ToList();
281: foreach (var subEntityTypeLoop in oneToOneSubEntityTypes)
282: {
283: var subEntityType = subEntityTypeLoop;
284: if (subEntityType.OtherNavigationPropertyName != null)
285: {
286: List<string> entityTypeKeys, subEntityTypeKeys;
287: if (subEntityType.ReferencialConstraint.
FromProperties.First().DeclaringType == entityType)
288: {
289: entityTypeKeys = subEntityType.ReferencialConstraint.FromProperties.Select
(p => p.Name).ToList();
290: subEntityTypeKeys = subEntityType.ReferencialConstraint.ToProperties.Select
(p => p.Name).ToList();
291: }
292: else
293: {
294: entityTypeKeys =
subEntityType.ReferencialConstraint.ToProperties.Select
(p => p.Name).ToList();
295: subEntityTypeKeys =
subEntityType.ReferencialConstraint.FromProperties.Select
(p => p.Name).ToList();
296: }
297: ParameterExpression entityParameter =
Expression.Parameter(typeof(object), "entity");
298: ParameterExpression subEntityParameter =
Expression.Parameter(typeof(object), "subEntity");
299: Func<object, object, bool> associateToBulkEntities =
300: Expression.Lambda<Func<object, object, bool>>(
301: entityTypeKeys.Select((entityTypeKey, keyIndex) =>
302: Expression.Equal(
303: Expression.MakeMemberAccess(
304: Expression.Convert(
305: subEntityParameter,
306: subEntityType.ClrType),
307: subEntityType.ClrType.GetProperty
(subEntityTypeKeys[keyIndex])),
308: Expression.MakeMemberAccess(
309: Expression.Convert(
310: entityParameter,
311: typeof(T)),
312: typeof(T).GetProperty(entityTypeKey)))).
313: Aggregate((accumulate, keyPredicate) =>
Expression.AndAlso(accumulate, keyPredicate)),
314: entityParameter,
315: subEntityParameter).
316: Compile();
317: Func<object, bool> npPredicate = subE =>
BulkDeletedEntities.OfType<T>().Any(e => associateToBulkEntities(e, subE));
318:
319: List<Func<object, bool>> bulkDeletedFuncs;
320: if (BulkDeletedFuncs.TryGetValue(subEntityType.ClrType, out bulkDeletedFuncs))
321: bulkDeletedFuncs.Add(npPredicate);
322: else
323: BulkDeletedFuncs.Add(subEntityType.ClrType,
new List<Func<object, bool>>() { npPredicate });
324: }
325: }
326: foreach (var entity in ObjectStateManager.GetObjectStateEntries
(EntityState.Added | EntityState.Deleted |
EntityState.Modified | EntityState.Unchanged).
327: Select(ose => new { Entity = ose.Entity as T, ose.State }).
328: Where(e => e.Entity != null && predicateCompiled(e.Entity)))
329: {
330: if (entity.State != EntityState.Deleted)
331: DeleteObjectAndAddThemIntoBulkDeletedEntities(entity.Entity);
332: else
333: {
334: BulkDeletedEntities.Add(entity.Entity);
335: foreach (var subEntity in oneToOneSubEntityTypes.
336: SelectMany(subEntityType =>
337: ObjectStateManager.GetObjectStateEntries(EntityState.Added |
EntityState.Deleted | EntityState.Modified | EntityState.Unchanged).
338: Where(ose => subEntityType.ClrType.IsAssignableFrom
(ose.Entity.GetType()) && !BulkDeletedEntities.Contains(ose.Entity))))
339: ApplyBulkDeletedFuncs(subEntity.Entity, subEntity.State);
340: }
341: }
342: }
343:
344: private void ApplyBulkDeletedFuncs(object entity, EntityState entityState)
345: {
346: List<Func<object, bool>> bulkDeletedFuncs;
347: if (_bulkDeletedFuncs != null)
348: {
349: Type t = entity.GetType();
350: do
351: {
352: if (BulkDeletedFuncs.TryGetValue(t, out bulkDeletedFuncs))
353: foreach (Func<object, bool> bulkDeletedFunc in bulkDeletedFuncs)
354: if (bulkDeletedFunc(entity))
355: {
356: if (entityState != EntityState.Deleted)
357: DeleteObjectAndAddThemIntoBulkDeletedEntities(entity);
358: else
359: BulkDeletedEntities.Add(entity);
360: return;
361: }
362: } while ((t = t.BaseType) != null);
363: }
364: }
365:
366: private void DeleteObjectAndAddThemIntoBulkDeletedEntities(object entity)
367: {
368: CollectionChangeEventHandler objectStateManagerObjectStateManagerChanged =
(sender, e) => BulkDeletedEntities.Add(e.Element);
369: ObjectStateManager.ObjectStateManagerChanged +=
objectStateManagerObjectStateManagerChanged;
370: DeleteObject(entity);
371: ObjectStateManager.ObjectStateManagerChanged -=
objectStateManagerObjectStateManagerChanged;
372: BulkDeletedEntities.Add(entity);
373: }
374: #endregion
375:
376: }
You can add or remove these features according to your needs. Here, I have tried to provide you some of the written code which I think is used more or less in every application.
Repository is kind of mediator to connect business layer with data access. So your business layer should be aware of this repository class. I would also like to suggest that you can do some refactoring on my repository class by introducing UoW as umbrella over multiple repositories and shared by all repositories in place of the object context directly and method like save, transaction can be switched over there. Hope it helps. Good luck!
In my childhood, my uncle has shown me how to see the cloud in a close look and I understand that one can draw some elements of the Earth in the sky-canvas if he/she wants to. After that the cloud becomes closer to me and It teaches me one thing that, a deeper-look to something will give you some clues to draw your imagination. You can able to see that one which you have build-up in your mind.
Years past, I have started my career as a software engineer and has been looking for passion in my coding and development which I should be to enjoy my profession and has started asking myself- 'am I doing any engineering here?!' Is my code becoming that thing which I have designed in my mind? So to find that answer I have tried that old solution here... I have decided to come closer to my code and start analyzing them. And it is really working for me and at least it gives me the confidence that I can build something that I really want to. I can draw my thinking there through my code and can build-up my vision that I have designed in my mind. It also helps me to think out of the box, solve each problems by making blocks and make me careful on each steps.
• Morshed's Technical Blog site: http://morshedanwar.wordpress.com/
• Morshed's Technical articles those are published in Codeproject site: http://www.codeproject.com/script/Articles/MemberArticles.aspx?amid=2992452
• Morshed's Linkedin profile: http://www.linkedin.com/in/morshedanwar
• Morshed's Facebook Profile : http://www.facebook.com/morshed.pulok
Beside all these I like to do - photography and music. Here is my Flickr photos : http://www.flickr.com/photos/morshed_anwar/