I'm building an auction site and the user can bid on the same item more than once (obviously). In the user's dashboard, a user can view his bids. When the user bids on the same item more than once, I want only one entry with the highest bid value to show up. My current code shows an entry for each bid. I tried a few things but I couldn't figure it out. Here's what I've got:
public class Bid
{
public int Id { get; set; }
public double Amount { get; set; }
public DateTime Date { get; set; }
public virtual Item Item { get; set; }
public virtual User User { get; set; }
}
protected override List<ItemForUserBids> ResolveCore(User source)
{
var items = new List<ItemForUserBids>();
var userBids = source.Bids;
foreach (var bid in userBids)
{
var item = bid.Item;
var c = new ItemForUserBids
{
BidValue = bid.Amount,
BidId = bid.Id,
Description = item.Description,
Id = item.Id,
ItemThumb = item.MainImageLink(),
Status = _itemsService.GetBiddingStatus(item, source),
TimeLeft = item.EndDate.TimeLeft(),
Title = item.Title
};
items.Add(c);
}
return items;
}
I tried to get Distinct bids based on the Item.Id but that did not work. Now I'm thinking maybe I could use the Date property of the Bid entity somehow to get to the result I want but my head stopped thinking.
Any suggestions?
UPDATE:
I got it to work using a dictionary and using OrderBy() and Max() like many suggested. But I think the latter could be further improved.
Implementation using a dictionary (works):
var userBids = new Dictionary<string, Bid>();
foreach (var bid in allUserBids)
{
var key = bid.Item.Id.ToString();
if(userBids.ContainsKey(key))
{
if (userBids[key].Amount < bid.Amount)
userBids[key] = bid;
}
userBids[key] = bid;
}
Attempt using the other method (works):
var highestBids =
source.Bids.Where(x => x.Date > DateTime.Now.AddYears(-1))
.GroupBy(x => x.Item.Id,
(itemId, bids) =>
new
{
ItemId = itemId,
MaxBid = bids.Max(x => x.Amount)
}).ToList();
var userBids = new List<Bid>();
foreach (var bid in source.Bids)
{
for(var i = 0; i < highestBids.Count; i++)
{
var curr = highestBids[i];
if (bid.Item.Id.Equals(curr.ItemId) && bid.Amount.Equals(curr.MaxBid)) {
userBids.Add(bid);
highestBids.Remove(curr);
}
}
}
How do I get rid of those loops? And maybe have it all in one chained statement?
The comments posted so far should be a good indication that you should look into re-architecting this a little, but the immediate code solution involves using System.Linq to chain together a GroupBy, Max, and a Select.
you could simply create a dictionary of user bids, where the key is the item id. Then for each user bid if an item id is not already used then add the current bid to the dictionary, if it is used then see if the bid amount of the item that exists in the dictionary already is lower than the current item then replace the existing item in the dictionary with the current one.
However this is very inefficient as really you only want to load the top 1 bid sorted descending by bid amount for each bid id, not load all the bids then work out the highest. What happens if your user has 10,000 old bids? Do they all get loaded?
Related
How to check if there exists an entry in the database? Debugging shows that the result returns null.
TimeSlots a Collection. I'm not sure if I've correctly done it.
Here is my context:
var result = await context.Bookings
.SingleOrDefaultAsync(b =>
b.BookDate == booking.BookDate
&& b.TimeSlots == booking.TimeSlots
&& b.RoomId == booking.RoomId);
public class Booking
{
public int Id { get; set; }
[Required]
public DateTime BookDate { get; set; }
[Required]
public int RoomId { get; set; }
public Room Room { get; set; }
[Required]
public ICollection<BookingTimeSlot> TimeSlots { get; set; }
[Required]
public ICollection<BookingModule> Modules { get; set; }
public Booking()
{
TimeSlots = new Collection<BookingTimeSlot>();
Modules = new Collection<BookingModule>();
}
}
public class BookingTimeSlot
{
public int BookingId { get; set; }
public int TimeSlotId { get; set; }
public Booking Booking { get; set; }
public TimeSlot TimeSlot { get; set; }
}
This is the input I'm trying to make:
{
"RoomId": 1,
"BookDate": "2020-10-27",
"TimeSlots": [1, 3],
"Modules": [1]
}
Your question is not clear whether you are looking to find duplicates or get the first result.
Second I think you are missing the primary key in your model or are you using a composite key, either way its good read to help you fix that.
//With Linq and EF find and process duplicates
// assuming Id is your primary key - [please fix this, some info for you][1]
var duplicateBookings = context.Bookings.GroupBy(i => i.id)
.Where(x => x.Count() > 1)
.Select(val => val.Key); // or .SelectMany(i => i.ToList());
// do what you need
foreach(var dupes in duplicateBookings )
{
//process or do what you need
context.Bookings.DeleteObject(dupes); // for e.g. delete duplicate bookings
}
If you just want the first result, then change the sing to first
var result = await context.Bookings
.FirstOrDefaultAsync(b => //first result
b.BookDate == booking.BookDate
&& b.TimeSlots == booking.TimeSlots
&& b.RoomId == booking.RoomId);
Well, for the most part referential integrity in the database does not solve such problems, and in the vast majority of cases RI is not used anyway. The reason is that's not the job of RI but worse such RI violations tend to occur FAR TOO late for a user interface that informs the user that such a booking cannot be done. In other words you don't try to make the booking, keep fingers crossed, and the hope the data can be written out.
WHAT you do is provide a UI that when the user selects a booking date, you give feedback that such a booking can't be made, and as such NO DATABASE writes or updates will have YET occurred - hence this is a UI issue, not really a database RI issue. And even if it was a database RI issue, you would have to attempt to write the data, and often the user is JUST checking and asking for a particular booking date - not necessary ready to actually book.
a booking collision can be found based on this logic:
RequestStartDate <= EndDate
and
RequestEndDate >= StartDate
So any overlap or even a full bracketing will be found with the above simple query.
So, with above? Then with a room number and list of booking date ranges for that room, then a collision would be found with this:
#dtRequestStartDate = "Enter start Date"
#dtRequestEndDate = "Enter end date"
#RoomNum = Room number
strSQL = SELECT * from tblBookings where
(#dtRequestStartDate <= RoomEndDate)
AND
(#dtRequestEndDate >= RoomStartDate)
AND
(#RoomNum = RoomNumber)
If above row.Count > 0 then
message = Sorry, you cannot book that room
So what you do is check before a booking, and if above returns rows, then you don't allow the booking. As long as you never allow overlaps for bookings, then the above SIMPLE logic will always work and always prevent a booking with collisions.
I have found a solution to my problem. I compare the TimeSlotId using Intersect, if there are intersection, it then return true.
public bool BookingExist(Booking booking)
{
var resultContext = context.Bookings
.Where(b => b.Room.Id == booking.RoomId && b.BookDate == booking.BookDate)
.SelectMany(b => b.TimeSlots.Select(bt => bt.TimeSlotId))
.AsEnumerable();
var resultInput = booking.TimeSlots.Select(bt => bt.TimeSlotId);
if (resultContext.Intersect(resultInput).Count() > 0)
return true;
else
return false;
}
I have problem with advanced filtering data using LINQ.
I'd like to get list of Plan classes with Details list where Arguments in Items Lists contains specific characters. Also the Items list should contains only this filtered elements.
My classes look like below:
class Plan
{
public string Name { get; set; }
public List<Detail> Details { get; set; }
public Plan()
{
Details = new List<Detail>();
}
}
class Detail
{
public string Setting { get; set; }
public List<Item> Items { get; set; }
public Detail()
{
Items = new List<Item>();
}
}
class Item
{
public string Arguments { get; set; }
}
My current solution look like this, but I think it isn't the best option. I tried to write this code using Where and Any, but I've got Plans list where Items contains all items.
var filteredPlans = plans.Select(x =>
new Plan
{
Name = x.Name,
Details = x.Details.Select(y =>
new Detail
{
Setting = y.Setting,
Items = y.Items.Where(c => c.Arguments.Contains("...")).Select(z =>
new Item
{
Arguments = z.Arguments
}).ToList()
}).ToList()
});
How can I write this code using WHERE statement or What is the best solution to do that?
Also how can I get harvest difference using LINQ EXPECT based on Items List? e.g. plans: contains all plans with items, plans2: contains all plans with filtered items, and the plans3 should contains all plans with items which not belong to plans2.
Does this work for you?
First I limit to only the plans where any of their details contain any item that matches the filter.
Then I limit details for each plan to only those with any item that matches the filter
Then I limit items for each plan
private List<Plan> FilteredPlans(List<Plan> plans, string filter)
{
List<Plan> filteredPlans = plans.Where(plan => plan.Details.Any(detail => detail.Items.Any(item => item.Arguments.Contains(filter)))).ToList();
foreach (var plan in filteredPlans)
{
plan.Details = plan.Details.Where(detail => detail.Items.Any(item => item.Arguments.Contains(filter))).ToList();
foreach (var detail in plan.Details)
{
detail.Items = detail.Items.Where(item => item.Arguments.Contains(filter)).ToList();
}
}
return filteredPlans;
}
Also, here's another version as a single statement, but I think it's far less readable. I essentially limit the items first and then work my way backwards only keeping containers that aren't empty
private List<Plan> FilteredPlansWithSelect(List<Plan> plans, string filter)
{
List<Plan> filteredPlans = plans.Select(plan =>
new Plan()
{
Name = plan.Name,
Details = plan.Details.Select(detail =>
new Detail()
{
Setting = detail.Setting,
Items = detail.Items.Where(item => item.Arguments.Contains(filter)).ToList()
}).Where(detail => detail.Items.Count > 0).ToList()
}).Where(plan => plan.Details.Count > 0).ToList();
return filteredPlans;
}
Edited for grammer
I have a SQL table like this:
DepartmentID is parent of department. I've build a tree by this table(in ASP.net (C#) project):
Records in tree above is:
I need to get parents in this tree.
I can do it in SQL Server like this(for Example id=2, id is input argument):
with cte1
as
(
select id,name,DepartmentID, 0 AS level
from Department
where id =2
union all
select Department.ID,Department.name,Department.DepartmentID, level+1
from Department
inner join cte1 on Department.ID=cte1.DepartmentID
)
select * from cte1
Output(id=2 (A))
Output(id=4 (A1))
I know EF does not support cte, but I need to get this result in EF.
It would be very helpful if someone could explain solution for this problem.
These posts are similar to your question.please see these:
writing-recursive-cte-using-entity-framework-fluent-syntax-or-inline-syntax
converting-sql-statement-that-contains-with-cte-to-linq
I think there is no way to write a single LINQ to SQL query that could get all However, LINQ supports a method to execute a query (strangly enough called DataContext.ExecuteQuery). Looks like you can use that to call a arbitrary piece of SQL and map it back to LINQ.
See this post:
common-table-expression-in-entityframework
The easiest way I can think of is to map the relationship in EF and then retrieve all departments and then get the root parent from that list. All of them should be loaded in memory and EF will take care of the tree structure with the mapping. Alternatively you can enable lazy loading and just get the parent but then with each child or childset a query will be executed by EF during retrieval.
Model
public class Department
{
public int Id { get; set; }
public string Name { get; set; }
public int? DepartmentId { get; set; }
public Department ParentDepartment { get; set; }
public virtual ICollection<Department> ChildDepartments { get; set; }
}
Mapping (using fluent)
public DbSet<Department> Departments { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// other mapping code
modelBuilder.Entity<Department>()
.HasOptional(x => x.ParentDepartment)
.WithMany(x => x.ChildDepartments)
.HasForeignKey(x => x.DepartmentId);
// other mapping code
}
Eager retrieval of root parent
using (var context = new YourDbContext())
{
var allDepartments = context.Departments.ToList(); // eagerly return everything
var rootDepartment = allDepartments.Single(x => x.DepartmentId == null);
}
Retrieval of only root parent and then use lazy loading, note that the DbContext needs to be available for Lazy Loading to work and it must also be enabled on the DbContext
using (var context = new YourDbContext())
{
var rootDepartment = context.Departments.Single(x => x.DepartmentId == null);
// do other stuff, as soon as context is disposed you cant lazy load anymore
}
Try one of these,
1-
int _ID = 2; // ID criteria
List<object> result = new List<object>(); // we will use this to split parent at child, it is object type because we need Level
var departments = entites.Departments.Where(x => x.ID == _ID).SelectMany(t => entites.Departments.Where(f => f.ID == t.DepartmentID),
(child, parent) => new { departmentID = child.DepartmentID, Name = child.Name, ID = child.ID, level = 0,
Parent = new { DepartmentID = parent.DepartmentID, Name = parent.Name, ID = parent.ID, level = 1 }});
// first we check our ID (we take A from where criteria), then with selectmany T represents the Department A, we need
// department A's departmentID to find its parent, so another where criteria that checks ID == DepartmentID, so we got T and the new list
// basically child from first where parent from second where, and object created.
// for showing the results
foreach (var item in departments)
{
result.Add(new { DepartmentID = item.departmentID,ID = item.ID, level= item.level,Name = item.Name}); // child added to list
result.Add(new { DepartmentID = item.Parent.DepartmentID, ID = item.Parent.ID, level = item.Parent.level, Name = item.Parent.Name }); // parent added to list
}
Result;
2-
List<object> childParent = new List<object>();
// basically get the child first
Departments child1 = entites.Departments.Where(x => x.ID == _ID).FirstOrDefault();
// find parent with child object
Departments parent1 = entites.Departments.Where(x => x.ID == child1.DepartmentID).FirstOrDefault();
// create child object with level
childParent.Add(new { child1.DepartmentID, child1.ID,child1.Name , level = 0});
// create parent object with level
childParent.Add(new { parent1.DepartmentID,parent1.ID,parent1.Name, level = 1 });
Result (not the same image, check column Header Text);
Edit 1:
3-
Another way, by giving ID as input and assuming that ID column is unique, so there will be always 2 values at the array and by returning list, the index of items actually represent their levels. (won't add results because they are same :)).Btw you can also use Union instead of Concat.
var ress = list.Where(x=> x.ID ==2)
.SelectMany(x=> list.Where(c=> c.ID == x.ID).Concat(list.Where(s => s.ID == x.DepartmentID))).ToList();
DataTable dt = new DataTable();
dt.Columns.Add("DepartmentID");
dt.Columns.Add("ID");
dt.Columns.Add("Name");
dt.Columns.Add("Level");
for (int i = 0; i < ress.Count(); i++)
{
dt.Rows.Add(ress[i].DepartmentID, ress[i].ID, ress[i].Name, i);
}
dataGridView1.DataSource = dt;
Edit 2
There is not cte in linq, basically using view,sp is the first choise but here is a solution, it might be a little push. Anyway it gives the result.
List<Departments> childParent = new List<Departments>();
// or basically get the child first
Departments child1 = entites.Departments.Where(x => x.ID == 7).FirstOrDefault();
// find parent with child object
Departments parent1 = entites.Departments.Where(x => x.ID == child1.DepartmentID).FirstOrDefault();
// create child object with level
Departments dep = new Departments(); // I add to department class a string level field
dep.DepartmentID = child1.DepartmentID;
dep.ID = child1.ID;
dep.Name = child1.Name;
dep.level = 0; // first item
childParent.Add(dep);
// create parent object with level
dep = new Departments();
dep.DepartmentID = parent1.DepartmentID;
dep.ID = parent1.ID;
dep.Name = parent1.Name;
dep.level = 1; // parent one
childParent.Add(dep);
while (childParent.Select(t => t.DepartmentID).Last() != null) // after added to list now we always check the last one if it's departmentID is null, if null we need to stop searching list for another parent
{
int? lastDepID = childParent.Last().DepartmentID; // get last departmentID
Departments tempDep = entites.Departments.Single(x => x.ID == lastDepID); // find as object
tempDep.level = childParent.Last().level + 1; // increase last level
childParent.Add(tempDep); // add to list
}
(Added another C1 to check 4th level)
Hope helps,
Below is the simple console project Program class code.
You can check with different IDs for the input parameter of the GetParentSet method.
class Program
{
static void Main(string[] args)
{
Program p = new Program();
var result= p.GetParentSet(6);
foreach(var a in result)
{
Console.WriteLine(string.Format("{0} {1} {2}",a.ID,a.Name,a.DepartmentId));
}
Console.Read();
}
private List<Department> GetParentSet(int id)
{
List<Department> result = new List<Department>(); //Result set
using (RamzDBEntities context = new RamzDBEntities())
{
var nodeList = context.Departments.Where(t=>t.ID<=id).ToList(); //Get All the the entries where ID is below or greater than the given to the list
var item = nodeList.Where(a => a.ID == id).SingleOrDefault(); //Get the default item for the given ID
result.Add(item); //Add it to the list. This will be the leaf of the tree
int size = nodeList.Count(); //Get the nodes count
for (int i = size; i >= 1;i--)
{
var newItem= nodeList.Where(j => j.ID == item.DepartmentId).SingleOrDefault(); //Get the immediate parent. This can be done by matching the leaf Department ID against the parent ID
if (item!=null && !result.Contains(newItem)) //If the selcted immediate parent item is not null and it is not alreday in the list
{
result.Add(newItem); //Add immediate parent item to the list
}
if (newItem.ID == 1) //If the immediate parent item ID is 1 that means we have reached the root of the tree and no need to iterate any more.
break;
item = newItem; //If the immediate parent item ID is not 1 that means there are more iterations. Se the immediate parent as the leaf and continue the loop to find its parent
}
}
return result; //return the result set
}
}
Code itself is self-explanatory. However below is the explanation. Hope this will help!
First all the entries with ID below or equal to the given ID is
assigned to a List
Then get the leaf of the tree and add it to the list named result. This is the first element of our result set
We iterate through the retrieved entries descending order. Get the immediate parent of the leaf by equating parent's ID to leaf's department ID
If this immediate parent is not null and its not already in the list add it to the list.
Make the immediate parent item as the leaf and continue the loop so that we can get the parent of the immediate parent.
continue this until we reach the root of the tree.
If the immediate parent ID is=1 that means we have reached the root of the tree and we can break the loop.
Since you generated the edmx, you have the code generated for your DbContext and for your Model Classes including Departments like on this screenshot.
You shouldn't modify them because they might (will) get overwritten by EF tools anyway on any model manipulation. Fortunately both classes are generated as partial so the creators thought about people wanting to customize it safely.
Example below is made for simplicity of implementation not for top performance. I assumed that the table containing Departments is not enormously big and the levels of nesting in hierarchy are not enormously deep.
Create a new Class (*.cs file) in your project and extend your auto-generated Departments class by your custom method or property:
using System;
using System.Collections.Generic;
using System.Linq;
namespace CustomEF.EFStuff
{
public partial class Departments
{
public List<Departments> Hierarchy {
get {
List<Departments> retVal = new List<Departments>();
retVal.Add(this);
using (YourAutoGeneratedContext ctx = new YourAutoGeneratedContext())
{
Departments tmp = this;
while(tmp.DepartmentID != null)
{
tmp = ctx.Departments.First(d => d.ID == tmp.DepartmentID);
retVal.Add(tmp);
}
}
return retVal;
}
private set { }
}
}
}
When you extend the partial class, make sure that you put it in the same namespace. In my case I named my project CustomEF and I've placed the edmx file in the EFStuff subfolder so the generator placed the auto generated class in the CustomEF.EFStuff namespace.
The example above will allow you to get the hierarchy for any Departments object e.g.
int level = 0;
foreach(Departments d in someDepartmentObject.Hierarchy)
{
Console.WriteLine(d.ID.ToString() + ", " + d.DepartmentID.ToString() + ", " + d.Name +", " +(level++).ToString());
}
If you also need to get the hierarchy from some code where you have an ID but not the object, you can additionally create another class (*.cs file) where you'll extend the auto-generated context.
using System.Collections.Generic;
using System.Linq;
namespace CustomEF.EFStuff
{
public partial class YourAutoGeneratedContext
{
public List<Departments> GetDepartmentHierarchy(int departmentId)
{
Departments mydep = this.Departments.FirstOrDefault(d => d.ID == departmentId);
if (mydep == null)
{
throw new System.Data.Entity.Core.ObjectNotFoundException("There is no department with ID = " + departmentId.ToString());
}
return mydep.Hierarchy;
}
}
}
Or in this case you might want to move the implementation to the Context class entirely, without extending the Departments class at all (and you wouldn't have to create an additional instance of your context, you'll have the this to use).
using System.Collections.Generic;
using System.Linq;
namespace CustomEF.EFStuff
{
public partial class YourAutoGeneratedContext
{
public List<Departments> GetDepartmentHierarchy(int departmentId)
{
Departments tmp = this.Departments.FirstOrDefault(d => d.ID == departmentId);
if (tmp == null)
{
throw new System.Data.Entity.Core.ObjectNotFoundException("There is no department with ID = " + departmentId.ToString());
}
List<Departments> retVal = new List<Departments>();
retVal.Add(tmp);
while (tmp.DepartmentID != null)
{
tmp = this.Departments.First(d => d.ID == tmp.DepartmentID);
retVal.Add(tmp);
}
return retVal;
}
}
}
As another unsophisticated use example:
YourAutoGeneratedContext ctx = new YourAutoGeneratedContext();
level = 0;
foreach (Departments currentHier in ctx.GetDepartmentHierarchy(10))
{
Console.WriteLine(currentHier.ID.ToString() + ", " + currentHier.DepartmentID.ToString() + ", " + currentHier.Name + ", " + (level++).ToString());
}
I don't know how much you can trust the data in the database. You might want to implement some checks including cross-referencing departments to prevent infinite loop.
Note that formally the term 'to extend a class' may apply to extension methods rather then to partial classes. I used this word from lack of better one. Extension methods would be something that you might want to use if, for some reason, you'd need your method/property returning EF native DbSet<> instead of the List<>. In such case you might want to take look at: https://shelakel.co.za/entity-framework-repository-pattern/
Example in EF6 to get all parents up to root node.
public class Department
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public int? ParentId { get; set; }
public virtual Department Parent { get; set; }
public virtual ICollection<Department> Children { get; set; }
private IList<Department> allParentsList = new List<Department>();
public IEnumerable<Department> AllParents()
{
var parent = Parent;
while (!(parent is null))
{
allParentsList.Add(parent);
parent = parent.Parent;
}
return allParentsList;
}
}
use include keyword.
_context.Invoices.Include(x => x.Users).Include(x => x.Food).ToList();
I have added 2 products in my basket. in the first step of my Test.
In the last step I assert that same product that were added in the first step of the test comes in the Last step which is the "Order Summary page". Please find below the code and screen shots.
Here there are 2 items, all the features of the 2 items displayed have same classes. just the indexing of the div is different. rest is the same.
I am using the Scenario Context functionality of the Specflow.
Mindwell, i want to achieve like this image , i have code currently for only 1 product, and i want to do the same for multiple products.
1) Basketpage. In this step i take all the elements of the page and take their values in the scenario context.
string productname = pdpPage.getBrandName();
pdpPage.ExpandSideBar();
pdpPage.SelectProductQuantity(Quantity);
var hp = new HeaderPage(driver);
int currentBagQuantity = hp.getBagQuantity();
decimal currentTotalBagPrice = hp.getBagTotalPrice();
ScenarioContext.Current.Add("Product Name",productname);
ScenarioContext.Current.Add("QuantityAdded", int.Parse(Quantity));
ScenarioContext.Current.Add("BagQuantity", currentBagQuantity);
ScenarioContext.Current.Add("CurrentBagPrice", currentTotalBagPrice);
ScenarioContext.Current.Add("ProductPrice", pdpPage.getProductPriceInDecimal());
2) OrderSummary Page. In this step i assert the values , This is the order summary page.
var os = new OrderSummaryPage(driver);
string brandname = os.getOrderProductName();
int quantity = os.getOrderQuantity();
decimal price = os.getOrderPrice();
Assert.IsTrue(brandname.Equals((string)ScenarioContext.Current["Product Name"]), "Err! Product is different!, on pdp is :" + ScenarioContext.Current["Product Name"] + "on order summary is" + brandname);
Assert.IsTrue(quantity.Equals((int)ScenarioContext.Current["QuantityAdded"]), "Err! Quantity is different from ordered!");
Assert.IsTrue(price.Equals((decimal)ScenarioContext.Current["ProductPrice"]), "Err! Product price is appearing to be different!");
Assert.IsTrue(GenericFunctions.isElementPresent(os.Delivery_Address), "Delivery Address details are not present");
Assert.IsTrue(GenericFunctions.isElementPresent(os.Billing_Address), "Billing Address details are not present!!");
I am new to this stuff!! How to loop these and get the dynamic stuff. I want to check and verify each items Product name , price , quantity.
Doing this :
My Step File :
[When(#"I check the items on basket page")]
public void WhenICheckTheItemsOnBasketPage()
{
var bp = new BasketPage(driver);
var h = bp.getLISTItemsFromOrderPage();
for (int i = 0; i <= h.Count; i++)
{
ScenarioContext.Current.Add("item", h[i]);
}
}
BasketPage.cs
public IList getLISTItemsFromOrderPage()
{
List<BasketItems> orderProducts = new List<BasketItems>();
var elements = (driver.FindElements(By.Id("basketitem")));
foreach (IWebElement element in elements)
{
orderProducts.Add(CreateOrderProduct(element));
}
return orderProducts;
}
public BasketItems CreateOrderProduct(IWebElement item)
{
return new BasketItems()
{
BrandName = item.FindElement(By.TagName("a")).Text.Trim(),
Quantity = GenericFunctions.DropDown_GetCurrentValue(item.FindElement(By.TagName("select"))),
Price = Convert.ToDecimal(item.FindElement(By.ClassName("col-md-2")).Text.Substring(1))
};
}
BasketItem.cs
public class BasketItems : BasketPageOR
{
public string BrandName { get; set; }
public string Quantity { get; set; }
public decimal Price { get; set; }
}
Please Help! Thanks in Advance!!
you method os.getOrderProductName(); doesn't make any sense if an order can have multiple products.
You should have a method os.getOrderProducts(); which will return a collection of OrderProduct objects. It should do this by finding all elements which have an id="productorderelement" (although you should not have elements with the same id, you should really use a class for this) and then loop over each element extracting the information to build the OrderProduct soemthing like this should allow you to get the elements with the id:
List<OrderProduct> orderProducts = new List<OrderProduct>();
var elements = (Driver.FindElements(By.XPath("//*[#id=\"productorderelement\"]")))
foreach (element in elements)
{
orderProducts.Add(CreateOrderProduct(element));
}
public class OrderProduct
{
public string BrandName{get;set;}
public int Quantity{get;set;}
public double Price{get;set;}
}
public OrderProduct CreateOrderProduct(IWebElement element)
{
return new OrderProduct()
{
BrandName= element.Something, //you need to extract the appropriate bit of the webelement that holds the brandname, quantity and price, but you don't show us the structure so I can't help there
Quantity= element.FindElement(By.Class("quantity")).Text, //for example
Price= element.GetAttribute("Price") //again another example
}
}
I am trying to use Group By method supported by LINQ.
I have this class
public class Attribute
{
public int Id {get;set;}
public string Name {get;set;}
public string Value {get;set;}
}
I have a service method that will retrive a IList
var attributes = _service.GetAll();
Id Name Value
7 Color Black
7 Color White
220 Size 16
Now I have another tow classes
one is
public class AttributeResourceModelSubItem
{
public string Name {get;set;}
public List<AttributeValueResourceModelSubItem> values { get; set; }
}
public class AttributeValueResourceModelSubItem
{
public int Id;
public string Name {get;set;}
}
I am trying to loop through the attributes list. and if the attribute id is the same, I wanna insert the records where id = to that id inside the AttributeValueResourceModelSubItem in which id = 1 and Name will be equal to the attribute value.
This what I got so far.
private IList<AttributeResourceModelSubItem> FormatAttributes(IList<Attribute> attributes)
{
Dictionary<int, Attribute> baseTypes = new Dictionary<int, Attribute>();
AttributeResourceModelSubItem attributeResourceModelSubItem = null;
var list = new IList<AttributeResourceModelSubItem>();
foreach (var item in attributes)
{
if (!baseTypes.ContainsKey(item.Id))
{
attributeResourceModelSubItem = new AttributeResourceModelSubItem()
attributeResourceModelSubItem.key = item.Name;
attributeResourceModelSubItem.values.Add(new AttributeValueResourceModelSubItem()
{
id = 1,
name = item.Value
});
list.Add(attributeResourceModelSubItem);
}
baseTypes.Add(item.Id, item);
}
return list;
}
Any help is appreciated.
It's pretty unclear from your example what you're actually trying to do, but this is the gist I get.
private IEnumerable<AttributeResourceModelSubItem> FormatAttributes(IEnumerable<Attribute> attributes)
{
return attributes.GroupBy(c => c.Id)
.Select(c => new AttributeResourceModelSubItem()
{
key = c.First().Name,
values = c.Select(x => new AttributeValueResourceModelSubItem()
{
id = 1,
name = x.value
}).ToList();
});
}
You should also definitely not use the word Attribute as a class name. That's already a .NET class.
I'll admit that I don't quite understand the id = 1 part, but I took that from your code. It also seems odd to group by the id then try and take the first name, but again that's what you have.
If you do, in fact, want to group by the name and take the id, which makes a little more sense, you'll want to swap a couple things around. Admittedly this structure still seems a little odd to me, but hopefully this will get you a couple steps closer to your goal.
private IEnumerable<AttributeResourceModelSubItem> FormatAttributes(IEnumerable<Attribute> attributes)
{
return attributes.GroupBy(c => c.name)
.Select(c => new AttributeResourceModelSubItem()
{
key = c.Key,
values = c.Select((item, index) => new AttributeValueResourceModelSubItem()
{
id = index + 1,
name = item.value
}).ToList();
});
}
I also made your id = 1 increment starting at one for each element in each values list. You might want that to be item.Id, or even just your original 1.