Есть ли способ сопоставить строковый столбец с перечислением в модели Entity?
Я сделал это в Hibernate, но не могу понять это в EMF.
Есть ли способ сопоставить строковый столбец с перечислением в модели Entity?
Я сделал это в Hibernate, но не могу понять это в EMF.
Это некрасиво, но для отображения перечислений в строки я нашел что-то вроде этого:
public virtual string StatusString
{
get { return Status.ToString(); }
set { OrderStatus newValue;
if (Enum.TryParse(value, out newValue))
{ Status = newValue; }
}
}
public virtual OrderStatus Status { get; set; }
OrderStatus - это тип перечислителя, Status - это перечисление, а StatusString - строковая версия.
Вероятно, более приятная версия.
Поле OrderStateIdentifier используется как для поля сериализации JSON, так и для базы данных, в то время как OrderState используется только в коде для удобства.
public string OrderStateIdentifier
{
get { return OrderState.ToString(); }
set { OrderState = value.ToEnum<OrderState>(); }
}
[NotMapped]
[JsonIgnore]
public OrderState OrderState { get; set; }
public static class EnumHelper
{
/// <summary>
/// Converts string to enum value (opposite to Enum.ToString()).
/// </summary>
/// <typeparam name="T">Type of the enum to convert the string into.</typeparam>
/// <param name="s">string to convert to enum value.</param>
public static T ToEnum<T>(this string s) where T: struct
{
T newValue;
return Enum.TryParse(s, out newValue) ? newValue : default(T);
}
}
Я думаю, что есть другое решение.
То, что мы недавно сделали в нашем проекте, использовало методы расширения.
Я написал два из них: один для Enum и один для Entity, но вот пример:
namespace Foo.Enums
{
[DataContract]
public enum EAccountStatus
{
[DataMember]
Online,
[DataMember]
Offline,
[DataMember]
Pending
}
... сам перечисление, и теперь методы расширения, содержащие статический класс:
public static class AccountStatusExtensionMethods
{
/// <summary>
/// Returns the Type as enumeration for the db entity
/// </summary>
/// <param name="entity">Entity for which to check the type</param>
/// <returns>enum that represents the type</returns>
public static EAccountStatus GetAccountStatus(this Account entity)
{
if (entity.AccountStatus.Equals(EAccountStatus.Offline))
{
return EAccountStatus.Offline;
}
else if (entity.AccountStatus.Equals(EAccountStatus.Online))
{
return EAccountStatus.Online;
}
else if (entity.AccountStatus.Equals(EAccountStatus.Pending))
{
return EAccountStatus.Pending;
}
throw new System.Data.Entity.Validation.DbEntityValidationException(
"Unrecognized AccountStatus was set, this is FATAL!");
}
... метод расширения для типа сущности и метод удобства для более короткого ввода:
/// <summary>
/// Gets the String representation for this enums choosen
/// </summary>
/// <param name="e">Instance of the enum chosen</param>
/// <returns>Name of the chosen enum in String representation</returns>
public static String GetName(this EAccountStatus e)
{
return Enum.GetName(typeof(EAccountStatus), e);
}
}
}
... и, наконец, использование:
// to set always the same, mappable strings:
db.AccountSet.Single(m => m.Id == 1).Status = EAccountStatus.Online.GetName();
// to get the enum from the actual Entity you see:
EAccountStatus actualStatus = db.AccountSet.Single(m => m.Id == 1).GetAccountStatus();
Теперь вам просто нужно "использовать Foo.Enums"; и вы можете вызвать методы как для объекта, так и для перечисления. И даже лучше, в каких-то обертках для ваших сущностей, вы также можете делать бесшовные сортировки между дефферентными типами, представляющими одно и то же в больших проектах.
Единственное, о чем стоит обратить внимание, это то, что вам иногда приходится выполнять метод расширения до того, как передать Linq-выражение Linq. Проблема здесь в том, что Linq не может выполнить метод расширения в своем собственном контексте...
Может быть, просто альтернатива, но мы сделали это так, потому что это дает вам большую гибкость в том, как получить вещи для сущностей. Вы можете легко написать расширение для получения фактических продуктов Аккаунтов в ShoppingCart...
Привет, Kjellski
Альтернативой является использование статического класса с строковыми константными полями вместо перечислений.
Например:
public class PocoEntity
{
public string Status { get; set; }
}
public static class PocoEntityStatus
{
public const string Ok = "ok";
public const string Failed = "failed";
}
Для дополнительной проверки на стороне базы данных вы можете добавить контрольное ограничение, чтобы убедиться, что столбец является ожидаемым значением (вы можете сделать это при сопоставлении с перечислением, но поскольку свойство является просто строкой, это помогает обеспечить потребитель вашего api правильно установил значение.)
ALTER TABLE [PocoEntity]
ADD CONSTRAINT [CHK_PocoEntity_Status]
CHECK ([Status] in ('ok', 'failed'));
Это в дорожной карте для EF7: https://github.com/aspnet/EntityFramework/issues/242
Вы можете проголосовать за него: http://data.uservoice.com/forums/72025-entity-framework-feature-suggestions/suggestions/2639292-simple-type-mapping-or-mapped-type-conversion-sup
Если вы хотите сопоставить значение enum с другой соответствующей строкой (например, аббревиатурой), вы можете использовать этот подход:
public class MinhaClasse
{
public string CodTipoCampo { get; set; }
[NotMapped]
public TipoDado TipoCampo
{
get => DictValorTipoDado.SingleOrDefault(e => e.Value == CodTipoCampo).Key;
set => CodTipoCampo = DictValorTipoDado[value];
}
private Dictionary<TipoDado, string> DictValorTipoDado = new Dictionary<TipoDado, string>()
{
{ TipoDado.Texto, "T" },
{ TipoDado.Numerico, "N" },
{ TipoDado.Data, "D" }
};
public enum TipoDado { Texto, Numero, Data }
}
Вы можете сделать либо:
Украсьте свойство Enum в вашем классе как текстовый столбец
[Column(TypeName = "nvarchar(50)")]
public FileTypes FileType { get; set; }
ИЛИ ЖЕ
в вашем классе DatabaseContext
переопределите OnModelCreating
и добавьте:
modelBuilder
.Entity<File>()
.Property(e => e.FileType)
.HasConversion(new EnumToStringConverter<FileTypes>());
У меня была та же проблема. Я придумал решение, но я не совсем этому доволен.
В моем классе Person
есть перечисление Gender
, и я использую аннотации данных для сопоставления строки с базой данных и игнорирования перечисления.
public class Person
{
public int PersonID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
[Column("Gender")]
public string GenderString
{
get { return Gender.ToString(); }
private set { Gender = EnumExtensions.ParseEnum<Gender>(value); }
}
[NotMapped]
public Gender Gender { get; set; }
}
здесь используется метод расширения, чтобы получить правильное перечисление из строки.
public class EnumExtensions
{
public static T ParseEnum<T>(string value)
{
return (T)Enum.Parse(typeof(T), value, true);
}
}
Я написал сообщение в блоге об этом - http://nodogmablog.bryanhogan.net/2014/11/saving-enums-as-strings-with-entity-framework/
может быть, это работает в этой ситуации https://docs.microsoft.com/en-us/ef/core/modeling/value-conversions