介绍 本文的源代码已更新到NetCore 2.0 ASP。净MVC项目。 当我们开始开发一个ASP。在Microsoft Visual Studio中,我们发现通过使用实体框架DbContext和Visual Studio工具自生成代码,可以很容易地为数据库中的每个“表”创建控制器和视图。但是,当您的数据库包含许多表时,即使逐一生成一个控制器,这个操作也会变得单调乏味。 背景 的基本规则 为了使用这篇文章的代码而不进行修改,你应该遵循以下规则: 首先使用代码,确保在数据库设计中,所有实体都派生自基类。所有实体都必须标记为DisplayTableName属性。不显示在索引视图上的属性必须标记为[notlisting],所有实体必须覆盖ToString()方法。必须将ForeignKey属性设置为ForeignKey属性,以映射导航属性 net反射 这项技术允许我们在运行时全面检查程序集和类型,获取属性、字段、方法、构造函数和类型的许多信息。 这些功能非常有助于实现我们的目标:按照对象的类型名称获取对象,并创建用于添加/修改该对象的视图,以及处理如何从数据库中列出、添加、修改和删除这些对象的控制器。 使用的代码 首先要做的是修改start .cs文件,修改处理通用请求的默认路由如下: 隐藏,复制Code

app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}/{id1?}/{id2?}/{id3?}");
});

现在,我们可以创建“genericcontroller>T,T1>”,它将处理应用程序中的所有~/<Generic>/Action/some_entity_id请求。 要开始编码,我们应该创建一个GenericController,它将包含用于CRUD和搜索的方法。 隐藏,复制Code

public partial class GenericController<T,T1> : BaseController<T1> where T : Base where T1 : DbContext, ICustomDbContext
{
}

下面的图片为我们展示了本文中使用的数据库结构示例: 基本模型和其他模型类是这样的: 隐藏,收缩,复制Code

    public class Base
    {
        [Key()]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        [NotListed]
        public int Id { get; set; }         [NotListed]
        public bool Visible { get; set; } = true;
    }     [DisplayTableName(Name = "Author")]
    [DeleteBehavior(DeleteBehaviorAttr.Hide)]
    public class Author : Base
    {
        [Display(Name = "Name")]
        [MaxLength(150)]
        [Required]
        public string Name { get; set; }         [Display(Name = "Alias")]
        [MaxLength(150)]
        [Required]
        public string Alias { get; set; }         public override string ToString()
        {
            return $"{Name}";
        }         [Display(Name = "Date of birth")]
        [DataType(DataType.Date)]
        public DateTime BirthDate { get; set; }         [Display(Name = "Date of death")]
        [DataType(DataType.Date)]
        public DateTime? Death { get; set; }         [Display(Name = "Web site")]
        [DataType(DataType.Url)]
        public string WebSite { get; set; }         [Display(Name = "Books")]
        public string Books { get { return AuthorBook!=null && AuthorBook.Any()?AuthorBook.Select(i => i.Book.Title).Aggregate((item, next) => item + ", " + next):""; } }         public ICollection<AuthorBook> AuthorBook { get; set; }
    }     [DisplayTableName(Name = "Books")]
    [ChainOfCreation(typeof(BookPrice),typeof(BookInGenre),typeof(AuthorBook))]
    [DeleteBehavior(DeleteBehaviorAttr.Hide)]
    public class Book : Base
    {
        [MaxLength(150)]
        [Required]
        public string Title { get; set; }
        
        public ICollection<AuthorBook> AuthorBook { get; set; }
        public ICollection<BookInGenre> BookInGenre { get; set; }
        public ICollection<BookPrice> BookPrice { get; set; }
        
        [MaxLength(150)]
        public string Editorial { get; set; }         [Range(30,3000)]
        public int Pages { get; set; }         public override string ToString()
        {
            return Title;
        }         [Display(Name = "Current price")]
        public decimal CurrentPrice { get { return BookPrice!=null&&BookPrice.Any()?BookPrice.OrderByDescending(i => i.Date).Select(i=>i.Price).FirstOrDefault():0; } }
        [Display(Name = "Authors")]
        public string Authors { get { return AuthorBook!=null&&AuthorBook.Any()?AuthorBook.Select(i=>i.Author.Name).Aggregate((item, next) => item + "," + next):""; } }
        [Display(Name = "Genres")]
        public string Genres {  get { return BookInGenre!=null&&BookInGenre.Any()? BookInGenre.Select(i => i.Genre.Name).Aggregate((item, next) => item + "," + next):""; } }
    }     [DisplayTableName(Name = "Prices")]
    [DeleteBehavior(DeleteBehaviorAttr.Delete)]
    public class BookPrice : Base
    {
        public DateTime Date { get; set; }         [ForeignKey("Book")]
        public int BookID { get; set; }
        public Book Book { get; set; }         [DataType(DataType.Currency)]
        public decimal Price { get; set; }
    }     [DisplayTableName(Name = "Books in genre")]
    public class BookInGenre: Base
    {
        [ForeignKey("Book")]
        [Display(Name = "Book")]
        public int BookID { get; set; }
        public Book Book { get; set; }         [ForeignKey("Genre")]
        [Display(Name = "Genre")]
        public int GenreID { get; set; }
        public Genre Genre { get; set; }         public override string ToString()
        {
            return Genre.ToString();
        }
    }     [DisplayTableName(Name = "Genre")]
    public class Genre : Base
    {
        [MaxLength(150)]
        [Required]
        public string Name { get; set; }         public ICollection<BookInGenre> BookInGenre { get; set; }         [Display(Name = "Books")]
        public string Books { get { return BookInGenre.Select(i => i.Book.Title).Aggregate((item, next) => item + ", " + next); } }         public override string ToString()
        {
            return Name;
        }
    }     [DisplayTableName(Name = "Book's authors")]
    public class AuthorBook: Base
    {
        [ForeignKey("Author")]
        [Display(Name = "Author")]
        public int AuthorID { get; set; }         public Author Author { get; set; }
        
        [ForeignKey("Book")]
        [Display(Name = "Book")]
        public int BookID { get; set; }
        public Book Book { get; set; }         public override string ToString()
        {
            return $"{Author}";
        }
    }

