How do I take last n elements from nested collection - asp.net-mvc

I am buinding a chat for an application, so when a user logs in I need to send them all 'unseen' messages, I am using entityframework, Id like to return only the last 20 unseen messages. but my query is not working, currently I get this exception
Count must be a DbConstantExpression or a DbParameterReferenceExpression
what am I doing wrong?
List<ChatVM> unSeenChats = db.Chats.Where(chat => !chat.Seen)
.Select(chat => new ChatVM
{
Id = chat.Id,
IsAnnonymous = chat.IsAnnonymous,
UserName = chat.UserName,
Messages = chat.Messages
.OrderBy(x => x.DateTime)
.Skip(chat.Messages.Count - 20 > 0
? chat.Messages.Count - 20
: 0)
.Take(20)
.Select(message => new MessageVM
{
Id = message.Id,
DateTime = message.DateTime,
Text = message.Text
}).ToList()
}).ToList();
my models are as follows:
public class Chat
{
...
public virtual ICollection<Message> Messages { get; set; }
}
public class Message
{
...
public int ChatId { get; set; }
public virtual Chat Chat { get; set; }
}
public class Entities : IdentityDbContext<ApplicationUser>
{
....
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
...
modelBuilder.Entity<Message>()
.HasRequired(p => p.Chat).WithMany(p => p.Messages).WillCascadeOnDelete(true);
}
}
thanks

