天天看点

MVC视图中的多个模型介绍代码项目PWMultiModelBag组件SampleObjects程序集用法结束评论

目录

介绍

代码

项目

PWMultiModelBag组件

实例化

添加对象

移除对象

检索对象

SampleObjects程序集

用法

结束评论

  • 下载演示 - 12 KB

介绍

我的解决方案是创建一个包含object类型对象列表的“模型包” ,并将该模型包用作视图中的唯一模型,从而遵守Microsoft施加的任意限制。请记住,这并不意味着您不能在其他环境中使用此代码,它只是解释了最初开发代码的原因。

本文描述了我自己的模型包实现,代码在.NET Standard 2.0程序集中,这使得它同时兼容.NET Core(3.0+)和.NET Framework。这纯粹是为了演示两种环境中的代码。将有问题的程序集重新定位为仅针对您的首选环境将是一件简单的事情。

政治/宗教观点——就我个人而言,我认为使用.NET Core没有任何实际价值——所以在这段代码中对Core的支持只是试图阻止不可避免的触发抱怨。请不要将此声明解释为讨论该主题的邀请。我不会理解,会特别不友善。你已经被警告过了。

未来雇主须知——上面的政治/宗教声明并不意味着我不会在公司代码上使用.NET Core。这只是我公认的狭隘世界观。例如,我不是Web开发的粉丝,但我目前有一份工作要做。这就是人生。

代码

该代码使用以下.NET功能:

  • 控制台应用程序——用于演示代码
  • 库程序集
  • 集合
  • 反射
  • 泛型
  • Linq

项目

演示解决方案中包含以下项目:

  • PWMultiModelBag——面向.NET Standard 2.0的类库。包含这篇文章用来描述的代码
  • DemoNetCore——面向.NET Core 5.0的控制台应用程序,练习PWMultiModelBag程序集。
  • DemoNetFrame——面向.NET Framework 4.7.2的控制台应用程序,练习PWMultiModelBag程序集。
  • SampleObjects——面向.NET Standard 2.0的类库,包含用于练习PWMultiModelBag程序集的示例类。

PWMultiModelBag组件

这是您将在自己的代码中引用的程序集。它只包含一个类——MultiModelBag。它是一个相当简单的类,具有向其内部集合添加对象、检索这些对象以供您的代码使用以及从内部集合中删除这些对象的方法。这是该类的所有JSOP优点。

namespace PWMultiModelBag
{
    /// <summary>
    /// Provides a model container for ASP.NET views that hods 1 or more models for a given view 
    /// (this skirts the single-model restriction). 
    /// </summary>
    public class MultiModelBag
    {
        /// <summary>
        /// Get/set a flag indicating whether the programmer can add multiple objects of the 
        /// same type
        /// </summary>
        public bool AllowDuplicates { get; set; }
        /// <summary>
        /// Get/set the collection containing the model objects
        /// </summary>
        public List<object> Models { get; set; }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="allowDupes"A flag indicating whether multiple objects of the same type can be added</param>
        public MultiModelBag (bool allowDupes=false)
        {
            this.AllowDuplicates = allowDupes;
            this.Models = new List<object>();
        }

        /// <summary>
        /// Adds the specified object to the collection (restricted by AllowDuplicates property)
        /// </summary>
        /// <param name="obj"></param>
		public int Add(object obj)
		{
			int added = 0;
			if (obj != null) 
			{
				var found = this.Models.FirstOrDefault(x=>x.GetType() == obj.GetType());
				if (this.AllowDuplicates || found == null)
				{
					this.Models.Add(obj);
					added++;
				}
			}
			return added;
		}

        /// <summary>
        /// Get the first item found in the list that matches the specified type (T).
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T Get<T>()
        {
            var found = this.Models.OfType<T>().Select(x=>x);
            return (found.Count()>0) ? found.First() : default(T);
        }

        /// <summary>
        /// Get the object in the colllection where the specified propery's value matches.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="propertyName"></param>
        /// <param name="value"></param>
        /// <returns>The object if found. Otherwise, null.</returns>
        /// <remarks>This overload is only useful when dupes are allowed.</remarks>
        public T Get<T>(string propertyName, object value)
        {
            T result = default(T);
            PropertyInfo[] properties = typeof(T).GetProperties();
            var found = this.Models.OfType<T>();

            foreach(object obj in found)
            {
                PropertyInfo property = properties.FirstOrDefault(x => x.Name == propertyName);
                result = (property != null && property.GetValue(obj) == value) ? (T)obj : default(T);
                if (result != null)
                {
                    break;
                }
            }
            return result;
        }

        /// <summary>
        /// Removes the 1st occurrence of the specified type from the collection
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns>The object that was removed, or null</returns>
        public T Remove<T>()
        {
            var found = this.Models.OfType<T>().Select(x => x);
            T result =  (found.Count() > 0) ? found.First() : default(T);
            if (result != null)
            {
                this.Models.Remove(result);
            }
            return result;
        }

