428 lines
14 KiB
C#
428 lines
14 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Collections.Specialized;
|
|
using System.ComponentModel;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Runtime.Serialization;
|
|
using NUnit.Framework;
|
|
using Umbraco.Core;
|
|
using Umbraco.Core.Models;
|
|
using Umbraco.Core.Models.EntityBase;
|
|
using Umbraco.Tests.TestHelpers.Entities;
|
|
|
|
namespace Umbraco.Tests.Models.Collections
|
|
{
|
|
[TestFixture]
|
|
public class PropertyCollectionTests
|
|
{
|
|
[Test]
|
|
public void SimpleOrder_Returns_Null_On_FirstOrDefault_When_Empty()
|
|
{
|
|
var orders = new SimpleOrder();
|
|
var item = orders.FirstOrDefault();
|
|
|
|
Assert.That(item == null, Is.True);
|
|
}
|
|
|
|
[Test]
|
|
public void PropertyCollection_Returns_Null_On_FirstOrDefault_When_Empty()
|
|
{
|
|
var list = new List<Property>();
|
|
var collection = new PropertyCollection(list);
|
|
|
|
var first = collection.FirstOrDefault();
|
|
var second = collection.FirstOrDefault(x => x.Alias.InvariantEquals("Test"));
|
|
|
|
Assert.That(first, Is.Null);
|
|
Assert.That(first == null, Is.True);
|
|
Assert.That(second == null, Is.True);
|
|
}
|
|
|
|
[Test]
|
|
public void PropertyTypeCollection_Returns_Null_On_FirstOrDefault_When_Empty()
|
|
{
|
|
var list = new List<PropertyType>();
|
|
var collection = new PropertyTypeCollection(list);
|
|
|
|
Assert.That(collection.FirstOrDefault(), Is.Null);
|
|
Assert.That(collection.FirstOrDefault(x => x.Alias.InvariantEquals("Test")) == null, Is.True);
|
|
}
|
|
|
|
[Test]
|
|
public void PropertyGroupCollection_Returns_Null_On_FirstOrDefault_When_Empty()
|
|
{
|
|
var list = new List<PropertyGroup>();
|
|
var collection = new PropertyGroupCollection(list);
|
|
|
|
Assert.That(collection.FirstOrDefault(), Is.Null);
|
|
Assert.That(collection.FirstOrDefault(x => x.Name.InvariantEquals("Test")) == null, Is.True);
|
|
}
|
|
|
|
[Test]
|
|
public void PropertyGroups_Collection_FirstOrDefault_Returns_Null()
|
|
{
|
|
var contentType = MockedContentTypes.CreateTextpageContentType();
|
|
|
|
Assert.That(contentType.PropertyGroups, Is.Not.Null);
|
|
Assert.That(contentType.PropertyGroups.FirstOrDefault(x => x.Name.InvariantEquals("Content")) == null, Is.False);
|
|
Assert.That(contentType.PropertyGroups.FirstOrDefault(x => x.Name.InvariantEquals("Test")) == null, Is.True);
|
|
Assert.That(contentType.PropertyGroups.Any(x => x.Name.InvariantEquals("Test")), Is.False);
|
|
}
|
|
}
|
|
|
|
public class SimpleOrder : KeyedCollection<int, OrderItem>, INotifyCollectionChanged
|
|
{
|
|
// The parameterless constructor of the base class creates a
|
|
// KeyedCollection with an internal dictionary. For this code
|
|
// example, no other constructors are exposed.
|
|
//
|
|
public SimpleOrder() : base() { }
|
|
|
|
public SimpleOrder(IEnumerable<OrderItem> properties)
|
|
{
|
|
Reset(properties);
|
|
}
|
|
|
|
// This is the only method that absolutely must be overridden,
|
|
// because without it the KeyedCollection cannot extract the
|
|
// keys from the items. The input parameter type is the
|
|
// second generic type argument, in this case OrderItem, and
|
|
// the return value type is the first generic type argument,
|
|
// in this case int.
|
|
//
|
|
protected override int GetKeyForItem(OrderItem item)
|
|
{
|
|
// In this example, the key is the part number.
|
|
return item.PartNumber;
|
|
}
|
|
|
|
internal void Reset(IEnumerable<OrderItem> properties)
|
|
{
|
|
Clear();
|
|
properties.ForEach(Add);
|
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
|
}
|
|
|
|
protected override void SetItem(int index, OrderItem item)
|
|
{
|
|
base.SetItem(index, item);
|
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));
|
|
}
|
|
|
|
protected override void RemoveItem(int index)
|
|
{
|
|
var removed = this[index];
|
|
base.RemoveItem(index);
|
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removed));
|
|
}
|
|
|
|
protected override void InsertItem(int index, OrderItem item)
|
|
{
|
|
base.InsertItem(index, item);
|
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item));
|
|
}
|
|
|
|
protected override void ClearItems()
|
|
{
|
|
base.ClearItems();
|
|
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
|
|
}
|
|
|
|
public new bool Contains(int partNumber)
|
|
{
|
|
return this.Any(x => x.PartNumber == partNumber);
|
|
}
|
|
|
|
public event NotifyCollectionChangedEventHandler CollectionChanged;
|
|
|
|
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
|
|
{
|
|
if (CollectionChanged != null)
|
|
{
|
|
CollectionChanged(this, args);
|
|
}
|
|
}
|
|
}
|
|
|
|
public class OrderItem : Item
|
|
{
|
|
public readonly int PartNumber;
|
|
public readonly string Description;
|
|
public readonly double UnitPrice;
|
|
|
|
private int _quantity = 0;
|
|
|
|
public OrderItem(int partNumber, string description,
|
|
int quantity, double unitPrice)
|
|
{
|
|
this.PartNumber = partNumber;
|
|
this.Description = description;
|
|
this.Quantity = quantity;
|
|
this.UnitPrice = unitPrice;
|
|
}
|
|
|
|
public int Quantity
|
|
{
|
|
get { return _quantity; }
|
|
set
|
|
{
|
|
if (value < 0)
|
|
throw new ArgumentException("Quantity cannot be negative.");
|
|
|
|
_quantity = value;
|
|
}
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return String.Format(
|
|
"{0,9} {1,6} {2,-12} at {3,8:#,###.00} = {4,10:###,###.00}",
|
|
PartNumber, _quantity, Description, UnitPrice,
|
|
UnitPrice * _quantity);
|
|
}
|
|
}
|
|
|
|
public abstract class Item : IEntity, ICanBeDirty
|
|
{
|
|
private bool _hasIdentity;
|
|
private int? _hash;
|
|
private int _id;
|
|
private Guid _key;
|
|
|
|
protected Item()
|
|
{
|
|
_propertyChangedInfo = new Dictionary<string, bool>();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Integer Id
|
|
/// </summary>
|
|
[DataMember]
|
|
public int Id
|
|
{
|
|
get
|
|
{
|
|
return _id;
|
|
}
|
|
set
|
|
{
|
|
_id = value;
|
|
HasIdentity = true;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Guid based Id
|
|
/// </summary>
|
|
/// <remarks>The key is currectly used to store the Unique Id from the
|
|
/// umbracoNode table, which many of the entities are based on.</remarks>
|
|
[DataMember]
|
|
public Guid Key
|
|
{
|
|
get
|
|
{
|
|
if (_key == Guid.Empty)
|
|
return _id.ToGuid();
|
|
|
|
return _key;
|
|
}
|
|
set { _key = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the Created Date
|
|
/// </summary>
|
|
[DataMember]
|
|
public DateTime CreateDate { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the Modified Date
|
|
/// </summary>
|
|
[DataMember]
|
|
public DateTime UpdateDate { get; set; }
|
|
|
|
/// <summary>
|
|
/// Gets or sets the WasCancelled flag, which is used to track
|
|
/// whether some action against an entity was cancelled through some event.
|
|
/// This only exists so we have a way to check if an event was cancelled through
|
|
/// the new api, which also needs to take effect in the legacy api.
|
|
/// </summary>
|
|
[IgnoreDataMember]
|
|
internal bool WasCancelled { get; set; }
|
|
|
|
/// <summary>
|
|
/// Property changed event
|
|
/// </summary>
|
|
public event PropertyChangedEventHandler PropertyChanged;
|
|
|
|
/// <summary>
|
|
/// Method to call on a property setter.
|
|
/// </summary>
|
|
/// <param name="propertyInfo">The property info.</param>
|
|
protected virtual void OnPropertyChanged(PropertyInfo propertyInfo)
|
|
{
|
|
_propertyChangedInfo[propertyInfo.Name] = true;
|
|
|
|
if (PropertyChanged != null)
|
|
{
|
|
PropertyChanged(this, new PropertyChangedEventArgs(propertyInfo.Name));
|
|
}
|
|
}
|
|
|
|
internal virtual void ResetIdentity()
|
|
{
|
|
_hasIdentity = false;
|
|
_id = default(int);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Method to call on entity saved when first added
|
|
/// </summary>
|
|
internal virtual void AddingEntity()
|
|
{
|
|
CreateDate = DateTime.Now;
|
|
UpdateDate = DateTime.Now;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Method to call on entity saved/updated
|
|
/// </summary>
|
|
internal virtual void UpdatingEntity()
|
|
{
|
|
UpdateDate = DateTime.Now;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tracks the properties that have changed
|
|
/// </summary>
|
|
//private readonly IDictionary<string, bool> _propertyChangedInfo = new Dictionary<string, bool>();
|
|
private IDictionary<string, bool> _propertyChangedInfo;
|
|
|
|
/// <summary>
|
|
/// Indicates whether a specific property on the current entity is dirty.
|
|
/// </summary>
|
|
/// <param name="propertyName">Name of the property to check</param>
|
|
/// <returns>True if Property is dirty, otherwise False</returns>
|
|
public virtual bool IsPropertyDirty(string propertyName)
|
|
{
|
|
return _propertyChangedInfo.Any(x => x.Key == propertyName);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Indicates whether the current entity is dirty.
|
|
/// </summary>
|
|
/// <returns>True if entity is dirty, otherwise False</returns>
|
|
public virtual bool IsDirty()
|
|
{
|
|
return _propertyChangedInfo.Any();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resets dirty properties by clearing the dictionary used to track changes.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Please note that resetting the dirty properties could potentially
|
|
/// obstruct the saving of a new or updated entity.
|
|
/// </remarks>
|
|
public virtual void ResetDirtyProperties()
|
|
{
|
|
_propertyChangedInfo.Clear();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Indicates whether the current entity has an identity, eg. Id.
|
|
/// </summary>
|
|
public virtual bool HasIdentity
|
|
{
|
|
get
|
|
{
|
|
return _hasIdentity;
|
|
}
|
|
protected set
|
|
{
|
|
_hasIdentity = value;
|
|
}
|
|
}
|
|
|
|
public static bool operator ==(Item left, Item right)
|
|
{
|
|
/*if (ReferenceEquals(null, left))
|
|
return false;
|
|
|
|
if(ReferenceEquals(null, right))
|
|
return false;*/
|
|
|
|
return ReferenceEquals(left, right);
|
|
|
|
return left.Equals(right);
|
|
}
|
|
|
|
public static bool operator !=(Item left, Item right)
|
|
{
|
|
return !(left == right);
|
|
}
|
|
|
|
/*public virtual bool SameIdentityAs(IEntity other)
|
|
{
|
|
if (ReferenceEquals(null, other))
|
|
return false;
|
|
if (ReferenceEquals(this, other))
|
|
return true;
|
|
|
|
return SameIdentityAs(other as Entity);
|
|
}
|
|
|
|
public virtual bool Equals(Entity other)
|
|
{
|
|
if (ReferenceEquals(null, other))
|
|
return false;
|
|
if (ReferenceEquals(this, other))
|
|
return true;
|
|
|
|
return SameIdentityAs(other);
|
|
}
|
|
|
|
public virtual Type GetRealType()
|
|
{
|
|
return GetType();
|
|
}
|
|
|
|
public virtual bool SameIdentityAs(Entity other)
|
|
{
|
|
if (ReferenceEquals(null, other))
|
|
return false;
|
|
|
|
if (ReferenceEquals(this, other))
|
|
return true;
|
|
|
|
if (GetType() == other.GetRealType() && HasIdentity && other.HasIdentity)
|
|
return other.Id.Equals(Id);
|
|
|
|
return false;
|
|
}
|
|
|
|
public override bool Equals(object obj)
|
|
{
|
|
if (ReferenceEquals(null, obj))
|
|
return false;
|
|
if (ReferenceEquals(this, obj))
|
|
return true;
|
|
|
|
return SameIdentityAs(obj as IEntity);
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
if (!_hash.HasValue)
|
|
_hash = !HasIdentity ? new int?(base.GetHashCode()) : new int?(Id.GetHashCode() * 397 ^ GetType().GetHashCode());
|
|
return _hash.Value;
|
|
}*/
|
|
|
|
public object DeepClone()
|
|
{
|
|
return this.MemberwiseClone();
|
|
}
|
|
}
|
|
} |