I don't think you're allowed to use .Count from within the query (hence the error that you're seeing). In any case, I think you're looking at this from the wrong perspective. You should probably be using the OrderByDescending method, and then just grab the first 20 posts from there.
Something like this:
List<ChatVM> unSeenChats = db.Chats.Where(chat => !chat.Seen)
.Select(chat => new ChatVM
{
Id = chat.Id,
IsAnnonymous = chat.IsAnnonymous,
UserName = chat.UserName,
Messages = chat.Messages
.OrderByDescending(x => x.DateTime)
.Take(20)
.Select(message => new MessageVM
{
Id = message.Id,
DateTime = message.DateTime,
Text = message.Text
}).ToList()
}).ToList();

Related

A specified Include path is not valid. The EntityType does not declare a navigation property with the name

The code I have below works. It's pulls in a list of every item in my One Repository.
When I add my second table to pull all the items out of THAT table I get the following error, on my DataTwo I can't figure out why it's throwing this error as the first one is programmed the exact same way.
"A specified Include path is not valid. The EntityType does not declare a navigation property with the name"
View Model
public IList<OneVM> Ones { get; set; }
public IList<TwoVM> Twos { get; set; }
public ViewModelVM()
{
this.Ones = new List<OneVM>();
this.Twos = new List<TwoVM>();
}
Working Original Code Below (Controller)
public ActionResult Directory()
{
var vm = new ViewModelVM();
var datas = _OneRepository.GetData();
vm.Datas = _mapper.Map<IList<DataVM>>(datas.OrderBy(i => i.Name));
return View(vm);
}
Desired Broken Code Below (Controller)
public ActionResult Directory()
{
var vm = new FormDirectoryVM();
var datas = _OneRepository.GetData();
var datasTwo= _TwoRepository.GetMoreData();
vm.Datas = _mapper.Map<IList<DataVM>>(datas.OrderBy(i => i.Name));
return View(vm);
vm.DatasTwo= _mapper.Map<IList<DataTwoVM>>(datasTwo);
return View(vm);
}
The problem was my Repository. I was including something that didn't need to be.
public IEnumerable<Two> GetMoreData()
{
return _context.Twos
.Include(i => i.Title) // I don't need this line
.Include(i => i.Description) // I don't need this line either
.Include(i => i.Keywords)
.Include(j => j.Text) // Or this Line
.Where(i => !i.IsDeleted)
;
}

LINQ JOIN with WHERE condition

I have a problem with the creation of LINQ query with lambda expression. I need join two tables and make some conditions. I have two tables MSR and BOMDetail.
MSR had theese columns -> MSRID, PN, Buyer,Plant EditDate.
BomDetail had theese columns -> BOMID, PN, AltQty, Plant, EditDate.
And i need to write this query into LINQ.
SELECT MSR.PN, Buyer, MSR.EditDate, MSR.Plant FROM MSR
JOIN BomDetail bd ON MSR.PN = bd.PN AND MSR.Plant = bd.Plant
WHERE LEN(ISNULL(bd.AltQty,''))>0
I need to make 2 conditions PN must equals between tables and Plant's too.
I have for result ViewModel in asp.net MVC.
public class MSRViewModel
{
public string PN { get; set; }
public string Buyer { get; set; }
public string Plant { get; set; }
public DateTime EditDate { get; set; }
}
And here is my sample, it works fine, but i don't know where i must write the second condition for bd.Plant = MSR.Plant.
var data = DbContext.BomDetails.Where(x => !string.IsNullOrEmpty(x.AltQty))
.Join(DbContext.MSRs
, bd => bd.PN,
msr => msr.PN,
(bd, msr) => new MSRViewModel
{
PN = msr.PN,
Buyer = msr.Buyer,
Plant = msr.Plant,
EditDate = msr.EditDate
}).ToList().AsEnumerable();
Thanks.
You can do this as follows:
var data = DbContext.BomDetails.Where(x => !string.IsNullOrEmpty(x.AltQty))
.Join(DbContext.MSRs
, bd => new { bd.PN, bd.Plant },
msr => new { msr.PN, msr.Plant },
(bd, msr) => new MSRViewModel
{
PN = msr.PN,
Buyer = msr.Buyer,
Plant = msr.Plant,
EditDate = msr.EditDate
}).ToList().AsEnumerable();

IQueryable to IEnumerable

public IEnumerable<Temp_Order> Get_Temp(string id)
{
//List<Temp_Order> data = new List<Temp_Order>();
IEnumerable<Temp_Order> data = db.Temp_Order
.Join(db.Items,
t_id => t_id.ItemId,
I_id => I_id.ItemId,
(t_id, I_id) => new { t_id.Quantity, I_id.ItemName })
.Where(x => x.ItemName == id);
return data;
}
In this method I want IEnumerable<Temp_Order>. So I will use this in controller and return to the view.
I'm getting this error:
Cannot implicitly convert type 'System.Linq.IQueryable' to 'System.Collections.Generic.IEnumerable'. An explicit conversion exists (are you missing a cast?) E:\WORK\Projects\RMS_Live\RMS_Live\Models\Order.cs
The Join is converting your query to an IEnumerable of an anonymous type. Add a Select on the end to cast is back to Temp_Order:
public IEnumerable<Temp_Order> Get_Temp(string id)
{
//List<Temp_Order> data = new List<Temp_Order>();
IEnumerable<Temp_Order> data = db.Temp_Order
.Join(db.Items, t_id => t_id.ItemId, I_id => I_id.ItemId, (t_id, I_id) => new { t_id.Quantity, I_id.ItemName })
.Where(x => x.ItemName == id)
.Select(a => new Temp_Order
{
ItemName = a.ItemName,
Property2 = a.Property2,
//snip
});
return data;
}
EDIT:
You indicate in the comments that you want all properties from both Temp_Order and Item which means you need another class. You can get away without creating another class, but it's much simpler in the long run. So first make your class, 2 ways spring to mind, you either replicate all the properties you need or just return the 2 objects, I'll use the latter:
public class OrderItem
{
public Temp_Order Temp_Order { get; set; }
public Item Item { get; set; }
}
Now your function becomes this:
public IEnumerable<OrderItem> Get_Temp(string id)
{
IEnumerable<OrderItem> data = db.Temp_Order
.Join(db.Items,
t_id => t_id.ItemId,
I_id => I_id.ItemId,
(t_id, I_id) => new OrderItem
{
Temp_Order = t_id,
Item = I_id
})
.Where(x => x.ItemName == id);
return data;
}
And in your view, make sure you set the model type to IEnumerable<OrderItem> and you can access all the properties like this:
#Model.Temp_Order.ItemName

Custom grid using Telerik pluggin in nopcommerce 2.8

Iam working on nopcommerce2.8 version. I have a problem with telerik plugin implementation to create new grid.
Iam implementing a concept where for a product i want to give different price for different customer. So to assign new price for different customers, in admin panel i am creating a grid in edit productvariant page using telerik. I have created a new tab to display these details. Iam able to display customer name and price in grid, but i am not able to call update function, when i click on update button after editing a row. The same update function i called for Deleting the grid row also, so when i click on delete the same update function getting trigger. I think some setting has been missed in View. Please help me to solve this update issue.
The model, view and controller of my nopcommerce in given below.
Thanks.
//Model
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
using FluentValidation.Attributes;
using Nop.Admin.Models.Customers;
using Nop.Admin.Validators.Catalog;
using Nop.Web.Framework;
using Nop.Web.Framework.Localization;
using Nop.Web.Framework.Mvc;
using Telerik.Web.Mvc;
namespace Nop.Admin.Models.Catalog
{
public partial class CustomerProductPriceModel : BaseNopModel
{
public int Customer_Id { get; set; }
[NopResourceDisplayName("Customer Name")]
public string Customer_name { get; set; }
[NopResourceDisplayName("Price")]
public decimal Price { get; set; }
[NopResourceDisplayName("Unit")]
public string Units { get; set; }
}
}
// view
#(Html.Telerik().Grid<CustomerProductPriceModel>()
.Name("Grid")
.DataKeys(x =>
{
x.Add(y => y.Customer_Id);
})
.DataBinding(dataBinding =>
{
dataBinding.Ajax()
.Select("CustomerProductPriceList", "ProductVariant", new { productVariantId = Model.Id })
.Update("CustomerPriceUpdate", "ProductVariant", new { productVariantId = Model.Id })
.Delete("CustomerPriceUpdate", "ProductVariant", new { productVariantId = Model.Id });
})
.Columns(columns =>
{
columns.Bound(y => y.Customer_name).Width(200).ReadOnly();
columns.Bound(y => y.Price).Width(100);
columns.Command(commands =>
{
commands.Edit().Text(T("Admin.Common.Edit").Text);
commands.Delete().Text(T("Admin.Common.Delete").Text);
}).Width(180);
})
.Editable(x =>
{
x.Mode(GridEditMode.InLine);
})
.EnableCustomBinding(true)
)
// controller
[GridAction(EnableCustomBinding = true)]
public ActionResult CustomerPriceUpdate(GridCommand command, CustomerProductPriceModel model, int productVariantId)
{
if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog))
return AccessDeniedView();
return CustomerProductPriceList(command, productVariantId);
}
[HttpPost, GridAction(EnableCustomBinding = true)]
public ActionResult CustomerProductPriceList(GridCommand command, int productVariantId)
{
if (!_permissionService.Authorize(StandardPermissionProvider.ManageCatalog))
return AccessDeniedView();
var productVariant = _productService.GetProductVariantById(productVariantId);
if (productVariant == null)
throw new ArgumentException("No product variant found with the specified id");
var CustomerPrices = PrepareCustomerProductPriceModel(productVariant.Product.Id);
var CustomerPricesa = CustomerPrices
.Select(x =>
{
return new CustomerProductPriceModel()
{
Customer_Id = x.Customer_Id,
Price = x.Price,
Units = x.Units,
Customer_name = x.Customer_name
};
})
.ToList();
var model = new GridModel<CustomerProductPriceModel>
{
Data = CustomerPricesa,
Total = CustomerPrices.Count
};
return new JsonResult
{
Data = model
};
}
Is there a reason not to use the built-in customer price levels already in place in nopCommerce, or are you wanting to display all prices at once?

