I have following two model classes
public partial class Items
{
public Items()
{
this.Items_RATINGS = new HashSet<Items_RATINGS>();
}
public int ITEMID { get; set; }
public string ITEMNAME { get; set; }
public virtual ICollection<Items_RATINGS> Items_RATINGS { get; set; }
}
public partial class Items_RATINGS
{
public int ItemsID { get; set; }
public byte ItemsRATING { get; set; }
public string COMMENTS { get; set; }
public virtual Items Items { get; set; }
}
In the controller calls, I have added
return View(db.Items.Include(c => c.Items_RATINGS).ToList());
In view, I'm calling
Html.DisplayFor(modelItem => item.Items_RATINGS.Average(dr => dr.ItemsRATING))
However, I'm getting following error
Templates can be used only with field access, property access, single-dimension array index, or single-parameter custom indexer expressions.
How do I retireve Average Value
it's better to calculate the average and then pass to the view, so in you model you could have this:
private ICollection<Items_RATINGS> _Items_RATINGS ;
public double Items_RATINGS
{
get { return _Items_RATINGS.Average(c => c.ItemsRATING); }
set;
}
and then bind the Items_RATINGS in view:
Html.DisplayFor(modelItem => item.Items_RATINGS)
Related
I have developed a Site using asp net mvc 6, I have used enom as categories, now I need to get product count under each enom value,
Here is my enum look like,
namespace ecom.Data
{
public enum BookCategory
{
Action,
Comedy,
Drama,
Others
}
}
What I want is get products count under this enoms,
See here, Front view without product count
This is my Controller Looks like,
using ecom.Data.Services;
namespace ecom.Controllers;
public class HomeController : Controller
{
private readonly IBooksService _service;
public async Task <IActionResult> BookView(string slug)
{
var data = await _service.GetBookBySlugAsync(slug);
if(data == null) return View("NotFound");
return View("BookView", data);
}
This is model which i use as product model,
namespace ecom.Models
{
public class Book:IEntityBase
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Slug { get; set; }
public string Description { get; set; }
public double Price { get; set; }
public string Image { get; set; }
public string PublishDate { get; set; }
//enom
public BookCategory BookCategory { get; set; }
//Relationships
public List<Writter_Book> Writter_Books { get; set; }
//Publisher
public int PublisherId { get; set; }
[ForeignKey("PublisherId")]
public Publisher Publisher { get; set; }
}
}
My cshtml view right now,
#foreach (var cat in Html.GetEnumSelectList<BookCategory>())
{
<li>#cat.Text <span>()</span></li>
}
I want to get product count to above inside foreach's span tag,
Anyone can help me with that??
You can use GroupBy and Count to achieve your goal.
Something like
myBooksDataSource
.GroupBy(book => book.BookCategory)
.Select(group => new { Category = group.Key, Count = group.Count() });
Which will output a collection of (BookCategory, Count).
{ BookCategory.Action, 20 }
{ BookCategory.Comedy, 204 }
You can create a class to hold that data if you need to pass it around.
public class BookCategoryCountData
{
public BookCategory Category { get; set; }
public int Count { get; set; }
}
and select using that object.
public IEnumerable<BookCategoryCountData> GetBookCountByCategory()
{
return _context.Books
.GroupBy(book => book.BookCategory)
.Select(group => new BookCategoryCountData() {
Category = group.Key,
Count = group.Count()
});
}
I have a tricky model I'm trying to normalize for binary classification. Here's an example of the model structure. I renamed a few things just to simplify.
public class Review
{
public bool Label { get; set; }
public ReviewItem ReviewItem { get; set; }
public List<OtherItem> OtherItems { get; set; }
}
public class ReviewItem
{
public string SomeText { get; set; }
public float SomeNumber { get; set; }
public bool SomeBool { get; set; }
}
public class OtherItem
{
public string SomeDifferentText { get; set; }
public float SomeDifferentNumber { get; set; }
public bool SomeDifferentBool { get; set; }
}
There can be any number of OtherItem in the List. This is what I tried to flatten the model a bit.
public class ReviewMlModel
{
public bool Label { get; set; }
public string ReviewItem_SomeText { get; set; }
public float ReviewItem_SomeNumber { get; set; }
public bool ReviewItem_SomeBool { get; set; }
public string[] OtherItem_SomeDifferentText { get; set; }
public float[] OtherItem_SomeDifferentNumber { get; set; }
public bool[] OtherItem_SomeDifferentBool { get; set; }
}
From there I tried this to normalize it:
var data = mlContext.Data.LoadFromEnumerable(allReviews);
var dataPrepEstimator = mlContext.Transforms.Text.FeaturizeText("ReviewItem_SomeText")
.Append(mlContext.Transforms.Text.FeaturizeText("OtherItem_SomeDifferentText"))
.Append(mlContext.Transforms.Conversion.ConvertType("ReviewItem_SomeBool"))
.Append(mlContext.Transforms.Conversion.ConvertType("OtherItem_SomeDifferentBool"))
.Append(mlContext.Transforms.Concatenate("Features",
"ReviewItem_SomeText", "OtherItem_SomeDifferentText", "ReviewItem_SomeBool",
"OtherItem_SomeDifferentBool", "ReviewItem_SomeNumber", "OtherItem_SomeDifferentNumber"));
var transformedData = dataPrepEstimator.Fit(data).Transform(data);
var model = mlContext.BinaryClassification.Trainers.AveragedPerceptron()
.Fit(transformedData);
This gives me the exception on the line where I try to create the model:
Schema mismatch for feature column 'Features': expected Vector<Single>, got VarVector<Single> (Parameter 'inputSchema')
I'm guessing this is due to the fact these arrays all have variable lengths, but I don't see a way to transform the VarVector. Do I need to go make the original allReviews enumerable have the same length array for every array? Or am I way off track with how I flattened the original model?
Looks like it was a combination of changing the arrays to all have the same size, as well as adding [VectorType(size)] attribute to all the array properties.
Probably I'm doing this the wrong way, but this is a sample of the code I'm dealing with:
ViewModel
public class TasDataView
{
public int nTas { get; set; }
public string codTas { get; set; }
public decimal nValue { get; set; }
public bool nState { get; set; }
public System.DateTime nDate { get; set; }
public IEnumerable<Segments> ListSegments { get; set; }
public IEnumerable<Tas> ListTas { get; set; }
}
Segments class
public class Segments
{
public int segmentValue { get; set; }
public string segmentCode { get; set; }
public bool nState { get; set; }
public System.DateTime nDate { get; set; }
}
So, when I call the View with my controller, I send an TasDataView object as parameter:
public ActionResult AddTas()
{
TasDataView TDV = new TasDataView();
SegmentsManager SM = new SegmentsManager();
TDV.ListSegments = SM.DataSegments();
return View(TDV);
}
And here is the problem I'm facing. I need all the values of ViewModel for this View to work. So, for example, I have:
#model Project.Models.ViewModel.TasDataView
#Html.EditorFor(model => model.nValue, new { htmlAttributes = new { #class = "form-control" } })
And that will create the box for inserting whatever I want into nValue. But what if I want to access the ListSegments properties (from Segments class) that I sent to the View, so I can make a DropDown List ?
I can't do something like:
#Html.DropDownListFor(x => x.ListSegments.segmentValue, new SelectList(Model.ListSegments));
because all the ListSegments (like segmentValue) properties "are not in" TasDataView, but in Segments class.
How can I reorganize my DataView to be manageable for what I want? Instead of a List<> I should have plain variables?
I guess you're doing it wrong. The first argument of DropDownListFor is an expression that represents the property of the model that receives the value of the item selected in the drop down list. It must be some dedicate=d property somewhere on your model. e.g. selectedSegment.
EDIT:
So, if your goal is to present a list of values via model to the user in a drop down list and let the user pick a value, you'll need to:
have a property on the model to store the selected item (segment), e.g. selectedSegment,
properly render your segments as strings to present them.
The simplest solution is given below.
public class TasDataView
{
public int nTas { get; set; }
public string codTas { get; set; }
public decimal nValue { get; set; }
public bool nState { get; set; }
public System.DateTime nDate { get; set; }
public IEnumerable<Segments> ListSegments { get; set; }
public IEnumerable<Tas> ListTas { get; set; }
public Int32 selectedSegment;
}
#Html.DropDownListFor(x => x.selectedSegment, new SelectList(Model.ListSegments.Select(s => s.segmentCode)));
So there are no properties of the list you need to access.
I want to return the item that has the profile ID I send. So in order to do this I will need to loop through all of the Items -> WebProproperties -> profile. The Class structure is at the end of the question.
I would rather use LINQ than create a nested foreach. I have been trying to get this to work for more than an hour now. I am stuck.
My first idea was to simply use where. But that doesn't work because you need to have something on the other side that needs to equal.
this.Accounts.items.Where(a => a.webProperties.Where(b => b.profiles.Where(c => c.id == pSearchString)) ).FirstOrDefault();
My second idea was to try using Exists which I don't have much experience with:
Item test = from item in this.Accounts.items.Exists(a => a.webProperties.Exists(b => b.profiles.Exists(c => c.id == pSearchString))) select item;
This doesn't work either:
Could not find an implementation of query pattern for source type 'Bool'
public RootObject Accounts {get; set;}
public class RootObject
{
public string kind { get; set; }
public string username { get; set; }
public int totalResults { get; set; }
public int startIndex { get; set; }
public int itemsPerPage { get; set; }
public List<Item> items { get; set; }
}
public class Profile
{
public string kind { get; set; }
public string id { get; set; }
public string name { get; set; }
public string type { get; set; }
}
public class WebProperty
{
public string kind { get; set; }
public string id { get; set; }
public string name { get; set; }
public string internalWebPropertyId { get; set; }
public string level { get; set; }
public string websiteUrl { get; set; }
public List<Profile> profiles { get; set; }
}
public class Item
{
public string id { get; set; }
public string kind { get; set; }
public string name { get; set; }
public List<WebProperty> webProperties { get; set; }
}
You can use Any() to determine existence. Also, note that many of the extension methods have overloads which take a predicate, including FirstOrDefault():
this.Accounts.items.FirstOrDefault(a => a.webProperties
.Any(b => b.profiles
.Any(c => c.id == pSearchString)));
You are looking for the .Any() operation I think. This will return true/false for whether there are any items matching your query.
For example:
if (this.Accounts.Items.Any(i=>i.webProperties.Any(wp=>wp.profiles.Any(p=>p.id == MySearchId)));
EDIT: You have full answer (was posted while I was composing mine) and as pointed out in comments my answer isn't actually returning your found item, just letting you know whether there is one. You can rework the first .Any to be a .FirstOrDefault to get that match.
E.g.
var result = this.Accounts.Items.FirstOrDefault(i=>i.webProperties.Any(wp=>wp.profiles.Any(p=>p.id == MySearchId)))
You can use the below mentioned code.
var abc = rr.items.Where(p => p.webProperties.Any(c => c.profiles.Any(d => d.id == "1"))).FirstOrDefault();
Just for your reference, your class should look like:
public class RootObject
{
public string kind { get; set; }
public string username { get; set; }
public int totalResults { get; set; }
public int startIndex { get; set; }
public int itemsPerPage { get; set; }
private List<Item> _items=new List<Item>();
public List<Item> items
{
get { return _items; }
set { _items = value; }
}
}
So I have a model that contains a list of models which contains items, and so on, like this:
public partial class CART
{
public CART()
{
//this.CART_DETAIL = new HashSet<CART_DETAIL>();
this.CART_DETAIL = new List<CART_DETAIL>();
}
public int CART_IDE { get; set; }
public int CART_COUNT { get; set; }
public string SHOPPING_CART_IDE { get; set; }
public virtual IList<CART_DETAIL> CART_DETAIL { get; set; }
}
public partial class CART_DETAIL
{
public int CART_DETAIL_IDE { get; set; }
public int CART_IDE { get; set; }
public int CART_DETAIL_COUNT { get; set; }
public Nullable<int> PACK_IDE { get; set; }
public Nullable<int> BACKSTORE_INVENTORY_IDE { get; set; }
public virtual CART CART { get; set; }
public virtual PACK PACK { get; set; }
public virtual BACKSTORE_INVENTORY BACKSTORE_INVENTORY { get; set; }
}
public partial class BACKSTORE_INVENTORY
{
public BACKSTORE_INVENTORY()
{
this.CART_DETAIL = new HashSet<CART_DETAIL>();
this.ORDER_DETAIL = new HashSet<ORDER_DETAIL>();
}
public int BACKSTORE_INVENTORY_IDE { get; set; }
public int INVENT_IDE { get; set; }
public int STORE_IDE { get; set; }
public decimal BACKSTORE_INVENTORY_PRICE { get; set; }
public int BACKSTORE_STOCK_QTY { get; set; }
public decimal BACKSTORE_DISCOUNT { get; set; }
public decimal BACKSTORE_SELLING_PRICE { get; set; }
public virtual INVENTORY INVENTORY { get; set; }
public virtual STORE STORE { get; set; }
public virtual ICollection<CART_DETAIL> CART_DETAIL { get; set; }
public virtual ICollection<ORDER_DETAIL> ORDER_DETAIL { get; set; }
}
When I open a connection and consult the data, everything's fine, but if I retrive the whole data in a view, for example, unless I modify the Hashset to a List and then proceed like this:
CART cart =
db.CART.FirstOrDefault(_item => _item.SHOPPING_CART_IDE == mShoppingCartID && _item.CART_ACTIVE_INDICATOR);
if (cart != null)
{
cart.CART_EXP_TIME = DateTime.Now.AddMinutes(90);
cart.USER_SESSION_IDE = UserSessionManager.GetUserSession().mUserSessionID;
cart.CART_DETAIL = cart.CART_DETAIL.ToList();
foreach (var cartDetail in cart.CART_DETAIL)
{
if(cartDetail.BACKSTORE_INVENTORY_IDE != null)
{
cartDetail.BACKSTORE_INVENTORY =
db.BACKSTORE_INVENTORY.First(_item => _item.BACKSTORE_INVENTORY_IDE == cartDetail.BACKSTORE_INVENTORY_IDE);
cartDetail.BACKSTORE_INVENTORY.INVENTORY =
db.INVENTORY.Find(cartDetail.BACKSTORE_INVENTORY.INVENT_IDE);
cartDetail.BACKSTORE_INVENTORY.INVENTORY.CARD =
db.CARD.Find(cartDetail.BACKSTORE_INVENTORY.INVENTORY.CARD_IDE);
}
else
{
cartDetail.PACK = db.PACK.First(_item => _item.PACK_IDE == cartDetail.PACK_IDE);
}
}
db.SaveChanges();
}
I get the following error: CS0021: Cannot apply indexing with [] to an expression of type 'System.Collections.Generic.ICollection<MyApp.Models.DAL.Entities.CART_DETAIL>' which I understand is because the ICollection does not afford indexing, and then I get The ObjectContext instance has been disposed and can no longer be used for operations that require a connection. for items that I forgot to retrive.
So my question: what makes this happen? Is there a way to retrieve all the data at once without having to get all specific items separately? A better way to do things?
What are you trying to achieve form the above code?
I am struggling to follow what your end goal is but would something along these lines be what you are looking for:
public List<Cart> GetAllInCart()
{
return db.CART.Where(a => a.Cart_IDE == CartIDE)
.Include(x => x.Cart_Detail)
.Include(x => x.Cart_Detail.Pack)
.Include(x => x.Cart_Detail.Backstore_Inventory)
.ToList()
}
I hope this helps :)