¿Esto pasará por el cable (serializado / deserializado) en algún momento ahora o en el futuro? Favorezca el tipo de ID único sobre el objeto completo who-know-how-large.
Si está buscando la seguridad de tipo de la ID para su entidad, también hay soluciones de código. Avísame si necesitas un ejemplo.
Editar: expandiendo la seguridad de tipo de ID:
Entonces, tomemos su método:
public Foo GetItem(int id) {}
Solo esperamos que el entero id
que se pasa sea para un Foo
objeto. Alguien podría usarlo mal y pasar Bar
la identificación entera de algún objeto o incluso simplemente escribir a mano 812341
. No es de tipo seguro Foo
. En segundo lugar, incluso si utilizó la Foo
versión de pasar un objeto , estoy seguro de que Foo
tiene un campo de identificación int
que alguien puede modificar. Y, por último, no puede usar la sobrecarga de métodos si estos existen en una clase juntos, ya que solo varía el tipo de retorno. Reescribamos este método un poco para que tenga un tipo seguro en C #:
public Foo GetItem(IntId<Foo> id) {}
Así que introduje una clase llamada IntId
que tiene una pieza genérica. En este caso particular, quiero un int
asociado Foo
solo con eso . No puedo pasar desnudo int
ni asignarle un IntId<Bar>
accidente accidentalmente. Entonces, a continuación es cómo he escrito estos identificadores de tipo seguro. Tenga en cuenta que la manipulación del subyacente real int
es solo en su capa de acceso a datos. Todo lo anterior que solo ve el tipo fuerte y no tiene acceso (directo) a su int
ID interna . No debería tener ninguna razón para hacerlo.
Interfaz IModelId.cs:
namespace GenericIdentifiers
{
using System.Runtime.Serialization;
using System.ServiceModel;
/// <summary>
/// Defines an interface for an object's unique key in order to abstract out the underlying key
/// generation/maintenance mechanism.
/// </summary>
/// <typeparam name="T">The type the key is representing.</typeparam>
[ServiceContract]
public interface IModelId<T> where T : class
{
/// <summary>
/// Gets a string representation of the domain the model originated from.
/// </summary>
/// <value>The origin.</value>
[DataMember]
string Origin
{
[OperationContract]get;
}
/// <summary>
/// The model instance identifier for the model object that this <see cref="IModelId{T}"/> refers to.
/// Typically, this is a database key, file name, or some other unique identifier.
/// <typeparam name="TKeyDataType">The expected data type of the identifier.</typeparam>
/// </summary>
/// <typeparam name="TKeyDataType">The expected data type of the identifier.</typeparam>
/// <returns>The unique key as the data type specified.</returns>
[OperationContract]
TKeyDataType GetKey<TKeyDataType>();
/// <summary>
/// Performs an equality check on the two model identifiers and returns <c>true</c> if they are equal; otherwise
/// <c>false</c> is returned. All implementations must also override the equal operator.
/// </summary>
/// <param name="obj">The identifier to compare against.</param>
/// <returns><c>true</c> if the identifiers are equal; otherwise <c>false</c> is returned.</returns>
[OperationContract]
bool Equals(IModelId<T> obj);
}
}
Clase base ModelIdBase.cs:
namespace GenericIdentifiers
{
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
/// <summary>
/// Represents an object's unique key in order to abstract out the underlying key generation/maintenance mechanism.
/// </summary>
/// <typeparam name="T">The type the key is representing.</typeparam>
[DataContract(IsReference = true)]
[KnownType("GetKnownTypes")]
public abstract class ModelIdBase<T> : IModelId<T> where T : class
{
/// <summary>
/// Gets a string representation of the domain the model originated from.
/// </summary>
[DataMember]
public string Origin
{
get;
internal set;
}
/// <summary>
/// The model instance identifier for the model object that this <see cref="ModelIdBase{T}"/> refers to.
/// Typically, this is a database key, file name, or some other unique identifier.
/// </summary>
/// <typeparam name="TKeyDataType">The expected data type of the identifier.</typeparam>
/// <returns>The unique key as the data type specified.</returns>
public abstract TKeyDataType GetKey<TKeyDataType>();
/// <summary>
/// Performs an equality check on the two model identifiers and returns <c>true</c> if they are equal;
/// otherwise <c>false</c> is returned. All implementations must also override the equal operator.
/// </summary>
/// <param name="obj">The identifier to compare against.</param>
/// <returns>
/// <c>true</c> if the identifiers are equal; otherwise <c>false</c> is returned.
/// </returns>
public abstract bool Equals(IModelId<T> obj);
protected static IEnumerable<Type> GetKnownTypes()
{
return new[] { typeof(IntId<T>), typeof(GuidId<T>) };
}
}
}
IntId.cs:
namespace GenericIdentifiers
{
// System namespaces
using System;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.Serialization;
/// <summary>
/// Represents an abstraction of the database key for a Model Identifier.
/// </summary>
/// <typeparam name="T">The expected owner data type for this identifier.</typeparam>
[DebuggerDisplay("Origin={Origin}, Integer Identifier={Id}")]
[DataContract(IsReference = true)]
public sealed class IntId<T> : ModelIdBase<T> where T : class
{
/// <summary>
/// Gets or sets the unique ID.
/// </summary>
/// <value>The unique ID.</value>
[DataMember]
internal int Id
{
get;
set;
}
/// <summary>
/// Implements the operator ==.
/// </summary>
/// <param name="intIdentifier1">The first Model Identifier to compare.</param>
/// <param name="intIdentifier2">The second Model Identifier to compare.</param>
/// <returns>
/// <c>true</c> if the instances are equal; otherwise <c>false</c> is returned.
/// </returns>
public static bool operator ==(IntId<T> intIdentifier1, IntId<T> intIdentifier2)
{
return object.Equals(intIdentifier1, intIdentifier2);
}
/// <summary>
/// Implements the operator !=.
/// </summary>
/// <param name="intIdentifier1">The first Model Identifier to compare.</param>
/// <param name="intIdentifier2">The second Model Identifier to compare.</param>
/// <returns>
/// <c>true</c> if the instances are equal; otherwise <c>false</c> is returned.
/// </returns>
public static bool operator !=(IntId<T> intIdentifier1, IntId<T> intIdentifier2)
{
return !object.Equals(intIdentifier1, intIdentifier2);
}
/// <summary>
/// Performs an implicit conversion from <see cref="IntId{T}"/> to <see cref="System.Int32"/>.
/// </summary>
/// <param name="id">The identifier.</param>
/// <returns>The result of the conversion.</returns>
public static implicit operator int(IntId<T> id)
{
return id == null ? int.MinValue : id.GetKey<int>();
}
/// <summary>
/// Performs an implicit conversion from <see cref="System.Int32"/> to <see cref="IntId{T}"/>.
/// </summary>
/// <param name="id">The identifier.</param>
/// <returns>The result of the conversion.</returns>
public static implicit operator IntId<T>(int id)
{
return new IntId<T> { Id = id };
}
/// <summary>
/// Determines whether the specified <see cref="T:System.Object"/> is equal to the current
/// <see cref="T:System.Object"/>.
/// </summary>
/// <param name="obj">The <see cref="T:System.Object"/> to compare with the current
/// <see cref="T:System.Object"/>.</param>
/// <returns>true if the specified <see cref="T:System.Object"/> is equal to the current
/// <see cref="T:System.Object"/>; otherwise, false.</returns>
/// <exception cref="T:System.NullReferenceException">The <paramref name="obj"/> parameter is null.</exception>
public override bool Equals(object obj)
{
return this.Equals(obj as IModelId<T>);
}
/// <summary>
/// Serves as a hash function for a particular type.
/// </summary>
/// <returns>
/// A hash code for the current <see cref="T:System.Object"/>.
/// </returns>
public override int GetHashCode()
{
unchecked
{
var hash = 17;
hash = (23 * hash) + (this.Origin == null ? 0 : this.Origin.GetHashCode());
return (31 * hash) + this.GetKey<int>().GetHashCode();
}
}
/// <summary>
/// Returns a <see cref="System.String"/> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="System.String"/> that represents this instance.
/// </returns>
public override string ToString()
{
return this.Origin + ":" + this.GetKey<int>().ToString(CultureInfo.InvariantCulture);
}
/// <summary>
/// Performs an equality check on the two model identifiers and returns <c>true</c> if they are equal;
/// otherwise <c>false</c> is returned. All implementations must also override the equal operator.
/// </summary>
/// <param name="obj">The identifier to compare against.</param>
/// <returns>
/// <c>true</c> if the identifiers are equal; otherwise <c>false</c> is returned.
/// </returns>
public override bool Equals(IModelId<T> obj)
{
if (obj == null)
{
return false;
}
return (obj.Origin == this.Origin) && (obj.GetKey<int>() == this.GetKey<int>());
}
/// <summary>
/// The model instance identifier for the model object that this <see cref="ModelIdBase{T}"/> refers to.
/// Typically, this is a database key, file name, or some other unique identifier.
/// </summary>
/// <typeparam name="TKeyDataType">The expected data type of the identifier.</typeparam>
/// <returns>The unique key as the data type specified.</returns>
public override TKeyDataType GetKey<TKeyDataType>()
{
return (TKeyDataType)Convert.ChangeType(this.Id, typeof(TKeyDataType), CultureInfo.InvariantCulture);
}
/// <summary>
/// Generates an object from its string representation.
/// </summary>
/// <param name="value">The value of the model's type.</param>
/// <returns>A new instance of this class as it's interface containing the value from the string.</returns>
internal static ModelIdBase<T> FromString(string value)
{
if (value == null)
{
throw new ArgumentNullException("value");
}
int id;
var originAndId = value.Split(new[] { ":" }, StringSplitOptions.None);
if (originAndId.Length != 2)
{
throw new ArgumentOutOfRangeException("value", "value must be in the format of Origin:Identifier");
}
return int.TryParse(originAndId[1], NumberStyles.None, CultureInfo.InvariantCulture, out id)
? new IntId<T> { Id = id, Origin = originAndId[0] }
: null;
}
}
}
y, para completar mi código base, también escribí uno para entidades GUID, GuidId.cs:
namespace GenericIdentifiers
{
// System namespaces
using System;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.Serialization;
/// <summary>
/// Represents an abstraction of the database key for a Model Identifier.
/// </summary>
/// <typeparam name="T">The expected owner data type for this identifier.</typeparam>
[DebuggerDisplay("Origin={Origin}, GUID={Id}")]
[DataContract(IsReference = true)]
public sealed class GuidId<T> : ModelIdBase<T> where T : class
{
/// <summary>
/// Gets or sets the unique ID.
/// </summary>
/// <value>The unique ID.</value>
[DataMember]
internal Guid Id
{
get;
set;
}
/// <summary>
/// Implements the operator ==.
/// </summary>
/// <param name="guidIdentifier1">The first Model Identifier to compare.</param>
/// <param name="guidIdentifier2">The second Model Identifier to compare.</param>
/// <returns>
/// <c>true</c> if the instances are equal; otherwise <c>false</c> is returned.
/// </returns>
public static bool operator ==(GuidId<T> guidIdentifier1, GuidId<T> guidIdentifier2)
{
return object.Equals(guidIdentifier1, guidIdentifier2);
}
/// <summary>
/// Implements the operator !=.
/// </summary>
/// <param name="guidIdentifier1">The first Model Identifier to compare.</param>
/// <param name="guidIdentifier2">The second Model Identifier to compare.</param>
/// <returns>
/// <c>true</c> if the instances are equal; otherwise <c>false</c> is returned.
/// </returns>
public static bool operator !=(GuidId<T> guidIdentifier1, GuidId<T> guidIdentifier2)
{
return !object.Equals(guidIdentifier1, guidIdentifier2);
}
/// <summary>
/// Performs an implicit conversion from <see cref="GuidId{T}"/> to <see cref="System.Guid"/>.
/// </summary>
/// <param name="id">The identifier.</param>
/// <returns>The result of the conversion.</returns>
public static implicit operator Guid(GuidId<T> id)
{
return id == null ? Guid.Empty : id.GetKey<Guid>();
}
/// <summary>
/// Performs an implicit conversion from <see cref="System.Guid"/> to <see cref="GuidId{T}"/>.
/// </summary>
/// <param name="id">The identifier.</param>
/// <returns>The result of the conversion.</returns>
public static implicit operator GuidId<T>(Guid id)
{
return new GuidId<T> { Id = id };
}
/// <summary>
/// Determines whether the specified <see cref="T:System.Object"/> is equal to the current
/// <see cref="T:System.Object"/>.
/// </summary>
/// <param name="obj">The <see cref="T:System.Object"/> to compare with the current
/// <see cref="T:System.Object"/>.</param>
/// <returns>true if the specified <see cref="T:System.Object"/> is equal to the current
/// <see cref="T:System.Object"/>; otherwise, false.</returns>
/// <exception cref="T:System.NullReferenceException">The <paramref name="obj"/> parameter is null.</exception>
public override bool Equals(object obj)
{
return this.Equals(obj as IModelId<T>);
}
/// <summary>
/// Serves as a hash function for a particular type.
/// </summary>
/// <returns>
/// A hash code for the current <see cref="T:System.Object"/>.
/// </returns>
public override int GetHashCode()
{
unchecked
{
var hash = 17;
hash = (23 * hash) + (this.Origin == null ? 0 : this.Origin.GetHashCode());
return (31 * hash) + this.GetKey<Guid>().GetHashCode();
}
}
/// <summary>
/// Returns a <see cref="System.String"/> that represents this instance.
/// </summary>
/// <returns>
/// A <see cref="System.String"/> that represents this instance.
/// </returns>
public override string ToString()
{
return this.Origin + ":" + this.GetKey<Guid>();
}
/// <summary>
/// Performs an equality check on the two model identifiers and returns <c>true</c> if they are equal;
/// otherwise <c>false</c> is returned. All implementations must also override the equal operator.
/// </summary>
/// <param name="obj">The identifier to compare against.</param>
/// <returns>
/// <c>true</c> if the identifiers are equal; otherwise <c>false</c> is returned.
/// </returns>
public override bool Equals(IModelId<T> obj)
{
if (obj == null)
{
return false;
}
return (obj.Origin == this.Origin) && (obj.GetKey<Guid>() == this.GetKey<Guid>());
}
/// <summary>
/// The model instance identifier for the model object that this <see cref="ModelIdBase{T}"/> refers to.
/// Typically, this is a database key, file name, or some other unique identifier.
/// </summary>
/// <typeparam name="TKeyDataType">The expected data type of the identifier.</typeparam>
/// <returns>The unique key as the data type specified.</returns>
public override TKeyDataType GetKey<TKeyDataType>()
{
return (TKeyDataType)Convert.ChangeType(this.Id, typeof(TKeyDataType), CultureInfo.InvariantCulture);
}
/// <summary>
/// Generates an object from its string representation.
/// </summary>
/// <param name="value">The value of the model's type.</param>
/// <returns>A new instance of this class as it's interface containing the value from the string.</returns>
internal static ModelIdBase<T> FromString(string value)
{
if (value == null)
{
throw new ArgumentNullException("value");
}
Guid id;
var originAndId = value.Split(new[] { ":" }, StringSplitOptions.None);
if (originAndId.Length != 2)
{
throw new ArgumentOutOfRangeException("value", "value must be in the format of Origin:Identifier");
}
return Guid.TryParse(originAndId[1], out id) ? new GuidId<T> { Id = id, Origin = originAndId[0] } : null;
}
}
}