C# Razor Viewmodel with Generic <T>? - c#

I'm trying to make a streamlined viewmodel that I can dynamically generate some html based off of. However, I don't quite understand how (or if it's even possible) to create a viewmodel with a dynamic value.
My viewmodel:
public class DataGridViewModel<T>
{
public List<T> DataList { get; set; }
public List<HeaderValue> DataHeaders { get; set; }
public DataGridViewModel(List<T> dataIn)
{
DataList = dataIn;
SetHeaders();
}
public void SetHeaders()
{
//Build a list of column headers based on [Display] attribute
DataHeaders = new List<HeaderValue>();
var t = DataList.First().GetType();
foreach (var prop in t.GetProperties())
{
var gridattr = prop.GetCustomAttributes(false).FirstOrDefault(x => x is DisplayAttribute);
var head = gridattr == null ? "" : (string) gridattr.GetType().GetProperty("Name").GetValue(gridattr);
var visible = gridattr != null;
DataHeaders.Add(new HeaderValue()
{
Header = head,
Visible = visible,
Property = prop.Name
});
}
}
}
My controller:
var docdto = DocumentService.FetchDocuments(); //Returns IQueryable<DocumentDTO>
var vm = new DataGridViewModel<DocumentDto>(docdto.ToList());
return View(vm);
DocumentDTO:
public class DocumentDto
{
public Int32 DocumentId { get; set; }
[Display(Name = "Category")]
public string CategoryName { get; set; }
}
//These could be very different, based on the table they're modeled after.
View:
#model DataGridViewModel<T>
#foreach(var header in Model.DataHeaders)
{
<h1>#header.Property</h1>
}
The issue I'm having is, it seems the view cannot use a generic or dynamic value, and must be assigned to a base class. The problem then is that the DTOs are all very different.
Is this possible to do with Razor?
Thank you for your time :)

Related

New class selected by LINQ query, how to transfer to another model?

I'm really not understanding this as I've only dabbled in MVC and C#. I apologize if my terminology is wrong or confusing, I will do my best to answer questions. I have a couple models like so:
public class DataSharingModels
{
public string ReferenceID { get; set; }
public NBTC NBTCGroup { get; set; }
public Contractors ContractorsGroup { get; set; }
public Coordinators CoordinatorsGroup { get; set; }
public NGO NGOGroup { get; set; }
public Public PublicGroup { get; set; }
public SelectList FA_RA_List { get; set; }
}
public class NBTC
{
public String NBTC_FA_Centroid { get; set; }
public String NBTC_FA_Bound { get; set; }
public String NBTC_RA_Centroid { get; set; }
//more properties...
}
The DataSharingModels class contains the public NBTC NBTCGroup property. It is not public List<NBTC> NBTCGroup because there will only be one produced per instance of the controller being hit.
Now in my controller, I have a LINQ statement that selects a new NBTC class:
var nbtcVals = (from ds in db.SharingPermissions
where ds.FocalRefID.ToString() == ReferenceID
&& ds.ShareGroup == "NBTC"
select new NBTC
{
NBTC_FA_Centroid = ds.CIP_FA_Centroid,
NBTC_FA_Bound = ds.CIP_FA_Boundary,
NBTC_RA_Centroid = ds.CIP_RA_Centroid,
//more properties...
});
Where I'm going wrong is I would like to add that to my DataSharingModels model. I thought the nbtcVals type would be NBTC, but it's IQueryable<##.Models.NBTC>. I understand I could do this, but it seems redundant:
DataSharingModels dsm = new DataSharingModels();
if (nbtcVals.Any())
{
foreach (var i in nbtcVals)
{
dsm.NBTCGroup.NBTC_FA_Centroid = i.NBTC_FA_Centroid;
dsm.NBTCGroup.NBTC_FA_Boundary = i.NBTC_FA_Bound;
dsm.NBTCGroup.NBTC_RA_Centroid = i.NBTC_RA_Centroid;
//more properties...
}
}
What is a more direct way to do this? There must be one. I supposed I could also return an anonymous type in the LINQ query and then assign each property in the foreach like dsm.NBTCGroup.NBTC_RA_Centroid = i.NBTC_RA_Centroid but that seems the same as the other way.
var nbtcgroup = (from ds in db.SharingPermissions
where ds.FocalRefID.ToString() == ReferenceID
&& ds.ShareGroup == "NBTC"
select new NBTC
{
NBTC_FA_Centroid = ds.CIP_FA_Centroid,
NBTC_FA_Bound = ds.CIP_FA_Boundary,
NBTC_RA_Centroid = ds.CIP_RA_Centroid,
//more properties...
})
.OrderByDescending(n => n.Id) // or some other property that could identify sorting
.FirstOrDefault();
This one has a translation to SQL (LIMIT or TOP depending on backend).

