在Aspnet Core中加载元数据 [英] Loading MetaData in Aspnet Core

查看:242
本文介绍了在Aspnet Core中加载元数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用AspNet Core在MVC模板中构建项目.

我使用了Entity Framework,并搭建了一个现有的数据库.现在,我想向某些类添加一些数据注释,但是我不想编辑由脚手架自动生成的类,因此我尝试使用元数据和覆盖现有方法saveChanges.

由脚手架自动生成的Users.cs

public partial class Users
{
    public int UserId { get; set; }
    ...
    // If I have [MaxLength(5, ErrorMessage = "Too short")] here, it works
    public string Email { get; set; }
}

UsersMetadata.cs (也尝试过Users.Metadata.cs或其他,什么都没有更改)

[ModelMetadataType(typeof(UsersModelMetaData))]
public partial class Users { }

public class UsersModelMetaData
{
    [MaxLength(5, ErrorMessage = "Too short")]
    public string Email { get; set; }
}

MyContext:DbContext类

public override int SaveChanges()
{
    var entities = from e in ChangeTracker.Entries()
                   where e.State == EntityState.Added
                       || e.State == EntityState.Modified
                   select e.Entity;
    foreach (var entity in entities)
    {               
    var validationContext = new ValidationContext(entity);
        Validator.ValidateObject(entity, validationContext, validateAllProperties: true);
    }

    return base.SaveChanges();
}

因此,即使这似乎是正确的解决方案(我整个上午都在搜索),也无法正常工作:问题似乎是未读取UsersModelMetaData中的数据注释,因为如果我将数据注释直接放在Users.cs文件中,方法saveChanges()将引发异常.

我确实找到了此解决方案-> https://stackoverflow.com/a/30127682/6070423 <-但是它基于AspNet,并且使用AspNet Core我不能使用AssociatedMetadataTypeTypeDescriptionProvider.

有什么办法可以解决这个问题吗?

解决方案

因此,在我问过

在那之后,我创建了一个映射器,该映射器将通过脚手架自动生成的类的实例映射到我所创建的相对类.像这样:

public static object MapEntity(object entityInstance)
   {
        var typeEntity = entityInstance.GetType();
        var typeMetaDataEntity = Type.GetType(typeEntity.FullName + "MetaData");

        if (typeMetaDataEntity == null)
            throw new Exception();

        var metaDataEntityInstance = Activator.CreateInstance(typeMetaDataEntity);

        foreach (var property in typeMetaDataEntity.GetProperties())
        {
            if (typeEntity.GetProperty(property.Name) == null)
                throw new Exception();

            property.SetValue(
                metaDataEntityInstance,
                typeEntity.GetProperty(property.Name).GetValue(entityInstance));
        }

        return metaDataEntityInstance;
    }

请注意,在第一行中,从实例的类型(即Users)开始,我通过将名称添加字符串"Metadata"来检索我创建的相应类.然后,我要做的只是创建一个UserMetaData类的实例,为该实例分配与Users实例相同的值.

最后,什么时候调用此映射器?在SaveChanges()中,当我进行验证时.那是因为验证是在UserMetaData类上进行的.

public override int SaveChanges()
        {
            var entities = from e in ChangeTracker.Entries()
                           where e.State == EntityState.Added
                               || e.State == EntityState.Modified
                           select e.Entity;
            foreach (var entity in entities)
            {
                var metaDataEntityInstance = EntityMapper.MapEntity(entity);
                var validationContext = new ValidationContext(metaDataEntityInstance);
                Validator.ValidateObject(
                    metaDataEntityInstance,
                    validationContext,
                    validateAllProperties: true);
            }

            return base.SaveChanges();
        }

这有点棘手,但都可以!

I'm building a project in MVC template using AspNet Core.

I've used Entity Framework and I've scaffolded an existing DB. Now, I want to add some Data Annotations to some class, but I don't wanna edit the class autogenerated by the scaffolding, so I've tried with the Metadata and the overriding of an existing method, the saveChanges.

Users.cs autogenerated by Scaffolding

