Skip to content

Instantly share code, notes, and snippets.

@seralexeev
Last active August 29, 2015 14:05
Show Gist options
  • Save seralexeev/fd47feb40385552683b7 to your computer and use it in GitHub Desktop.
Save seralexeev/fd47feb40385552683b7 to your computer and use it in GitHub Desktop.
Preparing to save with EntityFramework
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