Architecture abstraction problem in Repository ASP.NET MVC

I'm working with MVC ASP.NET 3.5 SP1 on VS2008.
I'm looking for a way to abstract this three methods I have in my Users repository.
public User GetUser(Log log)
{
return db.Users.FirstOrDefault(u => u.Logs.Any(l => l.id.Equals(log.id)));
}
public User GetUser(Product product)
{
return db.Users.FirstOrDefault(u => u.Products.Any(pr => pr.id.Equals(product.id)));
}
public User GetUser(Photo photo)
{
return db.Users.FirstOrDefault(u => u.Photos.Any(ph => ph.id.Equals(photo.id)));
}
My DB.edmx contains the models
User [id, username, ...]
Product [id, name, ...]
Photo [id, name, ...]
Log [id, data, ...]
Is it possible to have only ONE method for all of these (and future) methods based upon model.id search?
public User GetUser(Expression<Func<User, bool>> restriction)
{
return db.Users.Where(restriction).FirstOrDefault();
}
Now use it:
var u = Repository.GetUser(u => u.Logs.Any(l => l.id.Equals(log.id)));
You can also use MS DynamicQuery:
using System.Linq.Dynamic;
//...
public User GetUser(string propertyName, int id)
{
var restriction = propertyName + ".Any(id = #0)";
return db.Users.Where(restriction, id).FirstOrDefault();
}
var u = Repository.GetUser("Logs", log.id);
I may not have the syntax quite correct, but you get the idea.
If all the associated entities (Log, Product and Photo) will be searched by a common property (id INT) then maybe you could try something like this...
First, create an interface:
public interface IUserAssociation
{
int id { get; }
}
Then each of the three classes would implement this interface like so:
public partial class Product : IUserAssociation
{
}
The the GetUser method would look like so:
public User GetUser<T>(T entity) where T : IUserAssociation
{
var type = typeof(T);
if (type == typeof(Log))
{
return db.Users.FirstOrDefault(u => u.Logs.Any(l => l.id.Equals(entity.id)));
}
else if (type == typeof(Product))
{
return db.Users.FirstOrDefault(u => u.Products.Any(pr => pr.id.Equals(entity.id)));
}
else if (type == typeof(Photo))
{
return db.Users.FirstOrDefault(u => u.Photos.Any(ph => ph.id.Equals(entity.id)));
}
else
{
throw new ArgumentException();
}
}
Then you should be able to call GetUser and pass it a Log, Photo or Product entity from one method. It is not very elegant but would work for this specific situation.
I like Craig's solution better but I'd suggest this:
Repository.GetUser(u => u.Logs, log);
Which will be possible if all your entities derive from
public interface IEntity { public int Id { get; } }
Then method will be like
public User GetUser<T, Y>(Func<T, IList<Y>> getlist, Y sample)
where T: IEntity
where Y: IEntity
{
return db.Users.Select(x => getlist(x).Any(y => y.Id == sample.Id)).FirstOrDefault();
}
Also if we take idea of S#arp Architecture that if entity1.Id == entity2.Id (for persistent entities) then Equals(entity1, entity2) - we can use getlist(x).Contains(sample).

Resources