public partial class Users
{
    public int UserId { get; set; }
    ...
    // If I have [MaxLength(5, ErrorMessage = "Too short")] here, it works
    public string Email { get; set; }
}

UsersMetadata.cs (also tried Users.Metadata.cs or else, nothing changed)

[ModelMetadataType(typeof(UsersModelMetaData))]
public partial class Users { }

public class UsersModelMetaData
{
    [MaxLength(5, ErrorMessage = "Too short")]
    public string Email { get; set; }
}

MyContext : DbContext Class

public override int SaveChanges()
{
    var entities = from e in ChangeTracker.Entries()
                   where e.State == EntityState.Added
                       || e.State == EntityState.Modified
                   select e.Entity;
    foreach (var entity in entities)
    {               
    var validationContext = new ValidationContext(entity);
        Validator.ValidateObject(entity, validationContext, validateAllProperties: true);
    }

    return base.SaveChanges();
}

So, even if this seems to be the correct solution (i've search all morning), it doesn't work: the problem seems to be that the Data Annotation inside UsersModelMetaData aren't read, because if I put the Data Annotation directly in Users.cs file, method saveChanges() will throw an exception.

I did found this solution -> https://stackoverflow.com/a/30127682/6070423 <- but it's based on AspNet, and using AspNet Core I cannot use AssociatedMetadataTypeTypeDescriptionProvider.

Any idea how I could resolve this?

解决方案

So, after I've asked on the ASP.Net Core forum, thanks to Edward Z answer, I did found a way to make it work.

Basically, instead of creating a partial class with ModelMetadataType, I've created a Class containing all the property that need to be validated (so, I will have a class of this kind for each class created by scaffolding). Something like this.

// Class generated by scaffolding
public partial class Users
    {
        public int UserId { get; set; }
        public string UserName { get; set; }
        public string Password { get; set; }
        public string Email { get; set; }
    }

// Class I did create. Notice that the name could be anything
//   but should contain at least "Users", which is the name of
//   the table generated by scaffolding.
// Also notice that isUnique is a Custom Attribute I've wrote.
public class UsersMetaData
    {
        [MinLength(2, ErrorMessage = " too short")]   
        public string UserName { get; set; }
        [MinLength(2, ErrorMessage = " too short psw")]
        public string Password { get; set; }
        [IsUnique(ErrorMessage = "Email already exists")]
        public string Email { get; set; }            
    }

After that, I created a mapper which map an instance of the class auto-generated by scaffolding, to the relative class I've craeted. Something like this:

public static object MapEntity(object entityInstance)
   {
        var typeEntity = entityInstance.GetType();
        var typeMetaDataEntity = Type.GetType(typeEntity.FullName + "MetaData");

        if (typeMetaDataEntity == null)
            throw new Exception();

        var metaDataEntityInstance = Activator.CreateInstance(typeMetaDataEntity);

        foreach (var property in typeMetaDataEntity.GetProperties())
        {
            if (typeEntity.GetProperty(property.Name) == null)
                throw new Exception();

            property.SetValue(
                metaDataEntityInstance,
                typeEntity.GetProperty(property.Name).GetValue(entityInstance));
        }

        return metaDataEntityInstance;
    }

Notice that, in the first lines, starting from the type of the instance (which is Users), I retrieve the corresponding class I've created, by adding to that name the string "Metadata". Then, what I do is just to create an instance of the UserMetaData class assigning the same value of the Users instance.

Finally, when this mapper is called? In SaveChanges(), when I do the validation. That's because the validation is made on the UserMetaData class.

public override int SaveChanges()
        {
            var entities = from e in ChangeTracker.Entries()
                           where e.State == EntityState.Added
                               || e.State == EntityState.Modified
                           select e.Entity;
            foreach (var entity in entities)
            {
                var metaDataEntityInstance = EntityMapper.MapEntity(entity);
                var validationContext = new ValidationContext(metaDataEntityInstance);
                Validator.ValidateObject(
                    metaDataEntityInstance,
                    validationContext,
                    validateAllProperties: true);
            }

            return base.SaveChanges();
        }

It's kinda tricky, but it all works!

这篇关于在Aspnet Core中加载元数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