Не обязательно. Но если вы когда-нибудь захотите изменить типы первичных ключей (или поставщики баз данных вообще требуют изменений), хорошая идея абстрагировать ваши первичные ключи от разработчиков на уровне приложений. Что вы делаете, это создаете интерфейс, который абстрагирует первичный ключ на этом уровне, но уровень данных знает об этом через его реализацию. Вот пример:
namespace Aesop.DataAccess
{
// System namespaces
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Xml.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>
[ServiceContract]
public interface IModelIdentifier<T> : ISerializable, IXmlSerializable
{
/// <summary>
/// Gets a string representation of the domain the model originated
/// from.
/// </summary>
string Origin
{
[OperationContract]
get;
}
/// <summary>
/// The model instance identifier for the model object that this
/// <see cref="IModelIdentifier{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>
[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(IModelIdentifier<T> obj);
}
}
А вот реализация "стандартного" первичного ключа int:
namespace Aesop.DataAccess
{
// System namespaces
using System;
using System.Diagnostics;
using System.Globalization;
using System.Runtime.Serialization;
using System.Security.Permissions;
using System.Xml;
using System.Xml.Schema;
using System.Xml.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("Integer Identifier={id}, Origin={Origin}")]
[Serializable]
public sealed class IntegerIdentifier<T> : IModelIdentifier<T> where T : class, ISerializable, IXmlSerializable
{
/// <summary>
/// The unique ID.
/// </summary>
private int id;
/// <summary>
/// Initializes a new instance of the <see cref="IntegerIdentifier<T>"/> class.
/// </summary>
/// <param name="id">The unique ID.</param>
public IntegerIdentifier(int id)
{
this.id = id;
}
/// <summary>
/// Initializes a new instance of the <see cref="IntegerIdentifier<T>"/> class.
/// </summary>
/// <param name="info">The
/// <see cref="T:System.Runtime.Serialization.SerializationInfo"/> from
/// which to retrieve the data.</param>
/// <param name="context">The source (see
/// <see cref="T:System.Runtime.Serialization.StreamingContext"/>) for
/// this deserialization.</param>
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
private IntegerIdentifier(
SerializationInfo info,
StreamingContext context)
{
if (info == null)
{
throw new ArgumentNullException("info");
}
this.id = info.GetInt32("id");
}
/// <summary>
/// Prevents a default instance of the <see cref="IntegerIdentifier<T>"/> class from being created.
/// </summary>
private IntegerIdentifier()
{
}
/// <summary>
/// Gets a string representation of the domain the model originated
/// from.
/// </summary>
public string Origin
{
get
{
return this.GetType().Namespace;
}
}
/// <summary>
/// Implements the operator ==.
/// </summary>
/// <param name="integerIdentifier1">The first Model Identifier to
/// compare.</param>
/// <param name="integerIdentifier2">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 ==(
IntegerIdentifier<T> integerIdentifier1,
IntegerIdentifier<T> integerIdentifier2)
{
return object.Equals(integerIdentifier1, integerIdentifier2);
}
/// <summary>
/// Implements the operator !=.
/// </summary>
/// <param name="integerIdentifier1">The first Model Identifier to
/// compare.</param>
/// <param name="integerIdentifier2">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 !=(
IntegerIdentifier<T> integerIdentifier1,
IntegerIdentifier<T> integerIdentifier2)
{
return !object.Equals(integerIdentifier1, integerIdentifier2);
}
/// <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 IModelIdentifier<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()
{
return this.id.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.id.ToString(CultureInfo.InvariantCulture);
}
/// <summary>
/// The model instance identifier for the model object that this
/// <see cref="IModelIdentifier{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 TKeyDataType GetKey<TKeyDataType>()
{
return (TKeyDataType)Convert.ChangeType(
this.id,
typeof(TKeyDataType),
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 bool Equals(IModelIdentifier<T> obj)
{
if (obj == null)
{
return false;
}
return obj.GetKey<int>() == this.GetKey<int>();
}
/// <summary>
/// Populates a
/// <see cref="T:System.Runtime.Serialization.SerializationInfo"/> with
/// the data needed to serialize the target object.
/// </summary>
/// <param name="info">The
/// <see cref="T:System.Runtime.Serialization.SerializationInfo"/> to
/// populate with data.</param>
/// <param name="context">The destination (see
/// <see cref="T:System.Runtime.Serialization.StreamingContext"/>) for
/// this serialization.</param>
/// <exception cref="T:System.Security.SecurityException">The caller
/// does not have the required permission. </exception>
[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
public void GetObjectData(
SerializationInfo info,
StreamingContext context)
{
if (info == null)
{
throw new ArgumentNullException("info");
}
info.AddValue("id", this.id);
}
/// <summary>
/// This method is reserved and should not be used. When implementing
/// the IXmlSerializable interface, you should return null (Nothing in
/// Visual Basic) from this method, and instead, if specifying a custom
/// schema is required, apply the
/// <see cref="T:System.Xml.Serialization.XmlSchemaProviderAttribute"/>
/// to the class.
/// </summary>
/// <returns>
/// An <see cref="T:System.Xml.Schema.XmlSchema"/> that describes the
/// XML representation of the object that is produced by the
/// <see cref="M:System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter)"/>
/// method and consumed by the
/// <see cref="M:System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader)"/>
/// method.
/// </returns>
public XmlSchema GetSchema()
{
return null;
}
/// <summary>
/// Generates an object from its XML representation.
/// </summary>
/// <param name="reader">The <see cref="T:System.Xml.XmlReader"/>
/// stream from which the object is deserialized.</param>
public void ReadXml(XmlReader reader)
{
if (reader != null)
{
this.id = Convert.ToInt32(
reader.GetAttribute("id"),
CultureInfo.InvariantCulture);
}
}
/// <summary>
/// Converts an object into its XML representation.
/// </summary>
/// <param name="writer">The <see cref="T:System.Xml.XmlWriter"/>
/// stream to which the object is serialized.</param>
public void WriteXml(XmlWriter writer)
{
if (writer != null)
{
writer.WriteAttributeString(
"id",
this.id.ToString(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 IModelIdentifier<T> FromString(string value)
{
int id;
if (int.TryParse(
value,
NumberStyles.None,
CultureInfo.InvariantCulture,
out id))
{
return new IntegerIdentifier<T>(id);
}
return null;
}
/// <summary>
/// Generates an object from its XML representation.
/// </summary>
/// <param name="reader">The <see cref="T:System.Xml.XmlReader"/>
/// stream from which the object is deserialized.</param>
/// <returns>A new instance of this class as it's interface containing
/// the value from the XmlReader.</returns>
internal static IModelIdentifier<T> FromXml(XmlReader reader)
{
if (reader != null)
{
return new IntegerIdentifier<T>(Convert.ToInt32(
reader.GetAttribute("id"),
CultureInfo.InvariantCulture));
}
return null;
}
}
}