代码上定义了几个自定义属性: DisplayTableName:用于显示通用视图以引用所使用的实体。DeleteBehavior:用于定义删除和实体时的行为,如果设置为隐藏,那么当删除一个实体时,它将被隐藏,并且所有具有foreing键指向该对象的实体将被隐藏。创建链:由创建标记为此属性的实体类型时要生成的类型列表组成。create视图将组成一个HTML5表单,其中包含主实体属性和已定义的所有类型的主属性。创建视图的一个例子如下所示: *注意不同的背景颜色显示了creationattribute链上列出的实体类型的属性。 GenericController使用的DbContext对象应该像这样实现: 隐藏,收缩,复制Code

    public class LibraryContext: DbContext,ICustomDbContext
    {
        private ModelBuilder mb;
        private Dictionary<string,object> list = new Dictionary<string, object>();         public DbSet<Author> Author { get; set; }
        public DbSet<Book> Book { get; set; }
        public DbSet<BookPrice> BookPrice { get; set; }
        public DbSet<BookInGenre> BookInGenre { get; set; }
        public DbSet<Genre> Genre { get; set; }
        public DbSet<AuthorBook> AuthorBook { get; set; }         public LibraryContext(DbContextOptions options): base(options)
        {         }         protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            base.OnConfiguring(optionsBuilder);
        }         protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            this.mb = modelBuilder;
            base.OnModelCreating(modelBuilder);
            modelBuilder.Entity<Author>().ToTable("Author");
            modelBuilder.Entity<Book>().ToTable("Book");
            modelBuilder.Entity<BookPrice>().ToTable("BookPrice");
            modelBuilder.Entity<BookInGenre>().ToTable("BookInGenre");
            modelBuilder.Entity<Genre>().ToTable("Genre");
            modelBuilder.Entity<AuthorBook>().ToTable("AuthorBook");
        }         /// <summary>
        /// Get a dictionary of a table values with the database Key property and Value as the representation string of the class
        /// </summary>
        /// <paramname="type">Type of the requested Table</param>
        /// <returns></returns>
        public List<KeyValuePair<object,string>> GetTable(Type type)
        {
            //Get the DbContext Type
            var ttype = GetType();
            //The DbContext properties
            var props = ttype.GetProperties().ToList();
            // The DbSet property with base type @type
            var prop = props.Where(i => i.PropertyType.GenericTypeArguments.Any()&&i.PropertyType.GenericTypeArguments.First() == type).FirstOrDefault();             //The DbSet instance
            var pvalue = prop?.GetValue(this);             // Dictionary to return
            var l = new Dictionary<object, string>();             var pv = (IEnumerable<object>)pvalue;             //The entity Key property
            var keyprop = type.GetProperties().First(i => i.CustomAttributes.Any(j => j.AttributeType == typeof(KeyAttribute)));
            
            //Fills the dictionary
            foreach (Base item in pv)
            {
                //with the key and the ToString() entity result
                l.Add(keyprop.GetValue(item), item.ToString());
            }
            return l.ToList();
        }         /// <summary>
        /// Get a table casted to Objects
        /// </summary>
        /// <paramname="type">Type of the requested Table</param>
        /// <paramname="cast">Only to generate a different method signature</param>
        /// <returns></returns>
        public IEnumerable<object> GetTable(Type type,bool cast = true)
        {
            //Get the DbContext Type
            var ttype = GetType();
            //The DbContext properties
            var props = ttype.GetProperties().ToList();
            // The DbSet property with base type @type
            var prop = props.Where(i => i.PropertyType.GenericTypeArguments.Any() && i.PropertyType.GenericTypeArguments.First() == type).FirstOrDefault();             //The DbSet instance
            var pvalue = prop?.GetValue(this);             // Dictionary to return
            var l = new Dictionary<object, string>();             var pv = (IEnumerable<object>)pvalue;             return pv;
        }
    }

控制器操作和视图 视图代码自己解释 索引视图 控制器代码看起来像这个非常简单的代码: 隐藏,复制Code

/// <summary>
////// </summary>
/// <paramname="id">Page index</param>
/// <paramname="id1">Items per page</param>
/// <returns></returns>
public IActionResult Index(int? id, int? id1)
{
var data = _context.Set<T>().OrderBy(i => i.Id).ToList();
var type = typeof(T);
var attr = type.CustomAttributes.Where(i => i.AttributeType ==
typeof(DisplayTableNameAttribute)).FirstOrDefault();
ViewBag.TypeName = attr.NamedArguments.First().TypedValue.Value;
var props = type.GetProperties().ToList().Where
(i => !i.PropertyType.Name.StartsWith("ICollection")).ToList();
var props1 = props.Where(i => i.CustomAttributes.Any
(k => k.AttributeType == typeof(ForeignKeyAttribute))).ToList();
props = props.Except(props1).ToList();
ViewBag.props = props;
return View(data);
}

在本技巧的源代码中可以获得视图代码。 GenericController类 这个类包含非常有用的方法和视图,除了CRUD方法: 导入CSV文件导入OpenDocument Excel文件(将列分配到属性)在表中按属性搜索 创建/编辑操作 我们必须为这两个函数生成编辑创建视图,它们接受T模型、类型和属性,并生成表单中所有必需的字段。 在控制器的代码中,我们必须通过默认构造函数生成所需的对象,并用表单中的值填充其所有属性。 填充代码是这样的: 隐藏,收缩,复制Code

        public IActionResult Create()
        {
            var props = PrepareEditorView<T>(null);
            props = AddCreationChainProperties(props);
            ViewBag.props = props;
            return View();
        }         /// <summary>
        /// Adds the types included on the ChainOfCreation custom attribute to a list of HtmlPropertyControl
        /// </summary>
        /// <paramname="props">Current properties</param>
        /// <paramname="model">Model</param>
        /// <returns></returns>
        private List<HtmlPropertyControl> AddCreationChainProperties(List<HtmlPropertyControl> props,Base model = null)
        {
            var mtype = typeof(T);
            var etypes = new Dictionary<Type, string>();
            //Quitar los ID y por cada propiedad agregada en lo siguiente, eliminar la propiedad asociada
            if (mtype.CustomAttributes.Any(i => i.AttributeType == typeof(ChainOfCreationAttribute)))
            {
                
                var types = (IEnumerable<CustomAttributeTypedArgument>)mtype.CustomAttributes.First(i => i.AttributeType == typeof(ChainOfCreationAttribute)).ConstructorArguments.First().Value;
                foreach (var type in types)
                {
                    var color = "#" + r.Next(128, 255).ToString("X") + r.Next(128, 255).ToString("X") + r.Next(128, 255).ToString("X");
                  props.AddRange(PrepareEditorView(null, true, (Type)type.Value, new[] { typeof(T) }, ((Type)type.Value).Name + "_",color));
                    etypes.Add((Type)type.Value, ((Type)type.Value).Name + "_");
                }
            }
            ViewBag.ETypes = etypes;
            // Removing PrimaryKey properties, and NotListed attributes
            var p1 = props.Where(i => !i.Attributes.Any(ia => ia.AttributeType == typeof(KeyAttribute) || ia.AttributeType == typeof(NotListedAttribute))).ToList();
            foreach (var item in p1)
            {
                if (item.Attributes.Any(i=>i.AttributeType==typeof(ForeignKeyAttribute)))
                {
                    if (!ViewData.ContainsKey(item.propertyInfo.Name))
                        item.SelectorName = ViewData.Keys.First(i => i.EndsWith(item.propertyInfo.Name));
                }
            }
            return p1;
        }         [HttpPost]
        public IActionResult Create(T model, IFormCollection fm)
        {
            try
            {
                _context.Add<T>(model);
                _context.SaveChanges();
                var l1 = AddCreationChainProperties(new List<HtmlPropertyControl>());
                foreach (KeyValuePair<Type,string> item in ViewBag.ETypes)
                {
                    var obj = item.Key.GetConstructors()[0].Invoke(null);
                    var props = item.Key.GetProperties().Where(i=>!i.CustomAttributes.Any(j=>j.AttributeType==typeof(KeyAttribute)||j.AttributeType==typeof(NotListedAttribute))|| !i.PropertyType.Name.StartsWith("ICollection")).ToList();
                    foreach (var prop in props)
                    {
                        var prop1 = prop;
                        if (prop1.CustomAttributes.Any(i=>i.AttributeType==typeof(ForeignKeyAttribute)))
                        {
                            var prop2 = props.First(i => i.Name == (prop1.CustomAttributes.First(j => j.AttributeType == typeof(ForeignKeyAttribute)).ConstructorArguments.First().Value.ToString()));
                            if (prop2.PropertyType==typeof(T)) // If property type is model type then 
                            {
                                prop1.SetValue(obj, model.Id);
                            }
                            else
                            {
                                var pc = l1.FirstOrDefault(i => i.SelectorName == item.Value + prop.Name);
                                if (pc!=null)
                                {
                                    var value = int.Parse(fm[pc.SelectorName]);
                                    prop1.SetValue(obj, value);
                                }
                            }
                        }
                        else
                        {
                            var pc = l1.FirstOrDefault(i => i.SelectorName == item.Value + prop.Name);
                            if (pc != null || fm.ContainsKey(prop1.Name))
                            {
                                object value = fm[pc!=null?pc.SelectorName:prop1.Name];
                                if (new[]{ typeof(DateTime), typeof(Int32) , typeof(Int64) , typeof(Int16) , typeof(Double) , typeof(Decimal) }.Contains( prop.PropertyType))
                                {
                                    var tp = prop.PropertyType.GetMethods().First(i => i.Name == "Parse");
                                    object v1 = tp.Invoke(null, new[] { value.ToString() }); //ver por que no sale del método TryParse
                                    prop1.SetValue(obj, v1);
                                }
                                else
                                {
                                    prop1.SetValue(obj, value);
                                }
                            }
                        }
                    }
                    _context.Add(obj);
                    _context.SaveChanges();
                }
                return RedirectToAction("Index");
            }
            catch (Exception ee)
            {
                ViewBag.Exception = ee;
                var props = PrepareEditorView<T>(model).Where(i => !i.Attributes.Any(j => j.AttributeType == typeof(KeyAttribute))).ToList();
                props = AddCreationChainProperties(props);
                ViewBag.props = props;
                return View();
            }
        }

