Asp.Net MVC 4 Controller to update database - c#

I have the following code:
Model
public class Orders
{
public int Id { get; set; }
public int UserId { get; set; }
public int ProductId { get; set; }
public int Quantity { get; set; }
public double Price { get; set; }
public bool OrderStatus { get; set; }
public string OrderNumber { get; set; }
public string AdminStatus { get; set; }
public bool PaymentStatus { get; set; }
public DateTime OrderDate { get; set; }
}
Controller
public ActionResult CheckOut()
{
if (Request.IsAjaxRequest())
{
var userid = WebSecurity.CurrentUserId;
var data = _bbdb.Orders.Where(x => x.OrderStatus == false && x.UserId == userid).ToList();
foreach (var item in data)
{
_bbdb.Orders.Add(new Orders
{
Id = item.Id,
UserId = item.UserId,
ProductId = item.ProductId,
Quantity = item.Quantity,
Price = item.Price,
OrderStatus = true,
OrderNumber = item.UserId + DateTime.Now.ToShortTimeString(),
AdminStatus = item.AdminStatus,
PaymentStatus = item.PaymentStatus,
OrderDate = DateTime.Today
});
_bbdb.SaveChanges();
}
}
var cartdata = GetCartItems();
return PartialView("_CartTable", cartdata);
}
How can I change this bit code _bbdb.Orders.Add(new Orders to UPDATE instead ADD.
I thought something like _bbdb.Orders.AddOrUpdate(new Orders would do the trick, but I does not exist.
It is adding the right value, but I want to update the records not add new ones.
Any suggestion on how to improve or change will be appreciated.

Not sure how correct I am here, but try this...
_bbdb tracks the changes, so you simply need to get the item from the db, make the changes you need and then save them.
foreach (var item in data)
{
item.Price = 100; //Set whatever values you need to update
}
_bbdb.SaveChanges();
Also, notice that the SaveChanges() method was moved outside of foreach. The context will track the changes so you don't need to call SaveChanges after each item is added.
Finally, I believe that you don't need ToList() after the Where-clause

Related

How do I optimize this SQL query?

The following query gets all the forum posts of a category. And I also need to display the latest comment time for each post. But the query seem to take more time to make because of it. (which makes sense).
But I am not able to figure out how I can optimize it, and I'm not sure where to look for information on this.
Any tips or solutions to my problem would be greatly appreciated.
Query to get the initial posts for the category
var query = context.ctm_Forum_Post.Where(x => x.Deleted == false && x.FK_Categori_ID == Id)
.Select(x => new ForumPostModel()
{
Id = x.Id,
Title = x.Title,
BodyText = x.BodyText,
Summary = x.Summary,
Archieved = x.Archieved,
Created = x.Created,
Deleted = x.Deleted,
MemberID = x.FK_Member_ID,
Sticky = x.Sticky,
Updated = x.Updated,
CategoryId = x.FK_Categori_ID
}).ToList();
foreach (var item in query)
{
item.LatestCommentTime = this.GetNewestCommentDateByPost(item.Id);
}
return query.OrderByDescending(x=> x.Created);
And for each and every post I have to make one more call to the database.
var query = (from comments in context.ctm_Comments
join posts in context.ctm_Forum_Post on comments.Page_ID equals posts.Id
where posts.Id == id && comments.Deleted == false
orderby comments.Reqistration_timestamp descending
select comments.Reqistration_timestamp).FirstOrDefault();
Comments table
public partial class ctm_Comments
{
public int ID { get; set; }
public int Page_ID { get; set; }
public int Member_ID { get; set; }
public string Comment { get; set; }
public Nullable<System.DateTime> Reqistration_timestamp { get; set; }
public bool Deleted { get; set; }
public Nullable<System.Guid> Page_Guid { get; set; }
}
Post table
public partial class ctm_Forum_Post
{
public int Id { get; set; }
public string Title { get; set; }
public string BodyText { get; set; }
public string Summary { get; set; }
public int FK_Categori_ID { get; set; }
public bool Archieved { get; set; }
public bool Deleted { get; set; }
public bool Sticky { get; set; }
public int FK_Member_ID { get; set; }
public System.DateTime Created { get; set; }
public System.DateTime Updated { get; set; }
public virtual ctm_Forum_Category ctm_Forum_Category { get; set; }
}
Are you trying to pull all in one query? Why don't you pull latest 10-20 posts using paging that will make query fast and then use a separate query to pull comments of that post?
And in case you need to pull large data then I would suggest to use a stored procedure as that is optimized with the fast execution plan.
I am not sure why you are using Join to pull comments, you can add Post ID in Comment field and then use a simple query to pull comments of that post without using Joins unless you are not trying to pull other data from Post table..
try this code.
add Reqistration_timestamp property to ForumPostModel
public class ForumPostModel
{
//other property...............................
public Nullable<System.DateTime> Reqistration_timestamp { get; set; }
}
query
var query = context.ctm_Forum_Posts
.Join(context.ctm_Comments,
post => post.Id,
comment => comment.Page_ID,
(post, comment) => new
{
p = post,
c = comment
}
).Where(x => x.p.Deleted == false && x.p.FK_Categori_ID == Id).OrderByDescending(x => x.c.Reqistration_timestamp).Take(1)
.Select(x => new ForumPostModel()
{
Id = x.p.Id,
Title = x.p.Title,
BodyText = x.p.BodyText,
Summary = x.p.Summary,
Archieved = x.p.Archieved,
Created = x.p.Created,
Deleted = x.p.Deleted,
MemberID = x.p.FK_Member_ID,
Sticky = x.p.Sticky,
Updated = x.p.Updated,
CategoryId = x.p.FK_Categori_ID,
LatestCommentTime = this.GetNewestCommentDateByPost(x.p.Id),
Reqistration_timestamp = x.c.Reqistration_timestamp
}).OrderByDescending(x => x.Created).ToList();

Add objects one by one when click button MVC C#

I have added items to the list.But the problem is in here i have added items one by one to the list after button click items.Every time only 1 object shows in the list.
List<Items> _objList = new List<Items>();
public ActionResult AddNewItems(int ItemId)
{
Items items = itemBusiness.GetItemByItemId(ItemId);
_objList.Add(new Items { ItemId =items.ItemId,ItemName =items.ItemName,
ItemPrice = items.ItemPrice });
Session["ItemSession"] = _objList;
return RedirectToAction("Index","Home");
}
Items DTO
public class Items
{
public int ItemId { get; set; }
public string ItemCode { get; set; }
public string ItemName { get; set; }
public string ImageUrl { get; set; }
public int? ItemBadge { get; set; }
public DateTime? AddedDate { get; set; }
public int? AddedBy { get; set; }
public DateTime? UpdatedDate { get; set; }
public int? UpdatedBy { get; set; }
public bool? IsActive { get; set; }
public decimal ItemPrice { get; set; }
}
You need to store the list, then retrieve it to add a new item to it. That create could be done a couple ways including calling a method that creates if it does not exist and returns that session list as in the
var _objList = (List<Items>)Session["ItemSession"];
Just make sure you DO have one so it does not throw an InvalidCastException
This code assumes you DO have a list stored in some "create" method prior to adding another one.
public ActionResult AddNewItems(int ItemId)
{
var _objList = (List<Items>)Session["ItemSession"];
Items items = itemBusiness.GetItemByItemId(ItemId);
_objList.Add(new Items { ItemId = items.ItemId,
ItemName = items.ItemName,
ItemPrice = items.ItemPrice });
Session["ItemSession"] = _objList;
return RedirectToAction("Index","Home");
}
Example alternate, set a property on your class and use that avoiding the cast exception by checking for null and creating if not there yet (and store at end as you have):
ItemsHolder.Add(new Items { ItemId = items.ItemId,
ItemName = items.ItemName,
ItemPrice = items.ItemPrice });
Add to your class
// the property
public List<Items> ItemsHolder
{
get
{
object ItemsSession = Session["ItemSession"] as List<Items>;
if (ItemsSession == null)
{
ItemsSession = new List<Items>();
Session["ItemSession"] = ItemsSession;
}
return (List<Items>)ItemsSession;
}
}
Side note, just make sure your items are serializable. You might just want to store a list of ItemId and get the name and price as needed to avoid some issues. https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/serialization/index

ASP.NET MVC C# set model property values

I am having some problems with setting the values for my summaryVm in the controller method. I am accepting an OrderVM and I have checked in debug mode and the properties of OrderVM are all set when it is passed and the Order also had all values set (including orderID). However later in the method when I try to set the values of my SummaryVm to these values the summaryVm.Tickets is null, as is the summaryVm.orderID. Yet email and eventID are being set fine.
If anyone has suggestions on what I'm doing wrong it would be great because I'm at a loss to see what's wrong.
[HttpPost]
public ActionResult Create(OrderVM model)
{
if (!ModelState.IsValid)
return View("CreateOrder", model);
}
string currentUserId = User.Identity.GetUserId();
ApplicationUser currentUser = db.Users.FirstOrDefault(x => x.Id == currentUserId);
Event currentEvent = db.Events.Find(model.EventID);
Order order = new Order
{
OrderDate = DateTime.Now,
EventID = model.EventID,
Event = currentEvent,
user = currentUser
};
float total = 0;
// Initialise a TicketsOrdered data model for each valid ticket
foreach (TicketVm ticket in model.Tickets.Where(t => t.Quantity > 0))
{
total += (ticket.Price*ticket.Quantity);
TicketsOrdered ticketOrder = new TicketsOrdered
{
OrderID = order.OrderID,
TicketID = ticket.TicketID,
Quantity = ticket.Quantity
};
db.ticketsOrdered.Add(ticketOrder);
}
order.OrderTotal = total;
currentEvent.Order.Add(order);
currentUser.Order.Add(order);
SummaryVm summaryVm = new SummaryVm
{
email = order.user.UserName,
orderID = order.OrderID,
tickets = model.Tickets,
totalPrice = total,
eventID=model.EventID,
stripePublishableKey=ConfigurationManager.AppSettings["stripePublishableKey"]
};
db.Orders.Add(order);
db.SaveChanges();
return RedirectToAction("OrderSummary", "Order", summaryVm);
}
public class OrderVM
{
public int ID { get; set; }
public int EventID { get; set; }
public string EventName { get; set; }
// additional properties of Event you want to display
public DateTime OrderDate { get; set; }
public string FirstName { get; set; }
// additional properties of Order and OrderDetails you want to edit
public IList<TicketVm> Tickets { get; set; }
public Event events {get; set;}
}
public class SummaryVm
{
public IList<TicketVm> tickets { get; set; }
public string email { get; set; }
public float totalPrice { get; set; }
public int orderID { get; set; }
public int eventID { get; set; }
public string stripePublishableKey { get; set; }
public string StripeToken { get; set; }
public string StripeEmail {get;set;}
}
Move SaveChanges before creating SummaryVm
...
db.Orders.Add(order);
db.SaveChanges();
SummaryVm summaryVm = new SummaryVm
{
email = order.user.UserName,
orderID = order.OrderID,
tickets = model.Tickets,
totalPrice = total,
eventID=model.EventID,
stripePublishableKey=ConfigurationManager.AppSettings["stripePublishableKey"]
};
return RedirectToAction("OrderSummary", "Order", summaryVm);
and it will work.

how to manipulate a list of properties in a class when sending one email?

I'm trying to implement order confirmation in application (MVC 4) which uses a shopping cart.
I'm at the stage where I want to send out an email to both the customer and Admin user to confirm the order.
I want to send one email for one order(Cart).
My Cart class.
I have a Cart Model:
namespace MerchandiseProject.Domain.Entities
{
public class Cart
{
private List<CartLine> lineCollection = new List<CartLine>();
public void AddItem(Product product, int quantity)
{
CartLine line = lineCollection
.Where(p => p.Product.ProductID == product.ProductID)
.FirstOrDefault();
if (line == null)
{
lineCollection.Add(new CartLine { Product = product,
Quantity = quantity });
}
else
{
line.Quantity += quantity;
}
}
public void RemoveLine(Product product)
{
lineCollection.RemoveAll(l => l.Product.ProductID == product.ProductID);
}
public decimal ComputeTotalValue()
{
return lineCollection.Sum(e => e.Product.Price * e.Quantity);
}
public void Clear()
{
lineCollection.Clear();
}
public IEnumerable<CartLine> Lines
{
get { return lineCollection; }
}
}
public class CartLine
{
public Product Product { get; set; }
public int Quantity { get; set; }
}
}
I am passing this model and the shipping details model back to POST checkout actionResult in my Cartcontroller and assigning their values to an Orders class, these details are also persisted to the database.
The information being past back to the database are correct.
But how to extract it in the likes of a "foreach" statement has me stuck !
Orders Class:
namespace MerchandiseProject.Domain.Entities
{
public class Orders
{
private List<OrderLine> lineCollection = new List<OrderLine>();
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int OrderId { get; set; }
public DateTime OrderDate { get; set; }
public int ProductID { get; set; }
public string ProductName { get; set; }
public decimal ProductUnitPrice { get; set; }
public int Quantity { get; set; }
public decimal OrderTotal { get; set; }
public string Name { get; set; }
public string UserName { get; set; }
public string Line1 { get; set; }
public string Line2 { get; set; }
public string Line3 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
public string Country { get; set; }
public bool GiftWrap { get; set; }
public IEnumerable<OrderLine> Lines
{
get { return lineCollection; }
}
}
public class OrderLine
{
public Product Product { get; set; }
public int Quantity { get; set; }
}
}
My problem is in my Cart Controller Checkout ViewResult [POST], an email goes out for every type of product in the cart, I want to send one e-mail with the list of products ordered ? make sense ?
Cart Controller:
[HttpPost]
public ViewResult Checkout(Cart cart, ShippingDetails shippingDetails)
{
var CurrentUser = HttpContext.User;
string currentUserName = CurrentUser.Identity.Name;
if (cart.Lines.Count() == 0)
{
ModelState.AddModelError("", "Sorry, your cart is empty!");
}
if (ModelState.IsValid)
{
foreach (var line in cart.Lines)
{
var model = new Orders()
{
ProductName = line.Product.Name,
ProductUnitPrice = line.Product.Price,
Quantity = line.Quantity,
OrderTotal = line.Quantity * line.Product.Price,
UserName = currentUserName,
Name = shippingDetails.Name,
Line1 = shippingDetails.Line1,
Line2 = shippingDetails.Line2,
Line3 = shippingDetails.Line3,
City = shippingDetails.City,
State = shippingDetails.State,
Country = shippingDetails.Country,
GiftWrap = shippingDetails.GiftWrap,
OrderDate = DateTime.Now,
Zip = shippingDetails.Zip,
};
new MailController(_actionMailer).OrderDetailsEmail(model).Deliver();
}
orderProcessor.SaveOrder(currentUserName ,cart, shippingDetails);
cart.Clear();
return View("Completed");
}
else
{
return View(shippingDetails);
}
}
Please note - asp.net MVC 4 using razor views.
As #DStanley said, you are literally saying to send an email for each line item by having your MailController line within the foreach loop. If the problem is you need access to model outside the context of the foreach loop, then you need to persist it in a variable in a parent scope. For example:
var lineItems = new List<Order>();
foreach (var line in cart.Lines)
{
lineItems.Add(new Order
{
...
});
}
However, if you're going to do that, it's more efficient to just use Select:
var lineItems = cart.Lines.Select(m => new Order
{
ProductName = m.Product.Name,
...
});
Removing the need for the foreach completely.

Update Parent and Insert Child Telerik Opn Access with AutoMapper

I am using Telerik Open Access for database operation. I have a Parent Class named Order and it includes 2 classes named OrderHistory and Tasks. Now any kind of event happen like if order is put on hold or put for dispatching it's related entry will be put on OrderHistory table and Order table will be updated. Now I have done following code but it works sometimes but sometimes not. I don't know much about this telerik open access and automapper.
controller call:
OrderDTO updateorder = orderManagement.GetOrdersByOrderId(2);
updateorder.QueueId = 3;
updateorder.IsLocked = false;
updateorder.UpdatedBy = Convert.ToInt32(Session["UserId"], CultureInfo.CurrentCulture);
updateorder.UpdatedDate = DateTime.Now;
OrderHistoryDTO alertDto = new OrderHistoryDTO()
{
Event = 'Putting On Hold',
OrderID = orderDTO.Id
UserID = Convert.ToInt32(Session["UserId"], CultureInfo.CurrentCulture),
OccuerdDate = DateTime.Now,
EventType = 'Event'
};
updateorder.OrderHistories.Clear();
updateorder.OrderHistories.Add(alertDto);
updateorder = orderManagement.UpdateOrder(updateorder);
db operations
public OrderDTO UpdateOrder(OrderDTO orderEntity)
{
AutoMapper.Mapper.CreateMap<OrderDTO, Order>()
.ForMember(d => d.Tasks, m => m.Ignore())
.ForMember(d => d.OrderHistories, m => m.MapFrom(s => s.OrderHistories));
AutoMapper.Mapper.CreateMap<OrderHistoryDTO, OrderHistory>();
var orderBase = AutoMapper.Mapper.Map<Order>(orderEntity); // It will sometimes make OrderHistories list count to 0. though in starting orderEntity has OrderHistories count = 1.
base.Update(orderBase);
base.Save();
orderEntity = AutoMapper.Mapper.Map<OrderDTO>(orderBase);
return orderEntity;
}
OrderDTO
public class OrderDTO
{
public int OrderID { get; set; }
public bool? IsLocked { get; set; }
public int QueueId { get; set; }
[ScriptIgnore]
private IList<OrderHistoryDTO> _orderHistories = new List<OrderHistoryDTO>();
[ScriptIgnore]
public virtual IList<OrderHistoryDTO> OrderHistories
{
get { return this._orderHistories; }
}
public DateTime? UpdatedDate { get; set; }
public int? UpdatedBy { get; set; }
}
OrderHistoryDTO
public class OrderHistoryDTO
{
public int ID { get; set; }
public int UserID { get; set; }
public int OrderID { get; set; }
public string Event { get; set; }
public string EventType { get; set; }
public DateTime? OccuerdDate { get; set; }
public UserDTO User { get; set; }
}

Categories

Resources