System.Web.Mvc.WebViewPage<T>.Model.get returned null

I am trying to create my first ASP.NET MVC application but since two days I cannot solve my problem.
I am using Entity Framework Code First approach. I want to create DropDownListFor but there is always this error:
System.NullReferenceException
System.Web.Mvc.WebViewPage.Model.get returned null.
My Model:
public class Animals
{
public int AnimalsId { get; set; }
public int ClientsId { get; set; }
public string Name { get; set; }
public int TypesId { get; set; }
public float Age { get; set; }
public float Weight { get; set; }
public virtual Types Types { get; set; }
public IEnumerable<Clients> ClientsList { get; set; }
public virtual ICollection<BookVisit> AnimalsVisits { get; set; }
}
My controller:
public ActionResult Create([Bind(Include = "AnimalsId, ClientsId, Name, TypesId, Age, Weight")] Animals animals)
{
var person = new List<Clients>
{
new Clients { ClientsId = 50, Name = "Timo", Surname = "Werner", Email = "timo.werner#gmail.com", Phone = 123123123 }
};
var animalsView = new Animals
{
ClientsList = person.Select(x => new Clients
{
ClientsId = x.ClientsId
})
};
if (ModelState.IsValid)
{
db.Animals.Add(animals);
db.SaveChanges();
return RedirectToAction("List", "Animal");
}
return View(animalsView);
}
My view (only #model and dropdown):
#model xyz.Models.Animals
#Html.DropDownListFor(model => model.ClientsId, new SelectList(Model.ClientsList, "ClientsId", "Name", "Surname", "Email", "Phone"))
Could you please take a look ?
From the comments, it looks like you are not passing a valid view model object to the view. Your view code is expecting a valid model passed to it and the helper methods are using different properties of that.
public ActionResult Create()
{
var clients = new List<Clients>
{
new Clients { ClientsId = 50, Name = "Timo" },
new Clients { ClientsId = 51, Name = "Microsoft" }
};
var vm = new Animals
{
ClientsList = clients
};
return View(vm);
}
Also your current code which calls the DropDownListFor is wrong. When you create a SelectList from a collection, you have to pass the dataValue field and dataText fields.
#model Animals
#Html.DropDownListFor(model => model.ClientsId,
new SelectList(Model.ClientsList, "ClientsId", "Name"))
This error may also be caused by trying to use a null model in razor view. In such case check if the model is null or not before using it as shown below:
#if (Model != null) {
<a onclick="get('#Url.Action("GetEmployee", "DemoController")', #Model.Id)" ></a>
}

How to return the result of a stored procedure into a list in my view model

