I'm trying to create a list of orders in a custom Controller in a NopCommerce/MVC application and i want the list to be sorted by creationDate and contain total orders for that date and convert these values to string format.
The thing is i don't want an ActionResult displaying a grid in the view like in Admin/Orders. All i want is a List of all paid orders between model.StartDate and model.EndDate that contains two parameters "CreationDateUtc" and TotalOrders". i simply just need a list containing the data of orders sorted by creationdate.
The if i choose StartDate 2014-03-29 and EndDate 2014-04-02 the output i want would look something like this:
List OrdersTotalList with parameters CreationDateUtc and TotalOrders
CreationDateUtc "2014-03-29"
TotalOrders "562"
CreationDateUtc "2014-03-30"
TotalOrders "485"
CreationDateUtc "2014-03-31"
TotalOrders "733"
CreationDateUtc "2014-04-01"
TotalOrders "729"
CreationDateUtc "2014-04-02"
TotalOrders "681
"
I'm trying to access the data by an implementations of OrderList from OrderController in my CustomController. Problem is this method always returns 10 objects when infact the total number of orders within this timespace is 58. When debugging Total = orders.TotalCount are actually showing 58 orders as one int value). Also a gridmodel is used here but i really don't need a gridmodel, i just need the data from the database:
public List OrderList(GridCommand command, OrderListModel model, OrderModel Omodel)
{
DateTime S = new DateTime(2014, 3, 29); //-- Dates for testing
DateTime E = new DateTime(2014, 4, 02);
model.StartDate = S;
model.EndDate = E;
DateTime? startDateValue = (model.StartDate == null) ? null
: (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.StartDate.Value, _dateTimeHelper.CurrentTimeZone);
DateTime? endDateValue = (model.EndDate == null) ? null
: (DateTime?)_dateTimeHelper.ConvertToUtcTime(model.EndDate.Value, _dateTimeHelper.CurrentTimeZone).AddDays(1);
OrderStatus? orderStatus = model.OrderStatusId > 0 ? (OrderStatus?)(model.OrderStatusId) : null;
PaymentStatus? paymentStatus = model.PaymentStatusId > 0 ? (PaymentStatus?)(model.PaymentStatusId) : null;
ShippingStatus? shippingStatus = model.ShippingStatusId > 0 ? (ShippingStatus?)(model.ShippingStatusId) : null;
//load orders
var orders = _orderService.SearchOrders(startDateValue, endDateValue, orderStatus,
paymentStatus, shippingStatus, model.CustomerEmail, model.OrderGuid, command.Page - 1, command.PageSize);
var gridModel = new GridModel<OrderModel>
{
Data = orders.Select(x =>
{
var customerCurrency = _currencyService.GetCurrencyByCode(x.CustomerCurrencyCode);
var totalInCustomerCurrency = _currencyService.ConvertCurrency(x.OrderTotal, x.CurrencyRate);
return new OrderModel()
{
Id = x.Id,
OrderTotal = _priceFormatter.FormatPrice(totalInCustomerCurrency, true, customerCurrency),
OrderStatus = x.OrderStatus.GetLocalizedEnum(_localizationService, _workContext),
PaymentStatus = x.PaymentStatus.GetLocalizedEnum(_localizationService, _workContext),
ShippingStatus = x.ShippingStatus.GetLocalizedEnum(_localizationService, _workContext),
CreatedOn = _dateTimeHelper.ConvertToUserTime(x.CreatedOnUtc, DateTimeKind.Utc)
};
}),
Total = orders.TotalCount <-- Returns all orders (58) but as an integer
};
var reportSummary = _orderReportService.GetOrderAverageReportLine
(orderStatus, paymentStatus, shippingStatus, startDateValue, endDateValue, model.CustomerEmail);
var profit = _orderReportService.ProfitReport
(orderStatus, paymentStatus, shippingStatus, startDateValue, endDateValue, model.CustomerEmail);
var aggregator = new OrderModel()
{
aggregatorprofit = _priceFormatter.FormatPrice(profit, true, false),
aggregatortax = _priceFormatter.FormatPrice(reportSummary.SumTax, true, false),
aggregatortotal = _priceFormatter.FormatPrice(reportSummary.SumOrders, true, false)
//aggregatordates =
};
List<Order> TotalProductsSold = new List<Order>();
foreach (var o in orders)
{
TotalProductsSold.Add(o);
}
return TotalProductsSold.ToList(); //<-- returns 10 orders containing all order info
}
If i understand correct in order to archive this i have to first search through orders and if their PaymentStatus is Paid. Then create a List in the Method from above. A foreach loop could iterate through orders and add orders to the List, all though i need to specify i only want CreationDate and TotalOrders for that date as parameters in the List.
I know this isn't right but i emagine something similar. The thing is i need a list of order objects and not one object with one value:
List<OrderModel> OrdersTotalList = new List<OrderModel>();
foreach (var o in orders)
{
OrderModel OM = new OrderModel(OM.OrderTotal, OM.CreatedOn);
OrdersTotalList.Add(OM);
}
return OrdersTotalList; //--
Am i completely of or is this the right aproach? I was hoping someone more familiar with NopCommerce knows more about this.
Sorry for all the text
Thank you
Solved.
In order to get a full list of orders you can create a new constructor in IOrderService/OrderService that is of type List instead of IPagedList. The method used for searching orders are called "SearchOrders" and is of type IPagedList. IPagedList contains the property PageSize wich results in only 10 orders.
You can create a new method with same implementation as SearchOrders and change IPagedList to List, remove "int pageIndex" and "int pageSize".
Then use:
_orderService.YourNewConstructor(DateTime? startTime, DateTime? endTime,
OrderStatus? os, PaymentStatus? ps, ShippingStatus? ss, string billingEmail,
string orderGuid)
{
some code...
}
This will give you access to all orders.
Related
I'm following a YouTube series which teachs ASP.NET MVC. In the tutorial the teacher shows how to make a simple search functionality however in my case it's different.
I have search criteria: Studies (Dropdown), Country (Dropdown), Status (Dropdown) and Keyword (Input).
My question is how do I query the database to show the results depending on the search criteria that was chosen?
To be more clear:
If the User has chosen Studies and Country only then the code should use values from Studies and Country to search the respective database column.
Click here for the UI Design
Table: Students
[StudentID] INT IDENTITY (1, 1) NOT NULL,
[StudentName] VARCHAR (50) NOT NULL,
[StudentStudiesID] INT NOT NULL,
[StudentCountry] VARCHAR (50) NOT NULL,
[StudentCity] VARCHAR (50) NOT NULL,
[StudentStatus] VARCHAR (50) NOT NULL,
CONSTRAINT [PK_Students] PRIMARY KEY CLUSTERED ([StudentID] ASC),
CONSTRAINT [FK_Students_Studies] FOREIGN KEY ([StudentStudiesID]) REFERENCES [dbo].[Studies] ([StudiesID])
SearchController.cs
public class SearchController : Controller
{
public ActionResult Index()
{
DatabaseEntitiesModel db = new DatabaseEntitiesModel();
int Studies;
int.TryParse(Request.QueryString["Studies"], out Studies);
var Country = Request.QueryString["Country"];
var Status = Request.QueryString["Status"];
var Keyword = Request.QueryString["Keyword"];
IQueryable <Student> SearchQuery = db.Students;
List<SearchViewModel> SVM = SearchQuery.Select(x => new SearchViewModel
{
StudentID = x.StudentID,
StudentName = x.StudentName,
StudentCountry = x.StudentCountry,
StudentCity = x.StudentCity,
StudiesName = x.Study.StudiesName,
StudentStatus = x.StudentStatus
}).OrderByDescending(x => x.StudentID).ToList();
return View( SVM );
}
}
Reuse SearchQuery (items are lazy-loaded, until you call ToList()) and add as many specific Where() clauses/calls as you need:
// the type (IQueryable<Student>) should be defined explicitly
// details: https://stackoverflow.com/questions/21969154/cannot-implicitly-convert-type-system-linq-iqueryable-to-system-data-entity-d
IQueryable<Student> query = db.Students;
if(viewModel.Filter1 != null) {
query = query.Where(i => i.SomeStudentProperty1 == viewModel.Filter1);
}
if(viewModel.Filter2 != null) {
query = query.Where(i => i.SomeStudentProperty2 == viewModel.Filter2);
}
var result = query.ToList();
The easiest way to do this would be to test each condition and if it meets what you want, add a Where clause. Something like this:
int.TryParse(Request.QueryString["Studies"], out Studies);
var Country = Request.QueryString["Country"];
var Status = Request.QueryString["Status"];
var Keyword = Request.QueryString["Keyword"];
IQueryable<Student> SearchQuery = db.Students;
if(Studies > 0)
{
SearchQuery = SearchQuery.Where(s => s.StudiesID == Studies);
}
if(!string.IsNullOrEmpty(Country))
{
SearchQuery = SearchQuery.Where(s => s.StudentCountry == Country);
}
...More conditions can go here
Because of Lazy Loading, the actual query isn't executed until you call .ToList(), or iterate over the collection. Hopefully, this gets you started on the right track.
Edit
In my haste, I changed your IQueryable to a var. Fixed.
Also, as Erik pointed out, using Request.QueryString is not the way to go. You'll instead want to pass these values in to the action method. So, something like:
public ActionResult Index(int studies, string status, string country, string keyword)
This has stumped me for the last 3 hours... I'm probably just tired but can't seem to get the logic correct. Would I'd like to do is;
get a list of survey topics and a list of rated survey topics.
if none of the topics have been rated, return the first to the user to rate.
if they've all been rated, return a view saying 'yay you completed the survey'
else identify which ones have not been rated and serve those up in a view.
All topics are served up 1 at a time, each time the topic is rated, its saved then i redirect them back to this controller.
I think my string.equals is not working but can't seem to figure out why. The controller just keeps serving up the same topic. (I'm assuming its the first record that matches vs the one that doesn't?)
public ActionResult Index(string page)
{
Rating rating = new Rating();
var surveyItems = (from s in db.Surveys
where s.Category.Name.Equals(page)
select s).ToList();
var ratedItems = (from r in db.Ratings
where r.Category.Equals(page) && r.UserName.Equals(User.Identity.Name)
select r).ToList();
if (ratedItems.Count() == 0 && surveyItems.Count() > 0)
{
ViewBag.Remaining = surveyItems.Count();
rating.Topic = surveyItems.Select(si => si.Topic).FirstOrDefault();
rating.Category = page;
return View(rating);
}
else if (ratedItems.Count() > 0 && ratedItems.Count() == surveyItems.Count())
{
return View("Finished");
}
else
{
foreach (var si in surveyItems)
{
foreach (var ri in ratedItems)
{
if (!si.Topic.Equals(ri.Topic))
{
rating.Topic = si.Topic;
rating.Category = page;
ViewBag.Total = surveyItems.Count();
ViewBag.Remaining = ViewBag.Total - ratedItems.Count();
return View(rating);
}
}
}
}
Firstly, to answer your question directly, your inner loop will always fail because the 2 lists are not ordered AND theres no gaurantee that item 1 in each list will be the same. Even if they are, the second item from the first list will not equal the first item from the second list (inner loop).
Best bet is to tackle this entirely with LINQ, and while the query is a little difficult to read, the code is a lot cleaner.
var rating = (from s in db.Surveys
join r in db.Ratingson s.Topic equals r.Topic into rated
from ri in rated.Where(x => x.Username == User.Identity.Name).DefaultIfEmpty()
where s.Category.Name.Equals(page) && ri.Topic == null
select new RatingViewModel {Topic = s.Topic, Category = s.Category, Total = db.SurveyItems.Count(), Rated = rated.Count()}).FirstOrDefault();
if (rating == null)
{
return View("Finished");
}
return View(rating);
The LINQ query is essentially the equivalent of the following SQL (give or take)
SELECT * FROM Surveys s
LEFT OUTER JOIN Ratings r ON s.Topic = r.Topic AND r.Username = 'user'
WHERE r.Topic IS NULL
You'll also note that the query projects to RatingsViewModel, I added this because I noticed you had a few references to ViewBag as well as your Rating entity.
RatingViewModel:
public class RatingViewModel
{
public string Topic { get; set; }
public string Category { get; set; }
public int Total { get; set; }
public int Rated { get; set; }
public int Remaining {
get { return Total - Rated; }
}
}
EDIT
Played around with the query a little more, and this is the closest I could get:
// define the where's here so we can use the IQueryable multiple times in LINQ
var surveys = db.Surveys.Where(x => x.Category.Name.Equals(page));
var ratedItems = db.Ratings.Where(y => y.Username == User.Identity.Name && y.Category.Name.Equals(page));
var rated = ratedItems.Count(); // get the rated count here, otherwise we end up with an exception inside the LINQ query
var surveyTopic =
(from s in surveys
// LEFT OUTER JOIN to ratings
join r in ratedItems on s.Topic equals r.Topic into ratedSurveys
from ri in ratedSurveys.DefaultIfEmpty()
// define variables
let total = surveys.Count()
//let rated = ratedItems.Count() -- this throws a NotSupportedException... which seems odd considering the line above
// get non-rated surveys, in this case the RIGHT side of the join (Ratings) is null if there is no rating
where ri.Topic == null
// projection
select new RatingViewModel
{
Topic = s.Topic,
Category = s.Category,
Rated = rated,
Total = total
}).FirstOrDefault();
return surveyTopic == null ? View("Finished") : View(surveyTopic);
Unfortunately this results in 2 DB queries which I was hoping to avoid, still this should be a little closer to what you are after.
Brent,
It didn't like your solution so I tried to revamp it a bit but it's still not happy. Here's my tweak;
var surveyTopic = (from s in db.Surveys.Where(x => x.Category.Name.Equals(page))
let total = s.Topic.Count()
join r in db.Ratings.Where(y => y.UserName == User.Identity.Name) on s.Topic equals r.Topic
let rated = r.Topic.Count()
where r.Topic == null
select new RatingViewModel
{
Topic = s.Topic,
Category = s.Category.Name,
Rated = rated+1,
Total = total
}).FirstOrDefault();
I'm new in linq and i have a simple problem.
i must count how many books have user.
i know its easy but i cant do this.
//database
public List<UserViewModel> GetAllUsers()
{
var result = from u in databaseContext.User
select new UserViewModel
{
UserId = u.UserId,
LastName = u.LastName,
FirstName = u.FirstName,
BirthDate = u.BirthDate,
Email = u.Email,
Phone = u.Phone,
AddDate = u.AddDate,
ModifiedDate = u.ModifiedDate,
IsActive = u.IsActive,
};
return result.ToList();
}
how to do this?
In your model it seems that every Borrow has exactly one Book associated with it. If that is the case and the books cannot be taken more than once (which should be also correct) then all you need to do is:
int count = context.Users.First(x=>x.UserId = 1).Borrow.Count();
This way you can count all the Borrows of a given user supposing that they are all associated with a book.
If two borrows can have the same book than you should slightly rewrite this query to this:
int count = db.Borrow.Where(x => x.UserID== 1).Select(x => x.BookId).Distinct().Count();
This way you will get only the unique books borrowed from the user with id of 1.
To count only the unique books that are not returned use this:
int count = db.Borrow.Where(x => x.UserID== 1 && x.IsReturned == false).Select(x => x.BookId).Distinct().Count();
var getAllProducts = _productService.GetAllProducts();
if (productstest.Count > 0)
{
model.idproduct.Add(new SelectListItem()
{
Value = "0",
Text = _localizationService.GetResource("Common.All")
});
foreach (var m in getAllProducts)
model.idproduct.Add(new SelectListItem()
{
Value = m.Id.ToString(),
**Text = m.Size.Distinct().ToString(),**
Selected = model.Pid == m.Id
});
}
public virtual IList<Product> GetAllProducts(bool showHidden = false)
{
var query = from p in _productRepository.Table
orderby p.Name
where (showHidden || p.Published) &&
!p.Deleted
select p;
var products = query.ToList();
return products;
}
The issue is even i tried to populate the select list with distinct size using: Text = m.Size.Distinct().ToString(), but it shows the duplicate for instance 100 products are of size 33 cm , the list will populate the dropdownlist in the view with 33cm occuring 100 times , I dont want to show 100 times , just want to show 1 time, Can any one assist me with this issue ?
Presumably you are only trying to show one product of each different size... if so initialising your getAllProducts variable like so will do the trick:
var getAllProducts = _productService.GetAllProducts().GroupBy(p => p.Size).Select(g => g.First());
I have a rather simple scenario where I have two tables in which I want to add data. They are managed with primary key/foreign key. I want to add new data into TABLE A and then retrieve the Id and insert into TABLE B.
I can certainly do it with a stored procedure, but I'm looking at trying to do it using Linq.
What is the best approach ?
I can certainly get the ID and do two separate inserts but that doesn't certainly seem to be a very good way of doing things.
db.Table.InsertOnSubmit(dbObject);
db.SubmitChanges();
Int32 id = dbOject.Id;
//Rest of the code
Any way to elegantly do this?
Do you have the relationship defined between the 2 tables in the object relational designed? If so, you can have linq take care of assigning the ID property of the second table automatically.
Example...
Table A – Order
OrderId
OrderDate
Table B – Order Item
OrderItemId
OrderId
ItemId
Code (Using LINQ-to-SQL):
Order order = new Order();
Order.OrderDate = DateTime.Now();
dataContext.InsertOnSubmit(order);
OrderItem item1 = new OrderItem();
Item1.ItemId = 123;
//Note: We set the Order property, which is an Order object
// We do not set the OrderId property
// LINQ will know to use the Id that is assigned from the order above
Item1.Order = order;
dataContext.InsertOnSubmit(item1);
dataContext.SubmitChanges();
hi i insert data into three table using this code
Product_Table AddProducttbl = new Product_Table();
Product_Company Companytbl = new Product_Company();
Product_Category Categorytbl = new Product_Category();
// genrate product id's
long Productid = (from p in Accountdc.Product_Tables
select p.Product_ID ).FirstOrDefault();
if (Productid == 0)
Productid++;
else
Productid = (from lng in Accountdc.Product_Tables
select lng.Product_ID ).Max() + 1;
try
{
AddProducttbl.Product_ID = Productid;
AddProducttbl.Product_Name = Request.Form["ProductName"];
AddProducttbl.Reorder_Label = Request.Form["ReorderLevel"];
AddProducttbl.Unit = Convert.ToDecimal(Request.Form["Unit"]);
AddProducttbl.Selling_Price = Convert.ToDecimal(Request.Form["Selling_Price"]);
AddProducttbl.MRP = Convert.ToDecimal(Request.Form["MRP"]);
// Accountdc.Product_Tables.InsertOnSubmit(AddProducttbl );
// genrate category id's
long Companyid = (from c in Accountdc.Product_Companies
select c.Product_Company_ID).FirstOrDefault();
if (Companyid == 0)
Companyid++;
else
Companyid = (from Ct in Accountdc.Product_Companies
select Ct.Product_Company_ID).Max() + 1;
Companytbl.Product_Company_ID = Companyid;
Companytbl.Product_Company_Name = Request.Form["Company"];
AddProducttbl.Product_Company = Companytbl;
//Genrate Category id's
long Categoryid = (from ct in Accountdc.Product_Categories
select ct.Product_Category_ID).FirstOrDefault();
if (Categoryid == 0)
Categoryid++;
else
Categoryid = (from Ct in Accountdc.Product_Categories
select Ct.Product_Category_ID).Max() + 1;
Categorytbl.Product_Category_ID = Categoryid;
Categorytbl.Product_Category_Name = Request.Form["Category"];
AddProducttbl.Product_Category = Categorytbl;
Accountdc.Product_Tables.InsertOnSubmit(AddProducttbl);
Accountdc.SubmitChanges();
}
catch
{
ViewData["submit Error"] = "No Product Submit";
}