これは、現在または将来、いつでも有線(シリアル化/非シリアル化)で行われますか?who-knows-how-largeのフルオブジェクトではなく、単一のIDタイプを優先します。
エンティティに対するIDのタイプセーフを探している場合は、コードソリューションもあります。例が必要な場合はお知らせください。
編集:IDの型安全性の拡張:
だから、あなたの方法を取りましょう:
public Foo GetItem(int id) {}
私たちは願ってい整数ことid
に渡されたためであるFoo
オブジェクト。誰かがそれを誤用して、あるBar
オブジェクトの整数ID を渡すか、またはに手動で入力することさえでき812341
ます。に対してタイプセーフではありませんFoo
。第二に、オブジェクトFoo
バージョンのパスを使用した場合でも、誰かが変更できるFoo
IDフィールドint
があるはずです。最後に、戻り値の型のみが異なるため、これらがクラス内に同時に存在する場合、メソッドのオーバーロードを使用できません。C#でタイプセーフに見えるように、このメソッドを少し書き直しましょう。
public Foo GetItem(IntId<Foo> id) {}
そこでIntId
、汎用的な部分を持つ名前の付いたクラスを導入しました。この特定のケースではint
、Foo
のみに関連付けられているものが必要です。裸のまま渡すことint
もIntId<Bar>
、誤って割り当てることもできません。以下に、これらのタイプセーフな識別子の記述方法を示します。基礎となる実際の操作があることテイクノートを行いint
あるのみ、あなたのデータアクセス層で。上記のすべては、強い型のみを認識し、その内部int
ID への(直接)アクセス権はありません。理由はないはずです。
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);
}
}
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;
}
}
}
また、コードベースを完全にするために、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;
}
}
}