How can I define a List to add results of a query in a loop? - asp.net-mvc-3

I have an array filled with long type values and for each value in the array I need to implement a query. I used foreach loop as you can see from the code below:
var result;
foreach(long id in PrdIdArr)
{
var mainQuery = (from o in db.OPERATIONs
join u in db.UNITs on o.OP_UNIT_ID equals u.UNIT_ID into smt
from s in smt
join x in db.XIDs on s.UNIT_ID equals x.UNIT_ID
where o.OP_OT_CODE == OtCode
where x.IDTYP_CD == "BSN"
where s.START_PRD_ID == id
where o.OP_UPD_DATE >= _StartDate
where o.OP_UPD_DATE <= _EndDate
select new
{
o.OP_ID,
o.OP_UPD_DATE,
x.EXTERNAL_ID,
o.OP_OS_CODE,
o.OP_START,
o.OP_ST_STATION,
s.START_PRD_ID
}).Take(_RowNumber);
//var result = mainQuery.ToList();
result.add(mainQuery.ToList());
}
data = this.Json(result);
data.JsonRequestBehavior = JsonRequestBehavior.AllowGet;
return data;
However, I have a problem in my code; I have to define a main list just before the foreach loop so that I could add results of each query to the that main list. my question is: How can I define this list as you can see at the beginning of my code? Thanks for the help...

How can I define this list as you can see at the beginning of my code?
Make
new {
o.OP_ID,
o.OP_UPD_DATE,
x.EXTERNAL_ID,
o.OP_OS_CODE,
o.OP_START,
o.OP_ST_STATION,
s.START_PRD_ID
}
into a concrete type (say QueryResult, although something a little more specific than that), and then just declare
var result = new List<QueryResult>();
Also, you should consider turning
foreach(long id in PrdIdArr)
and
where s.START_PRD_ID == id
into
where PrdIdArr.Contains(s.Start_PRD_ID)

var result = new List<object>();
foreach(long id in PrdIdArr)
{
....
result.Add(mainQuery.ToList());
}

You could do this:
var result = PrdIdArr.Select(id =>
from o in db.OPERATIONs
join u in db.UNITs on o.OP_UNIT_ID equals u.UNIT_ID into smt
from s in smt
join x in db.XIDs on s.UNIT_ID equals x.UNIT_ID
where o.OP_OT_CODE == OtCode
where x.IDTYP_CD == "BSN"
where s.START_PRD_ID == id
where o.OP_UPD_DATE >= _StartDate
where o.OP_UPD_DATE <= _EndDate
select new
{
o.OP_ID,
o.OP_UPD_DATE,
x.EXTERNAL_ID,
o.OP_OS_CODE,
o.OP_START,
o.OP_ST_STATION,
s.START_PRD_ID
}
.Take(_RowNumber)
.ToList()
).ToList();
I highly recommend performing some Extract Method refactorings, as the code is pretty complex and hard to understand/mange this way.

Just create the anonymous type outside with the same property names and the correct type
var result = Enumerable.Range(0, 0).Select(x => new
{
OP_ID = 1,
OP_UPD_DATE = DateTime.Now,
EXTERNAL_ID = 1,
OP_OS_CODE = 1,
OP_START = DateTIme.Now,
OP_ST_STATION = "",
START_PRD_ID = 1,
}).ToList();
And in your loop call AddRange
result.AddRange(mainQuery.ToList());

Related

Linq to Sql Query - better solution (optimizing)

The following code works, but it's not a nice code. (low performance)
I have a dictionary with value and key.
First i go trough every webcodes who exist. Then i load all participants in a list (where webcode equals the actual webcode in the foreach). After that i add the data (parameter of the webcode and a count of participants to the dictionary).
Guid compID = Guid.Parse(wID);
ChartModel webcodes = new ChartModel();
webcodes.Title = "Webcodes Statistics";
webcodes.Data = new Dictionary<string, int>();
var webcodesData = db.t_Webcode;
foreach (var w in webcodesData)
{
var wData = db.t_Participant.Where(t => t.FK_Competition == compID && t.Webcode == w.Webcode);
if (wData.Count() != 0)
webcodes.Data.Add(w.Parameter, wData.Count());
}
ViewBag.Webcodes = webcodes;
TIA
You need something along these lines:
webcodes.Data = (from w in db.t_Webcode
join p in db.t_Participant on w.Webcode equals p.Webcode
where p.FK_Competition == compID
group w by w.Parameter into g
select new { g.Key, Count = g.Count() }).ToDictionary();
I can't test it but that is the type of query you need.
This will assume that you have relationships defined in your database and that your LINQ to SQL datacontext are aware of them. If not, you will need to join manually on t_Participants from tWebcode.
This should execute in 1 single SQL query, instead of 1 query per row in tWebcode.
var webcodesAndNoOfParticipants =
from webcode in db.tWebcode
// Define number of participants for this webcode
let numberOfParticipants = webcode.t_Participants.Count(participant => participant.FK_Competition == compID)
where numberOfParticipants > 0
select new {
WebcodeParameter = webcode.Parameter,
NoOfParticipants = numberOfParticipants
};
webcodes.Data = webcodesAndNoOfParticipants.ToDictionary(x => x.WebcodeParameter, x => x.NoOfParticipants);

Update class field in LINQ - is it possible?

I have 2 tables, and want to get records from 1 table and to "update" one of its fields from another table, and to pass final list of "Payment" objects somewhere. I cannot use anonymouse type, i need to get the list of proper typed objects.
There was a long way.
Got data:
var paymentsToShow = from p in paymentsRepository.Payments
join r in recordTypeRepository.RecordType
on p.RecordType equals r.Reference into p_r
where p.Customer == CustomerRef
from r in p_r.DefaultIfEmpty()
select new
{
Payment = p,
RecordType = r
};
var objList = paymentsToShow.ToList();
Change required field (basically, Payment.RecordTypeName is empty):
foreach (var obj in objList)
{
obj.Payment.RecordTypeName = obj.RecordType.Name;
}
Got list with correct type:
var paymentsList = from o in objList
select o.Payment;
Is there any way to get code shorter, to make required field update in the query or something else? I dont know where to look for.
I cannot change database.
You could do it like this:
var paymentsToShow = (from p in paymentsRepository.Payments
join r in recordTypeRepository.RecordType
on p.RecordType equals r.Reference into p_r
where p.Customer == CustomerRef
from r in p_r.DefaultIfEmpty()
select new
{
Payment = p,
RecordType = r
}).Select(x =>
{
x.Payment.RecordTypeName = x.RecordType.Name;
return x.Payment;
});
This will result in an IEnumerable<Payment>, so no anonymous type used.

Perform multiple LINQ updates to avoid uniquekey exception

I have a database with a Unique key on the the columns ParentRef and SortIndex.
In LINQ, I want to switch two SortIndex values. I want to do this in one transaction so there can be multiple users at once. How do I, with LINQ, switch the values in one go, so my Unique key does not be violated?
var dc = new MyDataContext();
using (TransactionScope trans = new TransactionScope())
{
var pageToBeMoved = dc.Pages.Where(p => p.ID == id).Single();
var pageToBeSwitched = (from p in dc.Pages
where p.ParentRef == pageToBeMoved.ParentRef
where p.SortIndex > pageToBeMoved.SortIndex
orderby p.SortIndex ascending
select p).First();
int tempSortIndex = pageToBeMoved.SortIndex;
pageToBeMoved.SortIndex = pageToBeSwitched.SortIndex;
pageToBeSwitched.SortIndex = tempSortIndex;
dc.SubmitChanges();
trans.Complete();
}
I think that to switch unique key values, you might need to use a third temporary value during the switch, that is:
create new value
set page2.SortIndex to new value
set page1.SortIndex to old page2.SortIndex
set page2.SortIndex to old page1.SortIndex
... otherwise you are likely to hit a unique key violation during the switch.
Something along these lines:
var dc = new MyDataContext();
using (TransactionScope trans = new TransactionScope())
{
var pageToBeMoved = dc.Pages.Where(p => p.ID == id).Single();
var pageToBeSwitched = (from p in dc.Pages
where p.ParentRef == pageToBeMoved.ParentRef
where p.SortIndex > pageToBeMoved.SortIndex
orderby p.SortIndex ascending
select p).First();
int oldMSortIndex = pageToBeMoved.SortIndex;
int oldSSortIndex = pageToBeSwitched.SortIndex;
// note: here you need to use some value that you know will not already
// be in the table ... maybe a max + 1 or something like that
int tempSortIndex = someunusedvalue;
pageToBeMoved.SortIndex = tempSortIndex;
dc.SubmitChanges();
pageToBeSwitched.SortIndex = oldMSortIndex;
dc.SubmitChanges();
pageToBeMoved.SortIndex = oldSSortIndex;
dc.SubmitChanges();
}

Is there a better way to code this LINQ fragment?

I have this fragment of code:
SmsDataClassesDataContext dc = new SmsDataClassesDataContext();
// Get the customer
Customer currentCustomer = dc.Customers.Single( c => c.Hash1 == forThisHash );
// Get from Name (LINQ to XML)
var q = from c in thisSmsPack.Descendants("from")
select c;
string from = q.First().Value;
foreach ( XElement element in thisSmsPack.Descendants("to") )
{
// Create the queue
SmsQueue sq = new SmsQueue();
sq.CustomerId = currentCustomer.CustomerId;
sq.MsgFrom = from;
sq.MsgTo = element.Attribute("name").Value;
sq.MsgPhone = element.Attribute("phone").Value;
sq.MsgBody = element.Attribute("msg").Value;
sq.Priority = currentCustomer.SendsSmsAtPriority;
sq.DontSendUntil = GetNextSendDate();
// sq.TimeCreated = System.DateTime.Now;
currentCustomer.SmsQueues.Add(sq);
}
dc.SubmitChanges();
I am creating new instances of "SmsQueues", populating the values and when the foreach loop is finished I submit the changes. Given the new lambda/linq/anonymous types that .NET 3.5 has, is there a more "modern" way to accomplish the above?
As a side question, maybe related, can I return an existing type composed of different columns in the select part of the linq expression?
Suppose you have three tables:
T1 == T1.Id, T1.Name
T2 == T2.Id, T2.Phone
T3 == T3.Name, T3.Phone, T3.SomethingElse
Can I perform a LINQ query that returns:
T1.Name, T2.Phone, SomethingElseNew
And let .NET know that that is of Type T3 (and it's a new instance of it)?
That way when I SubmitChanges, new T3 instances are inserted in the DB?
I don't know if I make myself clear :S
I don't have a system available to test this, but I think this (or something very close) should work.
CustomerId = currentCustomer.CustomerId;
var sqrange = from element in thisSmsPack.Descendants("to") )
select new SmsQueue
{
// Create the queue
MsgFrom = from,
MsgTo = element.Attribute("name").Value,
MsgPhone = element.Attribute("phone").Value,
MsgBody = element.Attribute("msg").Value,
Priority = currentCustomer.SendsSmsAtPriority,
DontSendUntil = GetNextSendDate()
// TimeCreated = System.DateTime.Now
};
currentCustomer.SmsQueues.AddRange(sqrange);
EDIT: Fixed the numerous syntax errors (as delineated in the comments)
You could do something like this (syntax may be off slightly, no intellisense here):
var q = T1.Join(T2, t => t.Id, t2 => t2.Id)
select new T3{Name=t.Name,Phone=t2.Phone,SomethingElseNew="Chickens"};

How can I get my orderby to work using an anonymous type?

What do I put in my order by?? I want to order by Name. I have moved the orderby after the distinct because I read that it needs to be done last.
var result = (from r in db.RecordDocs
where r.RecordID == recordID
select new
{
DocTypeID = r.Document.DocType.DocTypeID,
Name = r.Document.DocType.Name,
Number = r.Document.DocType.Number
}
).Distinct().OrderBy( );
Just do
.OrderBy(doc => doc.Name)
Another option, if you really prefer the query expression syntax would be to chain your query construction across multiple statements:
var query = from r in db.RecordDocs
where r.RecordID == recordID
select new
{
DocTypeID = r.Document.DocType.DocTypeID,
Name = r.Document.DocType.Name,
Number = r.Document.DocType.Number
};
query = query.Disctinct();
query = from doc in query orderby doc.Name select doc;
Since all of these methods are deferred, this will result in the exact same execution performance.

Resources