I have list of class which class has 3 properties like this.
public string attributeName { get; set; }
public string strFormId { get; set; }
public string strValue { get; set; }
I am adding my database data into it through list like this
List<myAttributeData> attributesData = new List<myAttributeData>();
var result = db.ExecuteQuery<myAttributeData>(query, new object[0]);
// attributesData.Clear();
foreach (myAttributeData item in result.ToList())
{
if (item.attributeName == "Province ")
{
var Loc = from d in db.tblLocations
where d.LocationId == Convert.ToInt32(item.strValue)
select new
{
d.LocationName
};
foreach (var item1 in Loc.ToList())
{
attributesData.Add(new myAttributeData()
{
attributeName = item.attributeName,
strFormId = item.strFormId,
strValue = item1.LocationName
});
}
}
The problem is its taking so much time right now i have 70 thousands record into my database table which is take more than half hour any suggestion about this situation thanks. I have to add my data into list because I need it to use it back when it is needed can anybody give me solutions to cut the time of adding data into string.
One word: caching.
The problem with this code is that you're iterating through your 70,000 records, and for each one, you're going back to the database, to read in extra information.
foreach (myAttributeData item in result.ToList())
{
if (item.attributeName == "Province ")
{
var Loc = from d in db.tblLocations
where d.LocationId == Convert.ToInt32(item.strValue)
You'll find your code flies if you can cache the list of locations (before calling your foreach loop)
List<Location> cachedLocations = db.tblLocations.ToList();
..and then set your Loc variable from there:
var Loc = from d in cachedLocations
where d.LocationId == Convert.ToInt32(item.strValue)
Always keep the number of calls back to the database as low as possible.
Good luck!
Related
Just started learning Dapper. I have an ADO.NET background. Using a demo I downloaded, I can insert/delete data from a webform into a MySql table just fine. This, however, I have searched all morning on.
In retrieving a single row from the db by ID, it doesn't return a LIST<>, it seems to be just an object (using code from the demo I downloaded). The query works, I get the object back. It has the fields: "ProductID, Description and Price".
The only way I could get the values to those three fields was like this:
System.Reflection.PropertyInfo pi = Product.GetType().GetProperty("ProductID");
System.Reflection.PropertyInfo desc = Product.GetType().GetProperty("Description");
System.Reflection.PropertyInfo price = Product.GetType().GetProperty("Price");
int _ProductID = (int)(pi.GetValue(Product, null));
string _Description = (string)(desc.GetValue(Product, null));
decimal _Price = (decimal)(price.GetValue(Product, null));
This works and gets the correct values for the three fields.
I'm used to looping through DataTables, but I just think there is probably a better way to get those values.
Is this the correct way to do this or am I missing something? I did actually read documentation and mess with this all morning before asking, too.
Some of the things I looked at seem to be very complex. I thought Dapper was supposed to simplify things.
OK, Thanks Marc. It was difficult for me to see what was supposed to be in the Dapper class files and what was supposed to be in my code behind. The original demo way of getting a product by ID had the query as .FirstOrDefault();
I changed everything to return a List<> and it all worked. I'm sure my ADO.NET is showing, but this works. In Dapper class files:
public List<Product> ProductAsList(int Id)
{
return this._db.Query<Product>("SELECT * FROM Cart_product WHERE ProductID=#Id", new { Id = Id }).**ToList()**;
}
This is just getting one row that matched the ProductID.
In page codebehind:
protected void CartItemAdd(string ProductId) // passing it the selected ProductID
{
var results = cartservice.ProductAsList(Convert.ToInt32(ProductId));
// returns that one row using Dapper ProductAsList(ProductId)
int _ProductId = 0;
string Description = string.Empty;
decimal Price = 0;
// Loop through the list and get the value of each item:
foreach (Product obj in results)
{
_ProductId = obj.ProductID;
Description = obj.Description;
Price = obj.Price;
}
// Using Dapper to insert the selected product into the shopping cart (table):
String UserName = "jbanks";
cartitem = new CartItem();
cartitem.ProductID = _ProductId;
cartitem.Quantity = 1;
cartitem.Description = Description;
cartitem.Price = Price;
cartitem.Created = DateTime.Now;
cartitem.CreatedBy = UserName;
result = cartservice.AddCartItem(cartitem);
if (result)
{
lblMessage.Text = string.Empty;
lblMessage.Text = "Successfully added a cart item";
}
}
}
It does indeed look up the product from one table and insert a selected item into another table.
Thanks again!
The main Query<T> API returns an IEnumerable<T>, which often will be a List<T>; the AsList<T>() extension method can get it back to a list without a copy, but either way: they are just T, for whatever T you asked for. If you asked for Query<Product>, then: they should be Product instances:
var results = connection.Query<Product>(someSql, someArgs); // perhaps .AsList()
foreach (Product obj in results) { // "var obj" would be fine here too
// now just use obj.ProductID, obj.Description and obj.Price
}
If that didn't work: check that you used the <T> version of Query. There is a non-generic variant too, which returns dynamic. Frankly, you should almost always use the <T> version.
Note: I'm assuming that somewhere you have something like
class Product {
public int ProductID {get;set;}
public string Description {get;set;}
public decimal Price {get;set;}
}
I am fairly new to using LINQ and are now trying to build a LINQ question I do not quite manage to solve.
I would like to ask a question to a database, where I want to bring back single rows from a few tables, but a list of rows from other tables.
See code below too see what I am trying to do:
public DB.store store { get; set; }
public List<DB.gallery_image> images { get; set; }
public List<DB.product> products { get; set; }
public static List<Store> getStoreInfo()
{
DBDataContext db = new DBDataContext();
var _dataToGet = from _store in db.stores
select new Store
{
store = _store,
images = (from a in db.gallery_images
where a.albumID == _store.storeID
select a).ToList(),
products = (from p in db.products
where p.storeID = _store.storeID).ToList()
};
return _dataToGet.ToList();
}
So I just want one row from "store" table, but a list from "images" and "product" tables.
The code above works fine, but is slow as hell.
I don't have any problems to select data from multiple tables as long as there is only one (or none) row per table, but when it is a list I'm having problem...
If I were you I would use deferred execution rather than materializing the queries with a call to ToList. I would change the data type of images and products to IEnumerable<> instead of List<>. Then I would not call ToList in the sub-queries because this results in a roundtrip to the database, hence, depending on how many stores you have it could turn into an extremely slow query.
You should see a performance gain here...
public DB.store store { get; set; }
public IEnumerable<DB.gallery_image> images { get; set; }
public IEnumerable<DB.product> products { get; set; }
public static List<Store> getStoreInfo()
{
DBDataContext db = new DBDataContext();
var _dataToGet = from _store in db.stores
select new Store
{
store = _store,
images = (from a in db.gallery_images
where a.albumID == _store.storeID
select a),
products = (from p in db.products
where p.storeID = _store.storeID)
};
return _dataToGet.ToList();
}
We have a table with 50 rows and want display initially only 10. Just below the table we'd like to add a "Show all" link (not a button) that allow, dynamically (or after reloading the page), to display of all the 50 rows.
What is the most straightforward way to achieve this?
Thanks.
Consider having a model composed of an int and a List<DataRow>:
public class MyDisplayViewModel
{
public int FullCount { get; set; }
public List<DataRow> Data { get; set; }
}
When populating your Data object (in your controller, for example) you can take the full count of your existing data and pass it in the FullCount property.
public ActionResult GetData(int count)
{
MyDisplayViewModel model = new MyDisplayViewModel();
//pseudocode for getting the total number of records
model.FullCount = yourDataProvider(yourDataType).Count();
//pseudocode for getting the the list of records
model.Data = yourDataProvider(yourDataType, count);
return View(model);
}
You can check then in your view if the data passed to it corresponds to all the "available" data. If not, show the link to get all the data from your database.
#{
foreach(var row in Model.Data)
{
//display your data
}
if (Model.Data.Count != Model.FullCount)
{
#Html.ActionLink("Load all", "GetData", "YourControllerName",
new { count = Model.FullCount }, null)
}
}
Of course, this suggestion is at the conceptual level. the view, for example, can implement an Ajax request, in order to get only the remaining DataRows.
I have in DB table Called Students We make some thing like lottery to know which students are going to be the winner in some thing like game in a school
public static List<Students> GetAll()
{
List<Students> list = new List<Students>();
var query = from c in new HsInternetDBDataContext().Students
orderby c.DateCreated ascending
select c;
foreach (DAL.Student dr in query.ToList())
{
l.Add(new Quotes(dr));
}
return list;
}
what i want to do is to retrieve random student from this table
the same question are posted in another blog and i quote the answer when i post it :
public static Quotes GetRandomStudents()
{
HsInternetDBDataContext db = new HsInternetDBDataContext();
var query = from c in db.Students select c;
int count = query.Count();
if (query.Count() > 0)
{
Random r = new Random();
return new Students(query.ToList()[r.Next(0, count)]);
}
else
{
return new Students();
}
}
Hope that helps because it works with another one has the same situation
I'm stumped by this easy data problem.
I'm using the Entity framework and have a database of products. My results page returns a paginated list of these products. Right now my results are ordered by the number of sales of each product, so my code looks like this:
return Products.OrderByDescending(u => u.Sales.Count());
This returns an IQueryable dataset of my entities, sorted by the number of sales.
I want my results page to show the rank of each product (in the dataset). My results should look like this:
Page #1
1. Bananas
2. Apples
3. Coffee
Page #2
4. Cookies
5. Ice Cream
6. Lettuce
I'm expecting that I just want to add a column in my results using the SQL ROW_NUMBER variable...but I don't know how to add this column to my results datatable.
My resulting page does contain a foreach loop, but since I'm using a paginated set I'm guessing using that number to fake a ranking number would NOT be the best approach.
So my question is, how do I add a ROW_NUMBER column to my query results in this case?
Use the indexed overload of Select:
var start = page * rowsPerPage;
Products.OrderByDescending(u => u.Sales.Count())
.Skip(start)
.Take(rowsPerPage)
.AsEnumerable()
.Select((u, index) => new { Product = u, Index = index + start });
Actually using OrderBy and then Skip + Take generates ROW_NUMBER in EF 4.5 (you can check with SQL Profiler).
I was searching for a way to do the same thing you are asking for and I was able to get what I need through a simplification of Craig's answer:
var start = page * rowsPerPage;
Products.OrderByDescending(u => u.Sales.Count())
.Skip(start)
.Take(rowsPerPage)
.ToList();
By the way, the generated SQL uses ROW_NUMBER > start and TOP rowsPerPage.
Try this
var x = Products.OrderByDecending(u => u.Sales.Count());
var y = x.ToList();
for(int i = 0; i < y.Count; i++) {
int myNumber = i; // this is your order number
}
As long as the list stays in the same order, which should happen unless the sales number changes. You could be able to get an accurate count;
There is also this way of doing it.
var page = 2;
var count = 10;
var startIndex = page * count;
var x = Products.OrderByDecending(u => u.Sales.Count());
var y = x.Skip(startIndex).Take(count);
This gives the start index for the page, plus it gives you a small set of sales to display on the page. You just start the counting on your website at startIndex.
Here is a long winded answer. First create a class to house the number/item pair like so:
public class NumberedItem<T>
{
public readonly int Number;
public readonly T Item;
public NumberedItem(int number, T item)
{
Item = item;
Number = number;
}
}
Next comes an abstraction around a page of items (numbered or not):
class PageOf<T> : IEnumerable<T>
{
private readonly int startsAt;
private IEnumerable<T> items;
public PageOf(int startsAt, IEnumerable<T> items)
{
this.startsAt = startsAt;
this.items = items;
}
public IEnumerable<NumberedItem<T>> NumberedItems
{
get
{
int index = 0;
foreach (var item in items)
yield return new NumberedItem<T>(startsAt + index++, item);
yield break;
}
}
public IEnumerator<T> GetEnumerator()
{
foreach (var item in items)
yield return item;
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
Once you have that you can "Paginate" a particular queryable collection using this:
class PaginatedQueryable<T>
{
private readonly int PageSize;
private readonly IQueryable<T> Source;
public PaginatedQueryable(int PageSize, IQueryable<T> Source)
{
this.PageSize = PageSize;
this.Source = Source;
}
public PageOf<T> Page(int pageNum)
{
var start = (pageNum - 1) * PageSize;
return new PageOf<T>(start + 1, Source.Skip(start).Take(PageSize));
}
}
And finally a nice extension method to cover the ugly:
static class PaginationExtension
{
public static PaginatedQueryable<T> InPagesOf<T>(this IQueryable<T> target, int PageSize)
{
return new PaginatedQueryable<T>(PageSize, target);
}
}
Which means you can now do this:
var products = Products.OrderByDescending(u => u.Sales.Count()).InPagesOf(20).Page(1);
foreach (var product in products.NumberedItems)
{
Console.WriteLine("{0} {1}", product.Number, product.Item);
}