天天看點

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

繼續閱讀