Last active
August 29, 2015 14:05
-
-
Save seralexeev/fd47feb40385552683b7 to your computer and use it in GitHub Desktop.
Preparing to save with EntityFramework
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Linq.Expressions; | |
using System.Reflection; | |
using Framework.Maybe; | |
namespace Base.DAL.EF | |
{ | |
public class ObjectSaver<TEntity> : IObjectSaver<TEntity> where TEntity : BaseObject | |
{ | |
private readonly IUnitOfWork _unitOfWork; | |
private readonly TEntity _objSrc; | |
private readonly TEntity _objDest; | |
private readonly bool _isNew; | |
public bool IsNew { get { return _isNew; } } | |
public TEntity Dest { get { return _objDest; } } | |
public TEntity Src { get { return _objSrc; } } | |
public ObjectSaver(IUnitOfWork unitOfWork, TEntity objSrc, TEntity objDest = null) | |
{ | |
#region Requires | |
if (unitOfWork == null) throw new ArgumentNullException("unitOfWork"); | |
if (objSrc == null) throw new ArgumentNullException("objSrc"); | |
#endregion | |
_unitOfWork = unitOfWork; | |
_objSrc = objSrc; | |
_isNew = objSrc.ID == 0; | |
if (_isNew) | |
{ | |
_objDest = objSrc; | |
} | |
else | |
{ | |
_objDest = objDest ?? _unitOfWork.GetRepository<TEntity>().Find(x => x.ID == _objSrc.ID); | |
_objSrc.ToObject(_objDest); | |
} | |
} | |
#region Stuff | |
/// <summary> | |
/// Returns new ObjectSaver with current context for derrived Type | |
/// </summary> | |
/// <typeparam name="TObject"></typeparam> | |
/// <returns></returns> | |
public IObjectSaver<TObject> AsObjectSaver<TObject>() where TObject : BaseObject | |
{ | |
return new ObjectSaver<TObject>(_unitOfWork, _objSrc as TObject); | |
} | |
#endregion | |
#region Many to many | |
/// <summary> | |
/// Prepare object to save with Entity Framework for Many to Many relationship | |
/// </summary> | |
/// <typeparam name="TEntry">Object type that inherits from BaseObject, type of collection entry</typeparam> | |
/// <param name="property">Many to Many navigation property</param> | |
/// <returns>ObjectSaver for chain support</returns> | |
public IObjectSaver<TEntity> SaveManyToMany<TEntry>(Func<TEntity, ICollection<TEntry>> property) where TEntry : BaseObject | |
{ | |
ICollection<TEntry> objSrcPropValue = property(_objSrc); | |
ICollection<TEntry> objDestPropValue = property(_objDest); | |
if (objSrcPropValue != null) | |
{ | |
int[] iDs = objSrcPropValue.Where(x => x.ID != 0).Select(x => x.ID).Distinct().ToArray(); | |
objDestPropValue.Clear(); | |
foreach (TEntry t in _unitOfWork.GetRepository<TEntry>().Filter(x => iDs.Contains(x.ID)).AsEnumerable()) | |
objDestPropValue.Add(t); | |
} | |
else if (objDestPropValue != null) | |
{ | |
objDestPropValue.Clear(); | |
} | |
return this; | |
} | |
#endregion | |
#region One object | |
/// <summary> | |
/// Prepare object to save with Entity Framework for One object relationship | |
/// </summary> | |
/// <typeparam name="TProperty">Object type that inherits from BaseObject, type of entry</typeparam> | |
/// <param name="property">One object navigation property</param> | |
/// <returns>ObjectSaver for chain support</returns> | |
public IObjectSaver<TEntity> SaveOneObject<TProperty>(Expression<Func<TEntity, TProperty>> property) | |
where TProperty : BaseObject | |
{ | |
#region Requires | |
if (property == null) throw new ArgumentNullException("property"); | |
Func<TEntity, TProperty> propertyFunc = property.Compile(); | |
MemberExpression propertyExpression = property.Body as MemberExpression; | |
if (propertyExpression == null) throw new Exception("propertyExpression"); | |
PropertyInfo propertyInfo = propertyExpression.Member as PropertyInfo; | |
if (propertyInfo == null) throw new Exception("propertyInfo"); | |
#endregion | |
TProperty objSrcPropValue = propertyFunc(_objSrc); | |
propertyInfo.GetValue(_objDest); // awesome entity framework lazy loading impl :) not working if you didn't get navigation value | |
propertyInfo.SetValue(_objDest, objSrcPropValue != null ? _unitOfWork.GetRepository<TProperty>().Find(x => x.ID == objSrcPropValue.ID) : null); | |
// FileData save issue | |
//propertyInfo.SetValue(_objDest, | |
// objSrcPropValue != null ? (objSrcPropValue is FileData ? objSrcPropValue : _unitOfWork.GetRepository<TProperty>().Find(x => x.ID == objSrcPropValue.ID)) : null); | |
return this; | |
} | |
#endregion | |
#region One to many | |
/// <summary> | |
/// Prepare object to save with Entity Framework for One to Many relationship | |
/// </summary> | |
/// <typeparam name="TEntry">Object type that inherits from BaseObject, type of collection entry</typeparam> | |
/// <param name="property">Many to Many navigation property</param> | |
/// <returns>ObjectSaver for chain support</returns> | |
public IObjectSaver<TEntity> SaveOneToMany<TEntry>(Func<TEntity, ICollection<TEntry>> property) where TEntry : BaseObject | |
{ | |
return this.SaveOneToManyMethodImplementation(property, false, null); | |
} | |
/// <summary> | |
/// Prepare object to save with Entity Framework for One to Many relationship | |
/// </summary> | |
/// <typeparam name="TEntry">Object type that inherits from BaseObject, type of collection entry</typeparam> | |
/// <param name="property">Many to Many navigation property</param> | |
/// <param name="entrySaverDelegate">Entry saver delegate</param> | |
/// <returns>ObjectSaver for chain support</returns> | |
public IObjectSaver<TEntity> SaveOneToMany<TEntry>(Func<TEntity, ICollection<TEntry>> property, | |
Action<IObjectSaver<TEntry>> entrySaverDelegate) where TEntry : BaseObject | |
{ | |
return this.SaveOneToManyMethodImplementation(property, false, entrySaverDelegate); | |
} | |
/// <summary> | |
/// Prepare object to save with Entity Framework for One to Many relationship | |
/// </summary> | |
/// <typeparam name="TEntry">Object type that inherits from BaseObject, type of collection entry</typeparam> | |
/// <param name="property">Many to Many navigation property</param> | |
/// <param name="makeHiddenWhenDelete">If true object hides instead deleteing</param> | |
/// <returns>ObjectSaver for chain support</returns> | |
public IObjectSaver<TEntity> SaveOneToMany<TEntry>(Func<TEntity, ICollection<TEntry>> property, | |
bool makeHiddenWhenDelete) where TEntry : BaseObject | |
{ | |
return this.SaveOneToManyMethodImplementation(property, makeHiddenWhenDelete, null); | |
} | |
/// <summary> | |
/// Prepare object to save with Entity Framework for One to Many relationship | |
/// </summary> | |
/// <typeparam name="TEntry">Object type that inherits from BaseObject, type of collection entry</typeparam> | |
/// <param name="property">Many to Many navigation property</param> | |
/// <param name="makeHiddenWhenDelete">If true object hides instead deleteing</param> | |
/// <param name="entrySaverDelegate">Entry saver delegate</param> | |
/// <returns>ObjectSaver for chain support</returns> | |
public IObjectSaver<TEntity> SaveOneToMany<TEntry>(Func<TEntity, ICollection<TEntry>> property, bool makeHiddenWhenDelete, | |
Action<IObjectSaver<TEntry>> entrySaverDelegate) where TEntry : BaseObject | |
{ | |
return this.SaveOneToManyMethodImplementation(property, makeHiddenWhenDelete, entrySaverDelegate); | |
} | |
protected IObjectSaver<TEntity> SaveOneToManyMethodImplementation<TEntry>(Func<TEntity, ICollection<TEntry>> property, bool makeHiddenWhenDelete, | |
Action<IObjectSaver<TEntry>> entrySaverDelegate) where TEntry : BaseObject | |
{ | |
#region Requires | |
if (property == null) throw new ArgumentNullException("property"); | |
#endregion | |
ICollection<TEntry> newCollection = property(_objSrc).With(x => x.ToList()); | |
IRepository<TEntry> entryRepository = _unitOfWork.GetRepository<TEntry>(); | |
ICollection<TEntry> objectEntriesDest = property(_objDest); | |
if (newCollection != null) | |
{ | |
if (!_isNew) | |
{ | |
#region Update existing entries | |
ICollection<TEntry> toUpdateSrc = newCollection.Where(x => x.ID != 0 && !x.Hidden).ToList(); | |
foreach (TEntry entry in toUpdateSrc) | |
{ | |
ObjectSaver<TEntry> entrySaver = new ObjectSaver<TEntry>(_unitOfWork, entry); | |
if (entrySaverDelegate != null) entrySaverDelegate(entrySaver); | |
} | |
#endregion | |
#region Delete entries | |
IEnumerable<int> toUpdateIDs = toUpdateSrc.Select(x => x.ID); | |
ICollection<TEntry> toDeleteDest = objectEntriesDest.Where(x => !toUpdateIDs.Contains(x.ID) && x.ID != 0).ToList(); | |
if (!makeHiddenWhenDelete) | |
{ | |
foreach (TEntry entry in toDeleteDest) | |
{ | |
objectEntriesDest.Remove(entry); | |
newCollection.Remove(entry); | |
entryRepository.Delete(entry); | |
} | |
} | |
else | |
{ | |
foreach (TEntry entry in toDeleteDest) | |
{ | |
entry.Hidden = true; | |
entryRepository.Update(entry); | |
} | |
} | |
#endregion | |
} | |
#region Create new entries | |
ICollection<TEntry> toCreate = newCollection.Where(x => x.ID == 0).ToList(); | |
foreach (var entry in toCreate) | |
{ | |
ObjectSaver<TEntry> entrySaver = new ObjectSaver<TEntry>(_unitOfWork, entry); | |
if (entrySaverDelegate != null) entrySaverDelegate(entrySaver); | |
if (!_isNew) objectEntriesDest.Add(entrySaver.Dest); | |
} | |
#endregion | |
} | |
else if (!_isNew) | |
{ | |
foreach (TEntry entry in objectEntriesDest.ToList()) | |
{ | |
objectEntriesDest.Remove(entry); | |
entryRepository.Delete(entry); | |
} | |
} | |
return this; | |
} | |
#endregion | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment