Adding SelectListItem() class without include Syst

2019-09-21 05:40发布

问题:

I am working on MVC.Net project which contains ViewModels and Controller. For why I don't use Model, because I use ADO.Net instead of Entity Framework for my DB connection.

To do so, I separate ViewModels with Controller in different project. Here is my problem, I utilize SelectListItem class in using System.Web.MVC, but only install it in my Controller project.

Here example of Controller code snippet:

    public ActionResult Index()
    {
        ViewBag.ddlistname = new List<SelectListItem> {
            new SelectListItem { Text = "Anthony", Value = "1" },
            new SelectListItem { Text = "Miranda", Value = "2" },
            new SelectListItem { Text = "Jhon", Value = "3" }
        };
        ViewBag.ddlistpos = new List<SelectListItem> {
            new SelectListItem { Text = "Store Clerk", Value = "A" },
            new SelectListItem { Text = "Waiter", Value = "B" },
            new SelectListItem { Text = "Cook", Value = "C" }
        };
        var result = new CRViewModel();
        return View(result);
    }

View:

@Html.DropDownListFor(model => model.EmpName,(IEnumerable<SelectListItem>)ViewBag.ddlistname )
@Html.DropDownListFor(model => model.EmpPost,(IEnumerable<SelectListItem>)ViewBag.ddlistpos )

I know it's a bad practice to fill List<SelectListItem>() using ViewBag which resulted in a lot of ViewBag inside Controller, not to mention casting ViewBag in View.

Which why I want my ViewModels like this

public class CRViewModels
{
    public string EmpName { get; set; }
    public string EmpPos { get; set; }
    public List<SelectListItem> EmpList = new List<SelectListItem>
        {
            new SelectListItem { Text = "Anthony", Value = "1" },
            new SelectListItem { Text = "Miranda", Value = "2" },
            new SelectListItem { Text = "Jhon", Value = "3" }
        };
    public List<SelectListItem> PosList = new List<SelectListItem>
        {
            new SelectListItem { Text = "Store Clerk", Value = "A" },
            new SelectListItem { Text = "Waiter", Value = "B" },
            new SelectListItem { Text = "Cook", Value = "C" }

        };
}

So in Controller I could write

public ActionResult Index()
    {
        var result = new CRViewModel();
        return View(result);
    }

and in View :

@Html.DropDownListFor(model => model.EmpName,Model.EmpList )
@Html.DropDownListFor(model => model.EmpPost,Model.PosList )

but I can't because in my ViewModels(VM) project, I don't have namespace System.Web.MVC. It's rather a waste if I add dll and include System.Web.MVC namespace to my VM project only for using the SelectListItem class.

If it is possible, How could I add SelectListItem class without adding whole namespace?

Edit : for your request, I add my real code example This is how my code works.

In DAL class:

public List<SelectListItem> getcustlist()
        {
            string SQL = "select cust_name, cust_id from tbl_cust";
            List <SelectListItem> result = getdropdownfromSQL(SQL, "cust_name", "cust_id ");
            return result;
        }
public List<SelectListItem> getpostype()
        {
            string SQL = "select pos_name, pos_code from tbl_pos";
            List<SelectListItem> result = getdropdownfromSQL(SQL, "pos_name", "pos_code");
            return result;
        }

public List<SelectListItem> getdropdownfromSQL(string SQL, string Text, string Value)
        {
            string Conn = GetConn();//function to get connection string
            List<SelectListItem> result = new List<SelectListItem>();
            string errmsg;
            DataTable fetch = getdt(SQL, Conn, out errmsg);
            if (fetch == null)
                return result;
            int rowaffect = fetch.Rows.Count;
            for (int i = 0; i < rowaffect; i++)
            {
                result.Add(new SelectListItem
                {
                    Text = fetch.Rows[i][Text].ToString(),
                    Value = fetch.Rows[i][Value].ToString()
                });
            }
            return result;
        }

public DataTable getdt(string SQL, string strconn, out string errormsg)
        {
            errormsg = "";
            DataTable dt = new DataTable();
            try
            {
                using (SqlConnection conn = new SqlConnection(strconn))
                {
                    using (SqlDataAdapter da = new SqlDataAdapter(SQL, conn))
                    {
                        conn.Open();
                        da.Fill(dt);
                    }
                }
            }
            catch (Exception ex)
            {
                errormsg = ex.Message;
                return null;
            }
            return dt;
        }

in controller :

private DAL func = new DAL();
public ActionResult Index()
{
    ViewBag.ddlistname = func.getcustlist();
    ViewBag.ddlistpos = func.getpostype();
    var result = new CRViewModel();
    return View(result);
}

回答1:

You can not achieve this behaviour without adding the namespace, as it stored info about this class.

However, you can create your own select item class and create an extension method for converting items of your class to SelectListItem as following:

public class SimpleItem
{
    public string Text { get; set; }
    public string Value { get; set; }
}

SimpleItem should be stored in assembly to which ViewModels and Views have access.

And in the MVC project create an extension method:

public static class HtmlExtensions 
{
    public static MvcHtmlString LocalDropDownListFor<TModel, TProperty>(
        this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expr, 
        IEnumerable<SimpleItem> items)
    {
        return helper.DropDownListFor(expr, 
            items.Select(x => new SelectListItem { Text = x.Text, Value = x.Value } ));
    }
}

You should include System.Web.Mvc.Html for enabling DropDownListFor method call from helper.

If it is your first HtmlHelper extension, would be better to include namespace of this class into page web.config. Or you will be required to include it on page manually:

@using YourProject.RequiredNamespace

After all you could simple call it on page:

@Html.LocalDropDownListFor(model => model.EmpName, Model.EmpList)