This is my first question here and I'm new to WPF/MVVM.
What I'm trying to do is display a multi-column listview that holds the results returned from a stored procedure.
I'm doing my best to implement MVVM and this is where I'm having trouble.
I have a model (SL_ID is the PK and not nullable in the database):
public class mJob_Select_OrderList
{
public int SL_ID { get; set; }
public string E32JobNumber { get; set; }
public string E32_CLIENT { get; set; }
public string SL_DESCRIPTION { get; set; }
public string E32_DESCRIPTION { get; set; }
}
and then I have a view model:
public class vmJob_Select : vmMain
{
#region Members
Models.mJob_Select _mJobSelectSettings;
public SLEVEN_CLASS.dbSLEVENDataContext _dc;
#endregion
#region Constructors
public vmJob_Select()
{
_dc = new SLEVEN_CLASS.dbSLEVENDataContext();
_mJobSelectSettings = new Models.mJob_Select { EnvironmentID = 1 };
//EnvironmentList
var dsEnvironmentList = (from el in _dc.vw_EnvironmentLists orderby el.ENV_ID select new mJob_Select_Environment { ENV_ID = el.ENV_ID, Environment = el.Environment, IMAGE_RESOURCE = el.IMAGE_RESOURCE });
EnvironmentList = new ObservableCollection<mJob_Select_Environment>(dsEnvironmentList);
//Orders List
var dsSLEVENOrders = _dc.get_SLORDER_List(SelectedEnvironmentID).ToList();
SelectOrderList = new List<mJob_Select_OrderList>(dsSLEVENOrders);
}
#endregion
#region Properties
public Models.mJob_Select mJobSelectSettings { get { return _mJobSelectSettings; } set { _mJobSelectSettings = value; } }
public int SelectedEnvironmentID { get { return mJobSelectSettings.EnvironmentID; } set { if (mJobSelectSettings.EnvironmentID != value) { mJobSelectSettings.EnvironmentID = value; RaisePropertyChanged("EnvironmentID"); } } }
public ObservableCollection<mJob_Select_Environment> EnvironmentList { get; set; }
public List<mJob_Select_OrderList> SelectOrderList { get; set; }
}
So basically EnvironmentList is pulled from a view in the database and I can populate an ObservableCollection successfully. The SelectedOrderList is the result of a stored procedure and I'm trying to load this result into a list object of some sort.
The error I'm getting is
Cannot convert from 'System.Collections.Generic.List' to 'int'
I was able to implement this successfully in the code behind on the view but I'm trying to adhere to MVVM and making my ViewModel do the work.
using (SLEVEN_CLASS.dbSLEVENDataContext dbSLEVEN = new SLEVEN_CLASS.dbSLEVENDataContext())
{
var dsSLEVENOrders = dbSLEVEN.get_SLORDER_List(intENVID);
lvSlevenJobs.ItemsSource = dsSLEVENOrders;
lvSlevenJobs.SelectedValuePath = "SL_ID";
}
I've been searching and trying to figure this out for awhile now and I've tried to implement some solutions that I've found here and there to no avail. One involved looping through the IResult and adding each row to the model another suggested building a more complex linq query instead of a stored procedure. None of these options are out of the question. Is there a generally accepted method for performing this task?
Thanks in advance to any answers/suggestions you can provide!
//EDIT//
I believe I've found my solution. I was trying to return my SPROC results to a model that I created but when using LINQ a Model is created automatically called Procedure Name + 'Result'.
I declared my list to utilize the 'Result' model like so:
public List<get_SLORDER_ListResult> SelectOrderList { get; set; }
List<get_SLORDER_ListResult> lstSLEVENOrders = _dc.get_SLORDER_List(SelectedEnvironmentID).ToList<get_SLORDER_ListResult>();
SelectOrderList = new List<get_SLORDER_ListResult>(lstSLEVENOrders);
I then bound my ListView to SelectOrderList.

Iterate through the MultiSelectList

