@ -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 | |||||
{ | |||||
} | |||||
} |
@ -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); | |||||
} | |||||
} |
@ -1,11 +1,130 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Threading.Tasks; | |||||
namespace Microsoft.eShopOnContainers.Services.Ordering.Domain.SeedWork | 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); | |||||
} | |||||
} | |||||
} |
@ -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); | |||||
} | |||||
} | |||||
} | |||||