在这个新版本中,它包括了HTML5控件的选择器和用于表示每个属性类型的输入类型。 删除操作 这非常简单。当从数据库中删除一个对象时,代码搜索DeleteBehaivor。如果没有定义,则假定删除。 隐藏,收缩,复制Code

        public IActionResult Delete(int id)
        {
            var element = _context.Find<T>(id);
            ViewBag.props = PrepareEditorView<T>(element);
            return View(element);
        }         [HttpPost]
        public IActionResult Delete(IFormCollection fm)
        {
            var id_element = _context.Find<T>(int.Parse(fm["id"]));
            var type = typeof(T);
            ///Get the user defined property to determinate if remove permanent the database row and it dependencies or hide it all
            var dtype = type.CustomAttributes.Any(i => i.AttributeType == typeof(DeleteBehaviorAttribute)) ? (DeleteBehaviorAttr)type.CustomAttributes.First(i => i.AttributeType == typeof(DeleteBehaviorAttribute)).ConstructorArguments.First().Value : DeleteBehaviorAttr.Delete;
            List<Base> elements = new List<Base>();
            RecursiveCascadesCollection(id_element,type, ref elements);
            switch (dtype)
            {
                case DeleteBehaviorAttr.Delete:
                    {
                         _context.RemoveRange(elements.ToArray());
                        _context.SaveChanges();
                        _context.Remove<T>(id_element);
                        _context.SaveChanges();
                        break;
                    }
                case DeleteBehaviorAttr.Hide:
                    {
                        elements.ForEach(k => k.Visible = false);
                        _context.Find<T>(int.Parse(fm["id"])).Visible = false;
                        _context.SaveChanges();
                        break;
                    }
            }
            return RedirectToAction("Index");
        }

