Skip to content

Instantly share code, notes, and snippets.

@vkhorikov
Created May 30, 2017 16:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vkhorikov/99c43fada0575b0751825a560a884732 to your computer and use it in GitHub Desktop.
Save vkhorikov/99c43fada0575b0751825a560a884732 to your computer and use it in GitHub Desktop.
Source code for the 2nd code review
public class Fabric : AggregateRootWithLinkedObjects
{
public bool PreOrder { get; protected set; }
private string _Due { get; set; }
public Maybe<string> Due
{
get => _Due;
protected set => _Due = value.Unwrap();
}
protected virtual ICollection<FabricImage> _Images { get; set; }
public IEnumerable<FabricImage> Images => _Images.Skip(0);
protected virtual ICollection<FabricTag> _Tags { get; set; }
public IEnumerable<FabricTag> Tags => _Tags.Skip(0);
protected Fabric()
{
_Images = new Collection<FabricImage>();
_Tags = new Collection<FabricTag>();
}
internal static Fabric Create(int[] imageIds, int[] tagIds, bool preOrder, Maybe<string> due)
{
Guard.ForDuplicates(imageIds, "Images");
Guard.ForDuplicates(tagIds, "Tags");
var fabric = new Fabric();
if (preOrder)
{
if (due.HasNoValue)
{
throw new InvalidOperationException("A pre ordered fabric must have due set");
}
fabric.SetPreOrder(due.Value);
}
else
{
if (due.HasValue)
{
throw new InvalidOperationException("A pre ordered fabric does not need a due set");
}
}
fabric._Images = imageIds
.Select(id => new FabricImage(fabric.Id, id))
.ToList();
fabric.MakeFirstImageDefault();
fabric._Tags = tagIds
.Select(id => new FabricTag(fabric.Id, id))
.ToList();
return fabric;
}
private void SetPreOrder(string due)
{
PreOrder = true;
Due = due;
}
private void HasArrived()
{
PreOrder = false;
Due = null;
}
private void MakeFirstImageDefault()
{
FabricImage firstImage = _Images.FirstOrDefault();
if (firstImage != null)
{
firstImage.SetIsDefault(true);
}
}
internal void Update(Fabric update)
{
if (update.PreOrder)
{
SetPreOrder(update.Due.Value);
}
else
{
HasArrived();
}
UpdateLinkedObjects(_Tags, update.Tags, AddFabricTag);
UpdateLinkedObjects(_Images, update.Images, AddFabricImage);
UpdateDefaultImage(update);
}
private void UpdateDefaultImage(Fabric update)
{
int? newDefaultImageId = update.GetDefaultImageId();
if (newDefaultImageId == null)
return;
Maybe<FabricImage> oldDefaultImageOrNothing = GetDefaultImage();
if (oldDefaultImageOrNothing.HasValue)
{
var oldDefaultImage = oldDefaultImageOrNothing.Value;
if (oldDefaultImage.ImageId == newDefaultImageId)
return;
oldDefaultImage.SetIsDefault(false);
}
var newDefaultImage = _Images.SingleOrDefault(i => i.ImageId == newDefaultImageId);
newDefaultImage.SetIsDefault(true);
}
public Maybe<FabricImage> GetDefaultImage()
{
return _Images.SingleOrDefault(i => i.IsDefault);
}
internal int? GetDefaultImageId()
{
if (_Images.Count > 0)
{
return GetDefaultImage().Value.ImageId;
}
return null;
}
private void AddFabricImage(int fabricId, int imageId)
{
var fabricImage = new FabricImage(fabricId, imageId);
_Images.Add(fabricImage);
}
private void AddFabricTag(int fabricId, int tagId)
{
var fabricTag = new FabricTag(fabricId, tagId);
_Tags.Add(fabricTag);
}
}
public abstract class AggregateRootWithLinkedObjects : AggregateRoot<int>
{
/// <summary>
/// This method should not be accessible outside its class. It has to be because there is no way to have internal and
/// protected at the same time (or is allowed)
/// </summary>
internal void UpdateLinkedObjects<T>(IEnumerable<ILinkToAggregate<T>> linkedObjectsToUpdate,
IEnumerable<ILinkToAggregate<T>> updates,
Action<int, int> addNewLinkedObjectToCollection)
{
IReadOnlyList<int> newLinkedObjectIds = GetIdsFromLinkedObjects(updates);
UpdateLinkedObjects(linkedObjectsToUpdate, newLinkedObjectIds, addNewLinkedObjectToCollection);
}
/// <summary>
/// This method should not be accessible outside its class.
/// </summary>
internal void UpdateLinkedObjects<T>(IEnumerable<ILinkToAggregate<T>> linkedObjectsToUpdate,
IReadOnlyList<int> newLinkedObjectIds,
Action<int, int> addNewLinkedObjectToCollection)
{
IReadOnlyList<int> oldLinkedObjectIds = GetIdsFromLinkedObjects(linkedObjectsToUpdate);
IEnumerable<ILinkToAggregate<T>> linkedObjectsToDelete = GetLinkedObjectsToDelete(linkedObjectsToUpdate, newLinkedObjectIds);
IEnumerable<ILinkToAggregate<T>> linkedObjectsToKeep = linkedObjectsToUpdate.Except(linkedObjectsToDelete);
IEnumerable<int> linkedObjectIdsToAdd = newLinkedObjectIds.Except(oldLinkedObjectIds);
MarkLinkedObjectsForDeletion(linkedObjectsToDelete);
MarkLinkedObjectsForKeeping(linkedObjectsToKeep);
CreateAndAddNewLinkedObjects<T>(linkedObjectIdsToAdd, addNewLinkedObjectToCollection);
}
private IReadOnlyList<int> GetIdsFromLinkedObjects<T>(IEnumerable<ILinkToAggregate<T>> linkedObjects)
{
return linkedObjects.Select(t => t.LinkedObjectId).ToList();
}
private IEnumerable<ILinkToAggregate<T>> GetLinkedObjectsToDelete<T>(IEnumerable<ILinkToAggregate<T>> linkedObjectsToUpdate, IReadOnlyList<int> newLinkedObjectIds)
{
return linkedObjectsToUpdate.Where(t => !newLinkedObjectIds.Contains(t.LinkedObjectId));
}
private void MarkLinkedObjectsForDeletion<T>(IEnumerable<ILinkToAggregate<T>> linkedObjectsToDelete)
{
foreach (ILinkToAggregate<T> linkedObject in linkedObjectsToDelete)
{
linkedObject.State = ObjectState.Deleted;
}
}
private void MarkLinkedObjectsForKeeping<T>(IEnumerable<ILinkToAggregate<T>> linkedObjectsToKeep)
{
foreach (ILinkToAggregate<T> linkedObject in linkedObjectsToKeep)
{
linkedObject.State = ObjectState.Unchanged;
}
}
private void CreateAndAddNewLinkedObjects<T>(IEnumerable<int> linkedObjectIdsToAdd,
Action<int, int> addNewLinkedObjectToCollection)
{
foreach (int linkedObjectId in linkedObjectIdsToAdd)
{
addNewLinkedObjectToCollection(Id, linkedObjectId);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment