3

I have defined an enum in my Entity Framework 5 model, which I'm using to define the type of a field on a table, e.g.

public enum PrivacyLevel : byte {
  Public = 1,
  FriendsOnly = 2,
  Private = 3,
}

And I have a table Publication that has a tinyint field PrivacyLevel, which I've mapped in the EF model to use the PrivacyLevel type defined above, using the method described here.

But I also want to be able to display a string description for each value of the enum. This I've done in the past for enums by decorating them with a Description attribute, e.g.

public enum PrivacyLevel : byte {
  [Description("Visible to everyone")]
  Public = 1,
  [Description("Only friends can view")]
  FriendsOnly = 2,
  [Description("Only I can view")]
  Private = 3,
}

I've got some code that converts enums to strings by checking if they have a Description attribute, and that works well. But here, because I had to define the enum in my model, the underlying code is auto-generated, and I don't have anywhere stable to decorate them.

Any ideas for a workaround?

4

3 回答 3

14

不确定这是否是您所追求的,但据我了解,我会尽可能清楚,因为您有一个具体的数据库优先方法,您可以通过 AutoMapper 使用 Dto 方法将大部分实体模型抽象为 ViewModels。

使用自动映射器配置文件,您可以为各种环境和场景快速设置配置文件,以实现灵活性和适应性

所以这是给我带来问题的“枚举”

这是我对此枚举的视图模型

首先这是我的布局

这是 Account 实体到 Account 视图模型的简单映射

public class AccountProfile : Profile
{
    protected override void Configure()
    {
        // Map from Entity object to a View Model we need or use
        // AutoMapper will automatically map any names that match it's conventions, ie properties from Entity to ViewModel have exact same name properties

        Mapper.CreateMap<Account, AccountViewModel>()
              .ForMember(model => model.CurrentPrivacy, opt => opt.MapFrom(account => (PrivacyLevelViewModel)account.PrivacyLevel));

        Mapper.CreateMap<Account, EditAccountViewModel>()
            .ForMember(model => model.SelectedPrivacyLevel, opt => opt.MapFrom(account => (PrivacyLevelViewModel) account.PrivacyLevel));

        // From our View Model Changes back to our entity

        Mapper.CreateMap<EditAccountViewModel, Account>()
              .ForMember(entity => entity.Id, opt => opt.Ignore()) // We dont change id's
              .ForMember(entity => entity.PrivacyLevel, opt => opt.MapFrom(viewModel => (PrivacyLevel)viewModel.NewSelectedPrivacyLevel));

    }


}

请注意,这不一定适用于 MVC,它可以在 WPF 或其他不绑定到 Web 的应用程序中使用,但由于它是一种很好的解释方式,这就是我在此示例中使用 MVC 的原因。

当我第一次收到我的个人资料的 Http Get 请求时,我从数据库中获取实体并将我实际需要的任何内容映射到视图

public ActionResult Index()
{
    // Retrieve account from db
    var account = new Account() { Id = 1, Name = "Patrick", AboutMe = "I'm just another dude", ProfilePictureUrl = "", PrivacyLevel = PrivacyLevel.Private, Friends = new Collection<Account>() };
    // ViewModel abstracts the Entities and ensures behavour that only matters to the UI
    var accountViewModel = Mapper.Map<AccountViewModel>(account);

    return View(accountViewModel); // strongly typed view model
}

所以我的个人资料索引视图可以使用我的枚举视图模型

这是输出

现在,当我想更改我的隐私设置时,我可以创建一个新的 EditAccountViewModel,它允许我在下拉列表中提交一个新值

public class EditAccountViewModel
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string AboutMe { get; set; }
    public int NewSelectedPrivacyLevel { get; set; }
    public PrivacyLevelViewModel SelectedPrivacyLevel { get; set; }

    public SelectList PrivacyLevels
    {
        get
        {
            var items = Enum.GetValues(typeof (PrivacyLevelViewModel))
                .Cast<PrivacyLevelViewModel>()
                .Select(viewModel => new PrivacyLevelSelectItemViewModel()
                {
                    Text = viewModel.DescriptionAttr(),
                    Value = (int)viewModel,
                });

            //SelectPrivacyLevel was mapped by AutoMapper in the profile from 
            //original entity value to this viewmodel
            return new SelectList(items, "Value", "Text", (int) SelectedPrivacyLevel);
        }
    }
}

现在,一旦我发送了我的新更改值的帖子,有趣的部分是我如何使用更新的隐私设置修改数据库中的“真实”实体

在将表单提交回我的编辑操作时,您可以获得原始的真实数据库实体,然后如果 ViewModel 状态有效则合并更改

AutoMapper 允许您配置 ViewModels 如何映射到实体,如果某些属性应该更改,从整数实体到视图模型的字符串值,也许您希望枚举真正成为“视图”中的字符串,并且只有枚举db,使用自动映射器,它允许您配置所有这些场景,并且通过约定,如果您的视图模型具有相同的属性名称/骆驼大小写到大写,则不需要配置“每个属性”。

最后,在您可以使用这些配置文件之前,您必须在应用程序入口点加载它们,例如 global.asax 或 Main。

AutoMapper 只需“配置”一次即可加载应用程序中定义的任何类型的配置文件。通过一些反射,您可以使用以下代码加载程序集中的所有配置文件:

public class AutoMapperConfig
{
    public static void RegisterConfig()
    {
        Mapper.Initialize(config => GetConfiguration(Mapper.Configuration));
    }

    private static void GetConfiguration(IConfiguration configuration)
    {
        configuration.AllowNullDestinationValues = true;
        configuration.AllowNullCollections = true;

        IEnumerable<Type> profiles = Assembly.GetExecutingAssembly().GetTypes().Where(type => typeof(Profile).IsAssignableFrom(type));

        foreach (var profile in profiles)
        {
            configuration.AddProfile(Activator.CreateInstance(profile) as Profile);
        }
    }
}

我在 global.asax 中调用配置:

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();

    WebApiConfig.Register(GlobalConfiguration.Configuration);
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
    RouteConfig.RegisterRoutes(RouteTable.Routes);
    AutoMapperConfig.RegisterConfig(); // AutoMapperConfig.cs
}

有关如何使用 AutoMapper 以及它如何使您受益的更多信息,请参见此处:

AutoMapper Github

于 2013-03-18T15:25:07.747 回答
2

最后我想出了一个更简单的解决方案:我只是使用了一个扩展方法来获取枚举的描述。这也使本地化变得更加容易,因此我可以使用资源字符串。

public static string Description(this PrivacyLevel level) {
  switch (level) {
    case PrivacyLevel.Public:
      return Resources.PrivacyPublic;
    case PrivacyLevel.FriendsOnly:
      return Resources.PrivacyFriendsOnly;
    case PrivacyLevel.Private:
      return Resources.PrivacyPrivate;
    default:
      throw new ArgumentOutOfRangeException("level");
  }
}
于 2013-10-20T14:39:26.620 回答
0

其他一些想法:byte PrivacyLevelByte在您的 EF 课程中使用。partial class为您定义属性的特定模型创建附加

PrivacyLevel PrivacyLevelEnum
{
    get { return (PrivacyLevel)PrivacyLevelByte; }
    set { PrivacyLevelByte = (byte)value;}
}

并在您的代码中定义PrivacyLevel枚举,而不是由 EF 设计器。这允许您处理任何属性,但仍为您提供 EF 模型上的枚举属性。

于 2013-12-21T16:22:02.650 回答