Work in progress for the Ordering microservice Domain Model
This commit is contained in:
parent
b6befd623a
commit
9aed8221f2
11
src/Services/Ordering/Ordering.API/DTO/OrderDTO.cs
Normal file
11
src/Services/Ordering/Ordering.API/DTO/OrderDTO.cs
Normal file
@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.API.DTO
|
||||
{
|
||||
public class OrderDTO
|
||||
{
|
||||
}
|
||||
}
|
26
src/Services/Ordering/Ordering.API/DTO/OrderItemDTO.cs
Normal file
26
src/Services/Ordering/Ordering.API/DTO/OrderItemDTO.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel.Order
|
||||
{
|
||||
[DataContract]
|
||||
public sealed class OrderItemDTO
|
||||
{
|
||||
public OrderItemDTO() { }
|
||||
|
||||
[DataMember]
|
||||
public Guid Id { get; set; }
|
||||
|
||||
[DataMember]
|
||||
public int Quantity { get; set; }
|
||||
|
||||
[DataMember]
|
||||
public int FulfillmentRemaining { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format("ID: {0}, Quantity: {1}, Fulfillment Remaing: {2}", this.ItemId, this.Quantity, this.FulfillmentRemaining);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel
|
||||
{
|
||||
public class Buyer : AggregateRoot
|
||||
{
|
||||
public Buyer(Guid buyerId, string name, string lastName, string email, Address address, string phoneNumber)
|
||||
{
|
||||
this.BuyerId = buyerId;
|
||||
this.Name = name;
|
||||
this.LastName = lastName;
|
||||
this.Email = email;
|
||||
this.Address = address;
|
||||
this.PhoneNumber = phoneNumber;
|
||||
}
|
||||
|
||||
protected Buyer() { } // infrastructure
|
||||
|
||||
public virtual Guid BuyerId
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public virtual string Name
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public virtual string LastName
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public virtual string Email
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public virtual Address Address
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public virtual string PhoneNumber
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel
|
||||
{
|
||||
public class Address : ValueObject<Address> //A VO doesn't have IDENTITY, like in this case, the Address
|
||||
{
|
||||
public virtual String Street
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public virtual String City
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public virtual String State
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public virtual String StateCode
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public virtual String Country
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public virtual String CountryCode
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public virtual String ZipCode
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public virtual double Latitude
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public virtual double Longitude
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel
|
||||
{
|
||||
public class Order : AggregateRoot
|
||||
{
|
||||
public Order(Guid buyerId, Address shippingAddress, Address billingAddress)
|
||||
: this(buyerId, shippingAddress, billingAddress, DateTime.UtcNow)
|
||||
{
|
||||
}
|
||||
|
||||
public Order(Guid buyerId, Address shippingAddress, Address billingAddress, DateTime orderDate)
|
||||
{
|
||||
this.BuyerId = buyerId;
|
||||
this.ShippingAddress = shippingAddress;
|
||||
this.BillingAddress = billingAddress;
|
||||
this.OrderDate = orderDate;
|
||||
}
|
||||
|
||||
protected Order() { } // Infrastructure. EF might need a plain constructor. Do not use.
|
||||
|
||||
//Order ID comes derived from the Entity base class
|
||||
|
||||
List<OrderItem> _orderItems;
|
||||
public virtual List<OrderItem> orderItems
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_orderItems == null)
|
||||
_orderItems = new List<OrderItem>();
|
||||
|
||||
return _orderItems;
|
||||
}
|
||||
|
||||
private set
|
||||
{
|
||||
_orderItems = value;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual Guid BuyerId
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public virtual Address ShippingAddress
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public virtual Address BillingAddress
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public virtual DateTime OrderDate
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public virtual OrderStatus Status
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel
|
||||
{
|
||||
public class OrderItem : Entity
|
||||
{
|
||||
//(CDLTLL) Might remove this constructor so
|
||||
// just with a method in the Order-AggregateRoot you can add an OrderItem..
|
||||
//public OrderItem(Guid itemId, decimal itemPrice, int quantity)
|
||||
//{
|
||||
// this.Id = itemId;
|
||||
// this.ItemPrice = itemPrice;
|
||||
// this.Quantity = quantity;
|
||||
// this.FulfillmentRemaining = quantity; <---- Put this logic into the AggregateRoot method AddOrderItem()
|
||||
//}
|
||||
|
||||
protected OrderItem() { } // Infrastructure. EF might need a plain constructor. Do not use.
|
||||
|
||||
public Guid ItemId { get; set; }
|
||||
|
||||
public decimal ItemPrice { get; set; }
|
||||
|
||||
public int Quantity { get; set; }
|
||||
|
||||
public int FulfillmentRemaining { get; set; }
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format("ID: {0}, Quantity: {1}, Fulfillment Remaing: {2}", this.ItemId, this.Quantity, this.FulfillmentRemaining);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel
|
||||
{
|
||||
public enum OrderStatus
|
||||
{
|
||||
Unknown,
|
||||
New,
|
||||
Submitted,
|
||||
InProcess,
|
||||
Backordered,
|
||||
Shipped,
|
||||
Canceled,
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.RepositoryContracts
|
||||
{
|
||||
interface IBuyerRepository
|
||||
{
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.AggregatesModel;
|
||||
using Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.RepositoryContracts
|
||||
{
|
||||
public interface IOrderRepository : IRepository<Order>
|
||||
{
|
||||
Order Get(int associatedConferenceId);
|
||||
|
||||
void ModifyOrder(Order seatOrder);
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@ using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork
|
||||
{
|
||||
public class AggregateRoot
|
||||
public class AggregateRoot : Entity
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,130 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork
|
||||
{
|
||||
public class Entity
|
||||
/// <summary>
|
||||
/// Base class for entities
|
||||
/// </summary>
|
||||
public abstract class Entity
|
||||
{
|
||||
//Members
|
||||
|
||||
int? _requestedHashCode;
|
||||
Guid _Id;
|
||||
|
||||
|
||||
//Properties
|
||||
|
||||
/// <summary>
|
||||
/// Get the persisten object identifier
|
||||
/// </summary>
|
||||
public virtual Guid Id
|
||||
{
|
||||
get
|
||||
{
|
||||
return _Id;
|
||||
}
|
||||
protected set
|
||||
{
|
||||
_Id = value;
|
||||
}
|
||||
}
|
||||
|
||||
//Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Check if this entity is transient, ie, without identity at this moment
|
||||
/// </summary>
|
||||
/// <returns>True if entity is transient, else false</returns>
|
||||
public bool IsTransient()
|
||||
{
|
||||
return this.Id == Guid.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate identity for this entity
|
||||
/// </summary>
|
||||
public void GenerateNewIdentity()
|
||||
{
|
||||
if ( IsTransient())
|
||||
this.Id = IdentityGenerator.NewSequentialGuid();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Change current identity for a new non transient identity
|
||||
/// </summary>
|
||||
/// <param name="identity">the new identity</param>
|
||||
public void ChangeCurrentIdentity(Guid identity)
|
||||
{
|
||||
if ( identity != Guid.Empty)
|
||||
this.Id = identity;
|
||||
}
|
||||
|
||||
|
||||
//Overrides Methods
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="M:System.Object.Equals"/>
|
||||
/// </summary>
|
||||
/// <param name="obj"><see cref="M:System.Object.Equals"/></param>
|
||||
/// <returns><see cref="M:System.Object.Equals"/></returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj == null || !(obj is Entity))
|
||||
return false;
|
||||
|
||||
if (Object.ReferenceEquals(this, obj))
|
||||
return true;
|
||||
|
||||
Entity item = (Entity)obj;
|
||||
|
||||
if (item.IsTransient() || this.IsTransient())
|
||||
return false;
|
||||
else
|
||||
return item.Id == this.Id;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="M:System.Object.GetHashCode"/>
|
||||
/// </summary>
|
||||
/// <returns><see cref="M:System.Object.GetHashCode"/></returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
if (!IsTransient())
|
||||
{
|
||||
if (!_requestedHashCode.HasValue)
|
||||
_requestedHashCode = this.Id.GetHashCode() ^ 31; // XOR for random distribution (http://blogs.msdn.com/b/ericlippert/archive/2011/02/28/guidelines-and-rules-for-gethashcode.aspx)
|
||||
|
||||
return _requestedHashCode.Value;
|
||||
}
|
||||
else
|
||||
return base.GetHashCode();
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// equals operator
|
||||
/// </summary>
|
||||
/// <param name="left">left parameter</param>
|
||||
/// <param name="right">right parameter</param>
|
||||
/// <returns>true if equals</returns>
|
||||
public static bool operator ==(Entity left, Entity right)
|
||||
{
|
||||
if (Object.Equals(left, null))
|
||||
return (Object.Equals(right, null)) ? true : false;
|
||||
else
|
||||
return left.Equals(right);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Distinct operator
|
||||
/// </summary>
|
||||
/// <param name="left">left parameter</param>
|
||||
/// <param name="right">right parameter</param>
|
||||
/// <returns>True if different</returns>
|
||||
public static bool operator !=(Entity left, Entity right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,93 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Base interface for implement a "Repository Pattern", for
|
||||
/// more information about this pattern see http://martinfowler.com/eaaCatalog/repository.html
|
||||
/// or http://blogs.msdn.com/adonet/archive/2009/06/16/using-repository-and-unit-of-work-patterns-with-entity-framework-4-0.aspx
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Indeed, DbSet is already a generic repository and therefore you would not need a Repo.
|
||||
/// But using this Repo interface allows us to ensure PI (Persistence Ignorant) principle
|
||||
/// within our domain model
|
||||
/// </remarks>
|
||||
/// <typeparam name="TEntity">Type of entity for this repository </typeparam>
|
||||
public interface IRepository<TEntity> : IDisposable
|
||||
where TEntity : Entity
|
||||
{
|
||||
/// <summary>
|
||||
/// Get the unit of work in this repository
|
||||
/// </summary>
|
||||
IUnitOfWork UnitOfWork { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Add item into repository
|
||||
/// </summary>
|
||||
/// <param name="item">Item to add to repository</param>
|
||||
void Add(TEntity item);
|
||||
|
||||
/// <summary>
|
||||
/// Delete item
|
||||
/// </summary>
|
||||
/// <param name="item">Item to delete</param>
|
||||
void Remove(TEntity item);
|
||||
|
||||
/// <summary>
|
||||
/// Set item as modified
|
||||
/// </summary>
|
||||
/// <param name="item">Item to modify</param>
|
||||
void Modify(TEntity item);
|
||||
|
||||
/// <summary>
|
||||
///Track entity into this repository, really in UnitOfWork.
|
||||
///In EF this can be done with Attach and with Update in NH
|
||||
/// </summary>
|
||||
/// <param name="item">Item to attach</param>
|
||||
void TrackItem(TEntity item);
|
||||
|
||||
/// <summary>
|
||||
/// Sets modified entity into the repository.
|
||||
/// When calling Commit() method in UnitOfWork
|
||||
/// these changes will be saved into the storage
|
||||
/// </summary>
|
||||
/// <param name="persisted">The persisted item</param>
|
||||
/// <param name="current">The current item</param>
|
||||
void Merge(TEntity persisted, TEntity current);
|
||||
|
||||
/// <summary>
|
||||
/// Get element by entity key
|
||||
/// </summary>
|
||||
/// <param name="id">Entity key value</param>
|
||||
/// <returns></returns>
|
||||
TEntity Get(Guid id);
|
||||
|
||||
/// <summary>
|
||||
/// Get all elements of type TEntity in repository
|
||||
/// </summary>
|
||||
/// <returns>List of selected elements</returns>
|
||||
IEnumerable<TEntity> GetAll();
|
||||
|
||||
/// <summary>
|
||||
/// Get all elements of type TEntity in repository
|
||||
/// </summary>
|
||||
/// <param name="pageIndex">Page index</param>
|
||||
/// <param name="pageCount">Number of elements in each page</param>
|
||||
/// <param name="orderByExpression">Order by expression for this query</param>
|
||||
/// <param name="ascending">Specify if order is ascending</param>
|
||||
/// <returns>List of selected elements</returns>
|
||||
IEnumerable<TEntity> GetPaged<KProperty>(int pageIndex, int pageCount, Expression<Func<TEntity, KProperty>> orderByExpression, bool ascending);
|
||||
|
||||
/// <summary>
|
||||
/// Get elements of type TEntity in repository
|
||||
/// </summary>
|
||||
/// <param name="filter">Filter that each element do match</param>
|
||||
/// <returns>List of selected elements</returns>
|
||||
IEnumerable<TEntity> GetFiltered(Expression<Func<TEntity, bool>> filter);
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork
|
||||
{
|
||||
/// <summary>
|
||||
/// Contract for ‘UnitOfWork pattern’. For more
|
||||
/// related info see http://martinfowler.com/eaaCatalog/unitOfWork.html or
|
||||
/// http://msdn.microsoft.com/en-us/magazine/dd882510.aspx
|
||||
/// In this Microservice, the Unit Of Work is implemented using the out-of-box
|
||||
/// Entity Framework Context (EF Core DbContext) persistence engine. But in order to
|
||||
/// comply the PI (Persistence Ignorant) principle in our Domain, we implement this interface/contract.
|
||||
/// This interface/contract should be complied by any UoW implementation to be used with this Domain.
|
||||
/// </summary>
|
||||
public interface IUnitOfWork
|
||||
: IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Commit all changes made in a container.
|
||||
/// </summary>
|
||||
///<remarks>
|
||||
/// If the entity have fixed properties and any optimistic concurrency problem exists,
|
||||
/// then an exception is thrown
|
||||
///</remarks>
|
||||
void Commit();
|
||||
|
||||
/// <summary>
|
||||
/// Commit all changes made in a container.
|
||||
/// </summary>
|
||||
///<remarks>
|
||||
/// If the entity have fixed properties and any optimistic concurrency problem exists,
|
||||
/// then 'client changes' are refreshed - Client wins
|
||||
///</remarks>
|
||||
void CommitAndRefreshChanges();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Rollback tracked changes. See references of UnitOfWork pattern
|
||||
/// </summary>
|
||||
void RollbackChanges();
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,47 @@
|
||||
using System;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork
|
||||
{
|
||||
internal static class IdentityGenerator
|
||||
{
|
||||
/// <summary>
|
||||
/// This algorithm generates secuential GUIDs across system boundaries, ideal for databases
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static Guid NewSequentialGuid()
|
||||
{
|
||||
byte[] uid = Guid.NewGuid().ToByteArray();
|
||||
byte[] binDate = BitConverter.GetBytes(DateTime.UtcNow.Ticks);
|
||||
|
||||
byte[] secuentialGuid = new byte[uid.Length];
|
||||
|
||||
secuentialGuid[0] = uid[0];
|
||||
secuentialGuid[1] = uid[1];
|
||||
secuentialGuid[2] = uid[2];
|
||||
secuentialGuid[3] = uid[3];
|
||||
secuentialGuid[4] = uid[4];
|
||||
secuentialGuid[5] = uid[5];
|
||||
secuentialGuid[6] = uid[6];
|
||||
// set the first part of the 8th byte to '1100' so
|
||||
// later we'll be able to validate it was generated by us
|
||||
|
||||
secuentialGuid[7] = (byte)(0xc0 | (0xf & uid[7]));
|
||||
|
||||
// the last 8 bytes are sequential,
|
||||
// it minimizes index fragmentation
|
||||
// to a degree as long as there are not a large
|
||||
// number of Secuential-Guids generated per millisecond
|
||||
|
||||
secuentialGuid[9] = binDate[0];
|
||||
secuentialGuid[8] = binDate[1];
|
||||
secuentialGuid[15] = binDate[2];
|
||||
secuentialGuid[14] = binDate[3];
|
||||
secuentialGuid[13] = binDate[4];
|
||||
secuentialGuid[12] = binDate[5];
|
||||
secuentialGuid[11] = binDate[6];
|
||||
secuentialGuid[10] = binDate[7];
|
||||
|
||||
return new Guid(secuentialGuid);
|
||||
}
|
||||
}
|
||||
}
|
145
src/Services/Ordering/Ordering.Domain/SeedWork/ValueObject.cs
Normal file
145
src/Services/Ordering/Ordering.Domain/SeedWork/ValueObject.cs
Normal file
@ -0,0 +1,145 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for value objects in domain.
|
||||
/// Value
|
||||
/// </summary>
|
||||
/// <typeparam name="TValueObject">The type of this value object</typeparam>
|
||||
public class ValueObject<TValueObject> : IEquatable<TValueObject>
|
||||
where TValueObject : ValueObject<TValueObject>
|
||||
{
|
||||
|
||||
//IEquatable and Override Equals operators
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="M:System.Object.IEquatable{TValueObject}"/>
|
||||
/// </summary>
|
||||
/// <param name="other"><see cref="M:System.Object.IEquatable{TValueObject}"/></param>
|
||||
/// <returns><see cref="M:System.Object.IEquatable{TValueObject}"/></returns>
|
||||
public bool Equals(TValueObject other)
|
||||
{
|
||||
if ((object)other == null)
|
||||
return false;
|
||||
|
||||
if (Object.ReferenceEquals(this, other))
|
||||
return true;
|
||||
|
||||
//compare all public properties
|
||||
PropertyInfo[] publicProperties = this.GetType().GetProperties();
|
||||
|
||||
if ((object)publicProperties != null
|
||||
&&
|
||||
publicProperties.Any())
|
||||
{
|
||||
return publicProperties.All(p =>
|
||||
{
|
||||
var left = p.GetValue(this, null);
|
||||
var right = p.GetValue(other, null);
|
||||
|
||||
|
||||
if (typeof(TValueObject).IsAssignableFrom(left.GetType()))
|
||||
{
|
||||
//check not self-references...
|
||||
return Object.ReferenceEquals(left, right);
|
||||
}
|
||||
else
|
||||
return left.Equals(right);
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
/// <summary>
|
||||
/// <see cref="M:System.Object.Equals"/>
|
||||
/// </summary>
|
||||
/// <param name="obj"><see cref="M:System.Object.Equals"/></param>
|
||||
/// <returns><see cref="M:System.Object.Equals"/></returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if ((object)obj == null)
|
||||
return false;
|
||||
|
||||
if (Object.ReferenceEquals(this, obj))
|
||||
return true;
|
||||
|
||||
ValueObject<TValueObject> item = obj as ValueObject<TValueObject>;
|
||||
|
||||
if ((object)item != null)
|
||||
return Equals((TValueObject)item);
|
||||
else
|
||||
return false;
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// <see cref="M:System.Object.GetHashCode"/>
|
||||
/// </summary>
|
||||
/// <returns><see cref="M:System.Object.GetHashCode"/></returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
int hashCode = 31;
|
||||
bool changeMultiplier = false;
|
||||
int index = 1;
|
||||
|
||||
//compare all public properties
|
||||
PropertyInfo[] publicProperties = this.GetType().GetProperties();
|
||||
|
||||
|
||||
if ((object)publicProperties != null
|
||||
&&
|
||||
publicProperties.Any())
|
||||
{
|
||||
foreach (var item in publicProperties)
|
||||
{
|
||||
object value = item.GetValue(this, null);
|
||||
|
||||
if ((object)value != null)
|
||||
{
|
||||
|
||||
hashCode = hashCode * ((changeMultiplier) ? 59 : 114) + value.GetHashCode();
|
||||
|
||||
changeMultiplier = !changeMultiplier;
|
||||
}
|
||||
else
|
||||
hashCode = hashCode ^ (index * 13);//only for support {"a",null,null,"a"} <> {null,"a","a",null}
|
||||
}
|
||||
}
|
||||
|
||||
return hashCode;
|
||||
}
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="left"></param>
|
||||
/// <param name="right"></param>
|
||||
/// <returns></returns>
|
||||
public static bool operator ==(ValueObject<TValueObject> left, ValueObject<TValueObject> right)
|
||||
{
|
||||
if (Object.Equals(left, null))
|
||||
return (Object.Equals(right, null)) ? true : false;
|
||||
else
|
||||
return left.Equals(right);
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="left"></param>
|
||||
/// <param name="right"></param>
|
||||
/// <returns></returns>
|
||||
public static bool operator !=(ValueObject<TValueObject> left, ValueObject<TValueObject> right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
{
|
||||
{
|
||||
"version": "1.0.0-*",
|
||||
|
||||
"dependencies": {
|
||||
"NETStandard.Library": "1.6.0"
|
||||
"NETStandard.Library": "1.6.0",
|
||||
"System.Reflection.TypeExtensions": "4.1.0",
|
||||
"System.Runtime.Serialization.Primitives": "4.1.1"
|
||||
},
|
||||
|
||||
"frameworks": {
|
||||
|
Loading…
x
Reference in New Issue
Block a user