每个模型类的控制器代码 可以根据模型生成任何数据类型的控制器。基类。在hte中,控制器类的定义用于泛型参数、实体类型和DbContext类型。这个项目中的控制器是这样的: 隐藏,复制Code

    public class AuthorController : GenericController<Author, LibraryContext>
    {
        public AuthorController(LibraryContext context, IConfiguration config) : base(context,config)
        {
        }
    }     public class BookController :  GenericController<Book,LibraryContext>
    {
        public BookController(LibraryContext context, IConfiguration config) : base(context, config)
        {
           
        }
    }     public class AuthorBooksController : GenericController<AuthorBook, LibraryContext>
    {
        public AuthorBooksController(LibraryContext context, IConfiguration config) : base(context, config)
        {
        }
    }

历史 这个新版本允许指定通用控制器中使用哪个DbContext,允许在您的项目上有两个或多个DbContext 此源项目将根据所有用户的建议进行更新。 本文转载于:http://www.diyabc.com/frontweb/news19584.html

ASP。NET MVC (NetCore 2.0)用于处理实体框架、DbContexts和对象的通用控制器和视图的更多相关文章

  1. 【无私分享:从入门到精通ASP.NET MVC】从0开始,一起搭框架、做项目 目录索引

    索引 [无私分享:从入门到精通ASP.NET MVC]从0开始,一起搭框架.做项目(1)搭建MVC环境 注册区域 [无私分享:从入门到精通ASP.NET MVC]从0开始,一起搭框架.做项目(2)创建 ...

  2. Asp.Net MVC+BootStrap+EF6.0实现简单的用户角色权限管理

    这是本人第一次写,写的不好的地方还忘包含.写这个的主要原因是想通过这个来学习下EF的CodeFirst模式,本来也想用AngularJs来玩玩的,但是自己只会普通的绑定,对指令这些不是很熟悉,所以就基 ...

  3. ASP.NET Core 1.0、ASP.NET MVC Core 1.0和Entity Framework Core 1.0

    ASP.NET 5.0 将改名为 ASP.NET Core 1.0 ASP.NET MVC 6  将改名为 ASP.NET MVC Core 1.0 Entity Framework 7.0    将 ...

  4. [转帖]2016年时的新闻:ASP.NET Core 1.0、ASP.NET MVC Core 1.0和Entity Framework Core 1.0

    ASP.NET Core 1.0.ASP.NET MVC Core 1.0和Entity Framework Core 1.0 http://www.cnblogs.com/webapi/p/5673 ...

  5. ASP.NET没有魔法——ASP.NET MVC使用Oauth2.0实现身份验证

    随着软件的不断发展,出现了更多的身份验证使用场景,除了典型的服务器与客户端之间的身份验证外还有,如服务与服务之间的(如微服务架构).服务器与多种客户端的(如PC.移动.Web等),甚至还有需要以服务的 ...

  6. ASP.NET MVC使用Oauth2.0实现身份验证

    随着软件的不断发展,出现了更多的身份验证使用场景,除了典型的服务器与客户端之间的身份验证外还有,如服务与服务之间的(如微服务架构).服务器与多种客户端的(如PC.移动.Web等),甚至还有需要以服务的 ...

  7. Asp.net MVC 传递数据 从前台到后台,包括单个对象,多个对象,集合

    今天为大家分享下 Asp.net MVC 将数据从前台传递到后台的几种方式. 环境:VS2013,MVC5.0框架 1.基本数据类型 我们常见有传递 int, string, bool, double ...

  8. Asp.Net MVC+BootStrap+EF6.0实现简单的用户角色权限管理4

    首先先加个区域,名为Admin using System.Web.Mvc; namespace AuthorDesign.Web.Areas.Admin { public class AdminAre ...

  9. Asp.Net MVC+BootStrap+EF6.0实现简单的用户角色权限管理6

    接下来先做角色这一板块的(增删改查),首先要新建一个Role控制器,在添加一个RoleList的视图.表格打算采用的是bootstrap的表格. using System; using System. ...

随机推荐

  1. docker run <image-id>和 docker start <container-id>

  2. 石子合并(区间dp典型例题)

    Description 有n堆石子排成一行,每次选择相邻的两堆石子,将其合并为一堆,记录该次合并的得分为两堆石子个数之和.已知每堆石子的石子个数,求当所有石子合并为一堆时,最小的总得分. Input ...

  3. Codeforces 1321C Remove Adjacent

    题意 给你一个字符串,字符\(s_i\)可以被伤处当且仅当\(s_{i-1}=s_i-1\)或\(s_{i+1}=s_i-1\).问最多能删几个字符. 解题思路 其实,有个很简单的做法就是从\(z\) ...

  4. 蒲公英 &#183; JELLY技术周刊 Vol.21 -- 技术周刊 &#183; React Hooks vs Vue 3 + Composition API

    蒲公英 · JELLY技术周刊 Vol.21 选 React 还是 Vue,每个人心中都会有自己的答案,有很多理由去 pick 心水的框架,但是当我们扪心自问,我们真的可以公正的来评价这两者之间的差异 ...

  5. 对Jenkinsfile语法说不,开源项目Jenkins Json Build挺你

    对Jenkinsfile语法说不,开源项目Jenkins Json Build挺你 项目背景 我所在的组织项目数量众多,使用的语言和框架也很多,比如Java.ReactNative.C# .NET.A ...

  6. [bash] 打印到屏幕相关语法

    程序: #!/bin/bash function showAlertMsg(){ echo -e "\e[1;31m"$"\e[0m" } function s ...

  7. SpringCloud-config分布式配置

    为什么要统一管理微服务配置? 随着微服务不断的增多,每个微服务都有自己对应的配置文件.在研发过程中有测试环境.UAT环境.生产环境,因此每个微服务又对应至少三个不同环境的配置文件.这么多的配置文件,如 ...

  8. appium 基础二:常用api接口

    一.获取手机分辨率 size=driver.get_window_size()#获取手机屏幕大小,分辨率 print(size)#{'width': 720, 'height': 1280} 得到的是 ...

  9. 血的教训!千万别在生产使用这些 redis 指令

    哎,最近小黑哥又双叒叕犯事了. 事情是这样的,前一段时间小黑哥公司生产交易偶发报错,一番排查下来最终原因是因为 Redis 命令执行超时. 可是令人不解的是,生产交易仅仅使用 Redis set 这个 ...

  10. Badboy脚本录制工具

    Badboy 目录 Badboy 1.Badboy安装 2.脚本的录制 1.Badboy安装 下载地址: http://www.badboy.com.au/download/index 安装其实傻瓜式 ...