I have a model like this:
public class ArticleWriter_ViewModel
{
public int MagId { get; set; }
public string MagNo { get; set; }
public string TitleIds { get; set; }
public MultiSelectList Articles { get; set; }
public int[] SelectedArticles { get; set; }
}
i fill the Articles like this:
ArticleWriter_ViewModel viewModel = new ArticleWriter_ViewModel();
Func<IQueryable<NumberTitle>, IOrderedQueryable<NumberTitle>> orderByFunc = null;
Expression<Func<NumberTitle, bool>> filterExpr = null;
if (id > 0)
{
filterExpr = p => p.MagazineId.Equals(id);
}
var wholeTitles = unitOfWork.NumberTitleRepository.Get(filterExpr, orderByFunc, "Magazine,Title").ToList();
then pass it to view. in a few views i show Article in DropDownListFor, but in others want to show it in DisplayFor.how can i iterate through the Articles to show in DisplayFor?
Create a display template in your project's Views\DisplayTemplates directory (create the folder if necessary) called ArticleWriter_ViewModel.cshtml like this (Razor syntax):
#model ArticleWriter_ViewModel
#foreach (NumberTitle article in Model)
{
#Html.DisplayFor(article => article.Title)
}
You can change the property referenced in the DisplayFor expression and/or add an expression to filter the list of articles as required.
If you want to give the display template a different name, use the UIHint annotation on the model name to assign the template name:
[UIHint("MyTemplate")]
public class ArticleWriter_ViewModel
{...}

Using Reflection and GetValue problems

I have an abstract class that looks like so:
public abstract class PageObjectsBase
{
public abstract string FriendlyName { get; }
public abstract string PageObjectKeyPrefix { get; }
public abstract string CollectionProperty { get; }
}
And a class that derives from PageObjectsBase:
public class PageRatingList : PageObjectsBase
{
public IList<PageRating> PageRatings { get; set; }
public PageRatingList()
{
this.PageRatings = new List<PageRating>();
}
public override string CollectionProperty
{
get
{
var collectionProperty = typeof(PageRatingList).GetProperties().FirstOrDefault(p => p.Name == "PageRatings");
return (collectionProperty != null) ? collectionProperty.Name : string.Empty;
}
}
public override string FriendlyName
{
get
{
return "Page feedback/rating";
}
}
public override string PageObjectKeyPrefix
{
get
{
return "pagerating-";
}
}
}
And a PageRating class which PageRatingList.PageRatings is holding a collection of:
public class PageRating : PageObjectBase
{
public int Score { get; set; }
public string Comment { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
The PageRatingList is being stored in a database (EPiServer's Dynamic Data Store, more specifically using the Page Object Manager). I need to create some reporting functionality and am essentially loading all reports that derive from PageObjectBase. When it comes to returning the data, the code will never know at compile time what type of data it is to load, so I am using Reflection. In my reporting class I have:
//this gives me the right type
var type = Type.GetType("MyNameSpace.PageRatingList", true);
var startPageData = this._contentRepository.Get<PageData>(startPage);
PageObjectManager pageObjectManager = new PageObjectManager(startPageData);
//this loads the instances from the DB
var props = pageObjectManager.LoadAllMetaObjects()
.FirstOrDefault(o => o.StoreName == "Sigma.CitizensAdvice.Web.Business.CustomEntity.PageRatingList");
//this gives me 4 PropertyInfo objects (IList: PageRatings, string : CollectionProperty, string :FriendlyName, string : PageObjectKeyPrefix)
var properties = props.Value.GetType().GetProperties();
I can then iterate through the PropertyInfo objects using:
foreach (var property in properties)
{
//extract property value here
}
The issue I am having is that I cannot figure out how to get the value of each of the propertyinfo objects. In addition, one of those properties is type List and again we wont know the type of T until runtime. So I also need some logic that checks if one of the PropertyInfo objects is of type List and then provides access to each of the properties in the List - the List being of type PageRating.
Can anyone help here? I've not really used reflection in the past so I am winging my way through it, rightly or wrongly!
Many thanks
Al
I may be missunderstanding the problem, but i think you may use something like this:
var props = new PageRatingList(); /*actual instanse of the object, in your case, i think "props.Value" */
var properties = typeof(PageRatingList).GetProperties();
foreach (var property in properties)
{
if (property.PropertyType == typeof(IList<PageRating>))
{
IList<PageRating> list = (IList<PageRating>)property.GetValue(props);
/* do */
}
else
{
object val = property.GetValue(props);
}
}
Hope this helps to find your solution.

Categories