        /// <summary>
        /// Removes the object with the specified value from the bag.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="propertyName"></param>
        /// <param name="value"></param>
        /// <returns>Returns the removed object</returns>
        public T Remove<T>(string propertyName, object value)
        {
            // gets the properties for the object type
            PropertyInfo[] properties = typeof(T).GetProperties();

            // find all instances of the specified object type
            var found = this.Models.OfType<T>().Select(x => x);
            
            // find the one we want to remove
            T result = default(T);
            foreach(object obj in found)
            {
                PropertyInfo property = properties.FirstOrDefault(x => x.Name == propertyName);
                result = (property != null && property.GetValue(obj) == value) ? (T)obj : default(T);
                if (result != null)
                {
                    this.Models.Remove(result);
                    break;
                }
            }
            return result;
        }
    }
}
           

该对象使用泛型允许程序员添加/获取/删除任何类型的对象。它旨在用于复杂对象,而不是内在函数(毕竟,我们在这里谈论的是“模型”),但我认为内在函数也可以使用。为了充分披露,我没有用内在对象对其进行测试。

反射用于(查找使用PropertyInfo的方法)来查找具有指定名称和值的对象以进行检索或删除。

实例化

当你实例化这个对象时,你可以指定可以将多个相同类型的对象添加到包中。如果允许重复,则无法防止添加在用于标识对象的属性值方面相同的对象。allowDupes参数的默认值为false。

添加对象

调用该MultiModelBag.Add方法将执行以下功能:

if the specified object is not null
{
    find the first item of the object's type in the collection
    if duplicates are allowed, or an item was not found
    {
        add the object to the collection
    }
}
           

此方法返回1(如果添加了对象)或 0(如果未添加对象)。

移除对象

移除对象从集合中移除指定类型的对象,并将移除的对象返回给调用方法。这很方便,因为您可以a)验证是否删除了正确的对象,并且b)在放开该对象范围之前对该对象进行进一步处理。当然,您也可以完全忽略返回值。

MultiModelBag.Remove方法有两个重载。

  • public T Remove<t>()——此重载删除它找到的第一个指定类型的对象。
  • public T Remove<t>(string propertyName, object value)——此重载接受属性名称和值,并在集合中搜索具有指定属性值的指定类型的第一个对象,如果找到则将其删除。

移除对象不受该AllowDuplicates标志的影响。

检索对象

检索对象将搜索指定对象并将其返回给调用方法。有两个重载:

  • public T Get<t>()——此重载查找并检索它找到的指定类型的第一个对象。如果找不到指定类型的对象,则返回null。
  • public T Get<t>(string propertyName, object value)——此重载接受属性名称和值,并在集合中搜索具有指定属性值的指定类型的第一个对象,并返回该对象(如果未找到,则返回null)。

SampleObjects程序集

该程序集提供了两个控制台应用程序使用的示例数据,并且仅用于减少演示代码占用空间。鉴于程序集的性质,不包括任何注释。

namespace SampleObjects
{
	// sample classes used in this demo
	public class ModelBase
	{
		public string Name { get; set; }
	}
	public class Model1:ModelBase
	{
		public Model1(string name) { this.Name=name; }
	}
	public class Model2:ModelBase
	{
		public Model2(string name) { this.Name=name; }
	}
	public class Model3:ModelBase
	{
		public Model3(string name) { this.Name=name; }
	}
}
           

用法

这两个演示控制台应用程序在代码和功能方面完全相同,并且充分练习了MultiModelBag类。

using System;
using PWMultiModelBag;
using SampleObjects;

namespace DemoNetFramework
{
	class Program
	{
		static void Main(string[] args)
		{
			// the MultiModelBag accepts a bool parameter that indicates whether or 
            // not to allow multiple objects of the same type.
			MultiModelBag bag = new MultiModelBag(true);
			bag.Add(new Model1("1"));
			bag.Add(new Model2("2a"));
			bag.Add(new Model2("2b"));

			// should be first model1 obj
			Model1 bagModel1  = bag.Get<model1>();
			// should be first model2 obj
			Model2 bagModel2  = bag.Get<model2>();
			// if allowdupes, should be 2nd model2 obj, otherwise will be null
			Model2 bagModel2b = bag.Get<model2>("Name", "2b");
			// should be null
			Model3 bagModel3  = bag.Get<model3>();

			// should be null because an object with a "Name" property of "2z" does 
            // not exist jin the model bag
			Model2 bagModel2c = bag.Remove<model2>("Name", "2z");
			// should be the same as bagModel2b
			Model2 bagModel2d = bag.Remove<model2>("Name", "2a");

			// set a breakpoint here to inspect the variables created above
			Console.ReadKey();
		}
	}
}
           

如您所见,用法非常简单。我们创建了一些对象并将它们添加到包中,接下来我们测试检索,最后我们测试移除。这段代码没有什么特别或花哨的。

结束评论

这段代码说明了绕过一些框架扩展很容易(在这个例子中,那些强加在MVC生态系统中的)。可能还有其他(更好的)方法可以达到相同的结果,因此请随意搜索并探索它们。

https://www.codeproject.com/Articles/5316927/Multiple-Models-In-a-MVC-View

继续阅读