Isso vai passar por cima do fio (serializado / desserializado) a qualquer momento agora ou no futuro? Favorecer o tipo de ID único sobre o objeto completo de quem sabe o tamanho.
Se você está procurando a segurança de tipo do ID para sua entidade, também existem soluções de código. Deixe-me saber se você precisar de um exemplo.
Edit: expandindo a segurança de tipo de ID:
Então, vamos usar seu método:
public Foo GetItem(int id) {}
Nós só esperamos que o número inteiro id
que é passado é para um Foo
objeto. Alguém pode usá-lo mal e passar Bar
o ID inteiro de algum objeto ou até digitar manualmente 812341
. Não é do tipo seguro Foo
. Em segundo lugar, mesmo se você usou a Foo
versão pass a object , tenho certeza que Foo
possui um campo de ID que é int
alguém que pode ser modificado. E, por último, você não pode usar a sobrecarga de método se eles existirem em uma classe juntos, pois apenas o tipo de retorno varia. Vamos reescrever um pouco esse método para parecer seguro em C #:
public Foo GetItem(IntId<Foo> id) {}
Então, eu apresentei uma classe chamada IntId
que tem uma parte genérica. Nesse caso em particular, quero um int
que esteja associado Foo
apenas. Não posso simplesmente passar nu int
e não posso atribuir um IntId<Bar>
a ele acidentalmente. Então, abaixo, é como escrevi esses identificadores de tipo seguro. Observe que a manipulação do subjacente real int
é apenas na sua camada de acesso a dados. Qualquer coisa acima que veja apenas o tipo forte e não tenha acesso (direto) ao seu int
ID interno . Não deve ter razão para isso.
Interface 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);
}
}
Classe 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;
}
}
}
e, para completar a minha base de código, também escrevi um 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;
}
}
}