I have a stored procedure that is returning data in this format:
HotelID | Price
---------------------
1 | 20
1 | 30
2 | 25
4 | 40
4 | 50
I'm getting the results like so:
ISingleResult<spResult> results = DataContext.sp();
I'd like to get a list of Hotels, based on the data returned from the stored procedure. Something like:
int[] uniqueHotelIds = GetUniqueHotelIdsFromResults(results);
List<Hotel> hotels = (from h in DataContext.Hotels
where uniqueHotelIds.Contains(h.HotelID)
select h).ToList();
I don't have much experience with ISingleResult, but could you do:
int[] uniqueHotelIds = results.Select(x => x.HotelID).Distinct();
You can also iterate through the SingleResult like this:
var myFancyResult;
ISingleResult<spResult> results = DataContext.sp();
foreach (var spResult in results)
{
myFancyResult = spResultin.NameOfColumn;
//Do something else with the data.
}
You can then name the result in the Stored Procedure like this, to give a clear picture:
SELECT ISNULL(#Result, 0) AS Result
ISNULL also ensures you don't have to deal with Nullables in C#.
In case anyone still needs the answer:
NOTE: ISingleResult, as the name implies, will return one result. In the code block below, it maps the resulting SP call to the "resultObject" class. The second line gives an example of the same code with parameters. You'd then want to check and make sure it's not null (depending on your SP).
ISingleResult<spResult> results = DataContext.sp();
var newObject = results.Select(r => new resultObject()).SingleOrDefault();
// or
var newObject = DataContext.sp().Select(r => new resultObject()).SingleOrDefault();
// with parameters
var newObject = DataContext.sp(id, count).Select(r => new resultObject(id, count)).SingleOrDefault();
Related
I'm trying to put together a list (let's call this FinalList) using the combined values of two lists: Customers and Products. Suppose we have four Customers and one Product, FinalList should have a final result of four items (one for each customer).
For example:
Customer List:
Customer Code | Customer Name | Customer Branch ID
------------------|-----------------------|------------------------
001 | Tom | T001
002 | Dick | T002
003 | Harry | T003
004 | Jerry | T004
Product List:
Product Code | Product Name
------------------|---------------------
P001 | Apple
Currently I'm trying to do it this way:
var finalList = new List<ProductDetailDto>();
var customerList = new List<CustomerGroup>();
/// productsList is also type List<ProductDetailDto>();
for (var j = 0; j<= productsList.Count()-1; j++)
{
for (int i = 0; i <= customerList.Count() - 1; i++)
{
var singleDetail = new ProductDetailDto();
// Copy current products to groupDetail
singleDetail = productsList[j];
// Assemble rest of the info
singleDetail.CustCode = customerList[i].Customer.CustomerCode;
singleDetail.CustName = customerList[i].Customer.CustomerName;
singleDetail.CustBranchId = customerList[i].Customer.CustomerBranchId;
finalList.Add(singleDetail);
}
}
return finalList;
After executing this however, finalList only used Jerry as customer for all four items. I tried using foreach as well with the same results. I'm not really sure what I did wrong here, and I'm embarrassed that this seems basic to some, so I'm hoping for a fresh set of eyes to spot what mistake I made here...
Also, is there any way I can further optimize this?
Any help will be greatly appreciated, as always. Thank you.
Here:
// Copy current products to groupDetail
singleDetail = productsList[j];
You don't actually copy current product, but you copy a reference to your item from productsList, and on every inner loop iteration you override properties in the same productsList[j] element.
You may want to read more on how assignment works on reference types:
https://www.microsoftpressstore.com/articles/article.aspx?p=2454676
You need to create a new object if you want to make a cross product of two lists:
var finalList = new List<ProductDetailDto>();
var customerList = new List<CustomerGroup>();
/// productsList is also type List<ProductDetailDto>();
for (var j = 0; j<= productsList.Count()-1; j++)
{
for (int i = 0; i <= customerList.Count() - 1; i++)
{
var singleDetail = new ProductDetailDto
{
ProductCode = productsList[j].ProductCode,
ProductName = productsList[j].ProductName
// and whatever other properties your product have
};
// Assemble rest of the info (these can actually go to object initializer too)
singleDetail.CustCode = customerList[i].Customer.CustomerCode;
singleDetail.CustName = customerList[i].Customer.CustomerName;
singleDetail.CustBranchId = customerList[i].Customer.CustomerBranchId;
finalList.Add(singleDetail);
}
}
return finalList;
As for me, it is confusing that you have properties like CustCode, CustName and CustBranchId in your ProductDetailDto. Are these properties just empty for objects in productsList? Consider creating another class specially for these needs like CustomerProductDto, so that your intention becomes more clear.
You can optimize this using LINQ:
var items = from p in productsList
from c in customerList
select new ProductDetailDto
{
ProductCode = p.ProductCode,
ProductName = p.ProductName
CustCode = c.Customer.CustomerCode,
CustName = c.Customer.CustomerName,
CustBranchId = c.Customer.CustomerBranchId,
};
return items.ToArray();
This line of code:
singleDetail = productsList[j];
affects a pointer and not values, so in the end you have a list of the same pointer so you have only the last modification repeated customerList.Count()
So you must add the values one by one like customerList
I would like to know is if I have an excel file and I am trying to get data via linq query will this be posible?
Excel file format
+-----------------+-------------+
| Inputlocation | Inputvalue |
+-----------------+-------------+
| 1 | Hello |
+-----------------+-------------+
| 2 | World!!! |
+-----------------+-------------+
Now If I am using Linq query given below is it possible to get Inputvalue data
var fileName = #"C:\Users\jshah\Documents\Visual Studio 2013\Projects\QA_ExpScript\QA_ExpScript\Excel\Inputdata.xls";
string sheetName = "Input";
var book = new LinqToExcel.ExcelQueryFactory(fileName);
var users = from x in book.Worksheet(Input) select x;
foreach (var x in users)
{
Console.WriteLine(x["1"]);
}
I am trying to do here is where inputlocation is "1" give me Inputvalue which is "Hello". Am I on a correct way to specify the query?. Also I am using this again and again later in my code. So please give better solution for this.
You can use where clause to filter the data like this:-
var users = from x in book.Worksheet()
where x["Inputlocation"].Cast<int>() == 1
select x["Inputvalue"].Cast<string>();
Then you can simply iterate through users:-
foreach (var item in users)
{
Console.WriteLine(item.Inputvalue); //This will print "Hello"
}
Although IMHO its always better to create a matching type to avoid any exceptions that can occur by typos when specifying the column names.
public class User
{
public int Inputlocation { get; set; }
public string Inputvalue { get; set; }
}
and here is the query:-
var users = from x in book.Worksheet<User>()
where x.Inputlocation == 1
select x;
This is kinda same as I am doing but somewhat answering the question. But don't what which #RahulSingh is saying.
Also related to #Chris 1 in the code. And creator of this post is i think saying that he does not want to change anythhing in his function. The only change he wants is Console.WriteLine(x["1"]); or Console.WriteLine(x["2"]); to get Inputvalue
Reference to my post
I have situation where a storeprocdure return collection, but I do not how the object structure because the query is very dynamic.
One query can return:
Id | Location | MarketSegment | ... n columns
and another can return
Id | Sales Rep | Location | Region | ... n columns
I am simply just return a "object" as you can see in the code below. I know this won't work, but how can I set it up so it does?
using (DbContext db = new Context())
{
var items = db.Database.SqlQuery<object>(
"SP #Param1, #Param2",
new SqlParameter("Param1", ped),
new SqlParameter("Param2", 25)
).ToList();
return Request.CreateResponse<List<object>>(HttpStatusCode.OK, items);
}
EDIT:
I don't know if showing the SP will help in anyways, except if I can explain it more.
Each columns are represented as Custom Fields. Users are able to create n numbers of Custom Fields. So If you run the SP for User1 and he has 5 custom fields, then each custom fields will be represented in Columns, but If User2 has 3 custom fields, only 3 columns will be represented. What I don't have control over is the Custom Field Name and number of custom fields.
If on SQL 2016 or newer, add "FOR JSON AUTO" to your query to return as JSON, e.g:
var json = db.Database.SqlQuery<string>("Select x, y, z FROM tbl FOR JSON AUTO").First();
Then use Json.Net to create a dynamic object using
var myDynamic = JObject.Parse(json)
You can't use SqlQuery<T> for custom fields.
Creates a raw SQL query that will return elements of the given generic
type. The type can be any type that has properties that match the
names of the columns returned from the query, or can be a simple
primitive type. - MSDN
But, you can use ExecuteReader to achieve that.
using (var db = new Context())
{
db.Database.Connection.Open();
var cmd = db.Database.Connection.CreateCommand();
cmd.CommandText = "SP #Param1, #Param2";
cmd.Parameters.Add(new SqlParameter("Param1", ped));
cmd.Parameters.Add(new SqlParameter("Param2", 25));
List<List<object>> items = new List<List<object>>();
var reader = cmd.ExecuteReader();
while (reader.Read())
{
var item = new List<Object>();
items.Add(item);
for (int i = 0; i < reader.FieldCount ; i++)
item.Add(reader[i]);
}
return Request.CreateResponse<List<object>>(HttpStatusCode.OK, items);
}
If you know what all the possible columns could be returned, there is no issue with using a class that has more properties than you need.
public class Data
{
public int ID {get;set;}
public string SalesRep {get;set;}//Simply will be empty in the first example, but populated in the second.
public string Location {get;set;}
}
I need your advice on the following.
I have a multi-dimensional IList containing items which have an index, Id and Text. Normally I know the value of Id and based on that I need to get the Text. Both Id and Text values are read from a database.
What we are currently using to get the value of Text field is:
foreach (Object myObj in List)
{
if (((MessageType)myObj).Id == id)
{
return ((MessageType)myObj).Text;
}
}
When count in IList becomes large (more than 32K), it takes some time to process.
Question: Is there a way to efficiently get the Text value without iterating through the IList?
Things I tried without success:
Use List.IndexOf(Id) - did not work because IndexOf applies to text only.
Converting List to multi-dimensional array - failed on List.CopyTo(array,0) my guess because it is multi-dimensional:
string[] array=new string[List.Count,List.Count];
List.CopyTo(array,0);
I can not use a AJAX/JQuery solution because it is an existing(live) project and it will take too much to re-code.
Thanks
If you want fast searching by some identifier in a collection with 32k elements, you should use Dictionary<K,V> as your collection.
var dict = new Dictionary<IDType, MessageType>();
A Dictionary is basically a search tree where the elements are stored in a sorted way so an element with a specific key (in your case Id) can be found without looking at all elements. For more information see MSDN.
If you cannot refactor the collection to be a dictionary, you may initially fill the dictionary (slow) and then search in the dictionary (fast). This will only be faster if you do multiple searches before you fill the dictionary again, i.e. if your list does not change often.
foreach(object o in List)
{
var msg = (MessageType)o;
dict.Add(msg.Id, msg);
}
Searching then is easy:
MessageType msg = dict[id];
EDIT: Well, I was curious and wrote a test routine which compares the linear search and the dictionary approach. Here's what I used:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
namespace ConsoleApplication1
{
class MessageType
{
public string Id;
public string Text;
}
class Program
{
static void Main(string[] args)
{
var rand = new Random ();
// filling a list with random text messages
List<MessageType> list = new List<MessageType>();
for (int i = 0; i < 32000; i++)
{
string txt = rand.NextDouble().ToString();
var msg = new MessageType() {Id = i.ToString(), Text = txt };
list.Add(msg);
}
IList List = (IList)list;
// doing some random searches
foreach (int some in new int[] { 2, 10, 100, 1000 })
{
var watch1 = new Stopwatch();
var watch2 = new Stopwatch();
Dictionary<string, MessageType> dict = null;
for (int i = 0; i < some; i++)
{
string id = rand.Next(32000).ToString();
watch1.Start();
LinearLookup(List, id);
watch1.Stop();
watch2.Start();
// fill once
if (dict == null)
{
dict = new Dictionary<string, MessageType>();
foreach (object o in List)
{
var msg = (MessageType)o;
dict.Add(msg.Id, msg);
}
}
// lookup
DictionaryLookup(dict, id);
watch2.Stop();
}
Console.WriteLine(some + " x LinearLookup took "
+ watch1.Elapsed.TotalSeconds + "s");
Console.WriteLine("Dictionary fill and " + some
+ " x DictionaryLookup took "
+ watch2.Elapsed.TotalSeconds + "s");
}
}
static string LinearLookup(IList List, string id)
{
foreach (object myObj in List)
{
if (((MessageType)myObj).Id == id)
{
return ((MessageType)myObj).Text;
}
}
throw new Exception();
}
static string DictionaryLookup(Dictionary<string, MessageType> dict,
string id)
{
return dict[id].Text;
}
}
}
The results I got in Release / x86:
Number of | Time [ms] with | Time[ms] with | Speedup (approx.)
searches | linear search | dictionary(*) | with dictionary
----------+----------------+---------------+-----------------
2 | 1.161 | 2.006 | 0.6
----------+----------------+---------------+-----------------
10 | 2.834 | 2.060 | 1.4
----------+----------------+---------------+-----------------
100 | 25.39 | 1.973 | 13
----------+----------------+---------------+-----------------
1000 | 261.4 | 5.836 | 45
----------+----------------+---------------+-----------------
(*) including filling the dictionary once.
So, I was a bit optimistic to say that searching twice would already pay off. In my test application I have to search 10 times for the dictionary to be faster.
I'm sorry I could not make a more realistic example, my Ids are all sorted. Feel free to try modifying and experimenting though ;-)
From the looks of it you have a List<MessageType> here, which is not multi-dimensional. Rather the objects inside the list have multiple properties.
You could easily get them out with LINQ much faster than a loop most likely:
var text = (from MessageType msgType in myList
where msgType.Id == id
select msgType.Text).FirstOrDefault();
Or even easier with an inline LINQ statement:
var text = myList.Where(s => s.Id == id).Select(s => s.Text).FirstOrDefault();
NOTE: As mentioned in comments above, the speed of these LINQ statements are only as good as the object's position in the List. If it is the last object in the list, you will likely see the same performance discrepancy. Dictionary<Index, MessageType> is going to be much more performant.
Better way is to use ILookup.
For example:
var look = query.ToLookup(x => x.SomeID, y=> y.Name)
and use:
if (look.Contains(myID)){
var name = look[myID].First();
}
I have a web page in which I am giving USER the options of writing notes. Now when ever the web page checks that a USER is:abc then it pulls up the note from the MEMO Table.
Here is my code in Page_Load():
using (EntityMemoDataContext em = new EntityMemoDataContext())
{
int getEntity = Int16.Parse(Session["EntityIdSelected"].ToString());
var showMemo = from r in em.EntityMemoVs_1s
where r.EntityID == getEntity
select r.Memo;
tbShowNote.Text = String.Join(#"<br />", showMemo);
}
tbShowNote is showing me value like this:
test<br />test1<br />test1<br />test4<br />test4
And I want it like this:
Test
Test1
Test2 ...
tbShowNote is a TextBox!
You only asked for the first memo, so that's what you got back. If you want it enumerated with each one on it's own line in html, you could do this:
using (EntityMemoDataContext em = new EntityMemoDataContext())
{
int getEntity1 = Int16.Parse(Session["EntityIdSelected"].ToString());
var showMemo = from r in em.EntityMemoVs_1s
where r.EntityID == getEntity1
select new
{
r.Memo
};
tbShowNote.Text = String.Join(#"<br />", showMemo);
}
The key takeaway is if r.Memo is of type string, then the LINQ query you executed gave you back a IQueryable<string>. It's on you to decide if you want to flatten that list later.
Edit: Equiso made a good observation in that you're actually returning an IQueryable of an anonymous type, not IQueryable<string> due to the new { ... } syntax. I'd say combine his answer with mine and run with it:
var showMemo = from r in em.EntityMemoVs_1s
where r.EntityID == getEntity1
select r.Memo;
tbShowNote.Text = String.Join(#"<br />", showMemo);
The problem is in the select part of your linq query, you are wrapping your results in an anonymous type, that is why when you call ToString() you see { Memo = test }. You probably want it like this:
var showMemo = from r in em.EntityMemoVs_1s
where r.EntityID == getEntity1
select r.Memo;
After that showMemo will contain just strings.
It looks like your showMemo is a collection and you are then just assigning the top value? If you are putting them in one string then you need to aggregate them together.