Optimize queries for Union, Except, Join with LINQ and C# - linq

I have 2 objects (lists loaded from XML) report and database (showed bellow in code) and i should analyse them and mark items with 0, 1, 2, 3 according to some conditions
TransactionResultCode = 0; // SUCCESS (all fields are equivalents: [Id, AccountNumber, Date, Amount])
TransactionResultCode = 1; // Exists in report but Not in database
TransactionResultCode = 2; // Exists in database but Not in report
TransactionResultCode = 3; // Field [Id] are equals but other fields [AccountNumber, Date, Amount] are different.
I'll be happy if somebody could found time to suggest how to optimize some queries.
Bellow is the code:
THANK YOU!!!
//TransactionResultCode = 0 - SUCCESS
//JOIN on all fields
var result0 = from d in database
from r in report
where (d.TransactionId == r.MovementID) &&
(d.TransactionAccountNumber == long.Parse(r.AccountNumber)) &&
(d.TransactionDate == r.MovementDate) &&
(d.TransactionAmount == r.Amount)
orderby d.TransactionId
select new TransactionList()
{
TransactionId = d.TransactionId,
TransactionAccountNumber = d.TransactionAccountNumber,
TransactionDate = d.TransactionDate,
TransactionAmount = d.TransactionAmount,
TransactionResultCode = 0
};
//*******************************************
//JOIN on [Id] field
var joinedList = from d in database
from r in report
where d.TransactionId == r.MovementID
select new TransactionList()
{
TransactionId = d.TransactionId,
TransactionAccountNumber = d.TransactionAccountNumber,
TransactionDate = d.TransactionDate,
TransactionAmount = d.TransactionAmount
};
//Difference report - database
var onlyReportID = report.Select(r => r.MovementID).Except(joinedList.Select(d => d.TransactionId));
//TransactionResultCode = 1 - Not Found in database
var result1 = from o in onlyReportID
from r in report
where (o == r.MovementID)
orderby r.MovementID
select new TransactionList()
{
TransactionId = r.MovementID,
TransactionAccountNumber = long.Parse(r.AccountNumber),
TransactionDate = r.MovementDate,
TransactionAmount = r.Amount,
TransactionResultCode = 1
};
//*******************************************
//Difference database - report
var onlyDatabaseID = database.Select(d => d.TransactionId).Except(joinedList.Select(d => d.TransactionId));
//TransactionResultCode = 2 - Not Found in report
var result2 = from o in onlyDatabaseID
from d in database
where (o == d.TransactionId)
orderby d.TransactionId
select new TransactionList()
{
TransactionId = d.TransactionId,
TransactionAccountNumber = d.TransactionAccountNumber,
TransactionDate = d.TransactionDate,
TransactionAmount = d.TransactionAmount,
TransactionResultCode = 2
};
//*******************************************
var qwe = joinedList.Select(j => j.TransactionId).Except(result0.Select(r => r.TransactionId));
//TransactionResultCode = 3 - Transaction Results are different (Amount, AccountNumber, Date, )
var result3 = from j in joinedList
from q in qwe
where j.TransactionId == q
select new TransactionList()
{
TransactionId = j.TransactionId,
TransactionAccountNumber = j.TransactionAccountNumber,
TransactionDate = j.TransactionDate,
TransactionAmount = j.TransactionAmount,
TransactionResultCode = 3
};

you may try something like below:
public void Test()
{
var report = new[] {new Item(1, "foo", "boo"), new Item(2, "foo2", "boo2"), new Item(3, "foo3", "boo3")};
var dataBase = new[] {new Item(1, "foo", "boo"), new Item(2, "foo22", "boo2"), new Item(4, "txt", "rt")};
Func<Item, bool> inBothLists = (i) => report.Contains(i) && dataBase.Contains(i);
Func<IEnumerable<Item>, Item, bool> containsWithID = (e, i) => e.Select(_ => _.ID).Contains(i.ID);
Func<Item, int> getCode = i =>
{
if (inBothLists(i))
{
return 0;
}
if(containsWithID(report, i) && containsWithID(dataBase, i))
{
return 3;
}
if (report.Contains(i))
{
return 2;
}
else return 1;
};
var result = (from item in dataBase.Union(report) select new {Code = getCode(item), Item = item}).Distinct();
}
public class Item
{
// You need also to override Equals() and GetHashCode().. I omitted them to save space
public Item(int id, string text1, string text2)
{
ID = id;
Text1 = text1;
Text2 = text2;
}
public int ID { get; set; }
public string Text1 { get; set; }
public string Text2 { get; set; }
}
Note that you need to either implement Equals() for you items, or implement an IEqualityComparer<> and feed it to Contains() methods.

Related

Entity Framework and LINQ: Is there anyway to auto reflect the renamed entity in LINQ query

I was trying to change the table names in the entity model or database but the old names are already use in many places in the application. Is there any way to auto reflect renamed entities or tables in the LINQ query or code.
Let say I have tables tblDepartment, tblEmployee and tblEmployeeDepartment. These tables are used in the code(LINQ) on many places. I like to change these tables names to Department, Employee and EmployeeDepartment. So, is there anyway to auto reflect name in LINQ or code when I change table names either using Database First or Model First approach.
P.S. The application is based on .Net 3.5
Working With linq and Entity Framework plus excel reports|Enjoy
public static string strMessage = "";
public SchoolEntities dbContext;
public string login(string strUsername, string strPassword)
{
dbContext = new SchoolEntities();
var linqQuery = from User in dbContext.People
where User.FirstName == strUsername && User.LastName == strPassword
select User;
if (linqQuery.Count() == 1)
{
strMessage = "Good";
}
else
{
strMessage = "Bad";
}
return strMessage;
}
public Object LoadPersonDetails()
{
dbContext = new SchoolEntities();
//DataTable dtPerson = new DataTable();
var linqQuery = from users in dbContext.People
select users;
//List<Person> Users = linqQuery.ToList();
//dtPerson = linqQuery.ToList();
return linqQuery;
}
public void InsertPerson(string strLName, string strFName, string strHireDate, string EnrollmentDate)
{
dbContext = new SchoolEntities();
Person NewPerson = dbContext.People.Create();
NewPerson.LastName = strLName;
NewPerson.FirstName = strFName;
NewPerson.HireDate = Convert.ToDateTime(strHireDate);
NewPerson.EnrollmentDate = Convert.ToDateTime(EnrollmentDate);
dbContext.People.Add(NewPerson);
dbContext.SaveChanges();
}
public void DeleteUser(int intPersonID)
{
//dbContext = new SchoolEntities();
using (dbContext = new SchoolEntities())
{
Person Person = dbContext.People.Where(c => c.PersonID == intPersonID).FirstOrDefault();
if (Person != null)
{
dbContext.People.Remove(Person);
dbContext.SaveChanges();
}
}
}
public void ModifyPerson(int intPersonID, string strLName, string strFName, string strHireDate, string EnrollmentDate)
{
var UpdatePerson = dbContext.People.FirstOrDefault(s => s.PersonID == intPersonID);
UpdatePerson.LastName = strLName;
UpdatePerson.FirstName = strFName;
UpdatePerson.HireDate = Convert.ToDateTime(strHireDate);
UpdatePerson.EnrollmentDate = Convert.ToDateTime(EnrollmentDate);
dbContext.SaveChanges();
}
private Excel.Application XApp = null; //Creates the Excel Document
private Excel.Workbook XWorkbook = null; //create the workbook in the recently created document
private Excel.Worksheet XWorksheet = null; //allows us to work with current worksheet
private Excel.Range XWorkSheet_range = null; // allows us to modify cells on the sheet
public void Reports()
{
dbContext = new SchoolEntities();
var linqQuery = (from users in dbContext.StudentGrades
group users by new { users.EnrollmentID, users.CourseID, users.StudentID, users.Grade }
into UserGroup
orderby UserGroup.Key.CourseID ascending
select new { UserGroup.Key.EnrollmentID, UserGroup.Key.CourseID, UserGroup.Key.StudentID, UserGroup.Key.Grade }).ToList();
var RatingAverage = dbContext.StudentGrades.Average(r => r.Grade);
var GradeSum = dbContext.StudentGrades.Sum(r => r.Grade);
/*var linqQuery = (from users in dbContext.StudentGrades
orderby users.CourseID descending
select users).ToList();*/
//Array Motho = linqQuery.ToArray();
XApp = new Excel.Application();
XApp.Visible = true;
XWorkbook = XApp.Workbooks.Add(1);
XWorksheet = (Excel.Worksheet)XWorkbook.Sheets[1];
//Create column headers
XWorksheet.Cells[2, 1] = "EnrollmentID";
XWorksheet.Cells[2, 2] = "CourseID";
XWorksheet.Cells[2, 3] = "StudentID";
XWorksheet.Cells[2, 4] = "Grade";
//XWorksheet.Cells[2, 5] = "Enrollment Date";
int row = 3;
foreach (var Mothos in linqQuery)
{
XWorksheet.Cells[row, 1] = Mothos.EnrollmentID.ToString();
XWorksheet.Cells[row, 2] = Mothos.CourseID.ToString();
XWorksheet.Cells[row, 3] = Mothos.StudentID.ToString();
XWorksheet.Cells[row, 4] = Mothos.Grade.ToString();
row++;
}
int rows = linqQuery.Count();
XWorksheet.Cells[rows + 4, 3] = "Grades Average";
XWorksheet.Cells[rows + 4, 4] = RatingAverage.Value.ToString();
XWorksheet.Cells[rows + 5, 3] = "Grades Sum";
XWorksheet.Cells[rows + 5, 4] = GradeSum.Value.ToString();
//XWorkSheet_range.ColumnWidth = 30;
//XWorksheet.Cells.AutoFit();
}
Working with xml | add, modify and delete xml data
string conn = "E:/School/Development Sftware/2014/ReadWriteUpdateDeleteXML/DataService/Profiles.xml";
public string InsertProfile(string fname, string lname, string phone, string gender)
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(conn);
XmlElement subRoot = xmlDoc.CreateElement("Profile");
//add first name
XmlElement appendedElementFname = xmlDoc.CreateElement("FirstName");
XmlText xmlTextFname = xmlDoc.CreateTextNode(fname.Trim());
appendedElementFname.AppendChild(xmlTextFname);
subRoot.AppendChild(appendedElementFname);
xmlDoc.DocumentElement.AppendChild(subRoot);
//add last name
XmlElement appendedElementLname = xmlDoc.CreateElement("LastName");
XmlText xmlTextLname = xmlDoc.CreateTextNode(lname.Trim());
appendedElementLname.AppendChild(xmlTextLname);
subRoot.AppendChild(appendedElementLname);
xmlDoc.DocumentElement.AppendChild(subRoot);
//add phone
XmlElement appendedElementPhone = xmlDoc.CreateElement("Phone");
XmlText xmlTextPhone = xmlDoc.CreateTextNode(phone.Trim());
appendedElementPhone.AppendChild(xmlTextPhone);
subRoot.AppendChild(appendedElementPhone);
xmlDoc.DocumentElement.AppendChild(subRoot);
//add gender
XmlElement appendedElementGender = xmlDoc.CreateElement("Gender");
XmlText xmlTextGender = xmlDoc.CreateTextNode(gender.Trim());
appendedElementGender.AppendChild(xmlTextGender);
subRoot.AppendChild(appendedElementGender);
xmlDoc.DocumentElement.AppendChild(subRoot);
xmlDoc.Save(conn);
return "Profile Saved";
}
public DataSet LoadXML()
{
DataSet dsLog = new DataSet();
dsLog.ReadXml(conn);
return dsLog;
}
public string DeleteProfile(string fname)
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(conn);
//XmlNode nodeToDelete = xmlDoc.SelectSingleNode("/Profiles/Profile[#FirstName=" + fname + "]");
//if (nodeToDelete != null)
//{
// nodeToDelete.ParentNode.RemoveChild(nodeToDelete);
//}
//xmlDoc.Save("C:/Users/Shazzy/Documents/Visual Studio 2010/Projects/ReadWriteUpdateDeleteXML/DataService/Profiles.xml");
//return "Deleted";
foreach (XmlNode node in xmlDoc.SelectNodes("Profiles/Profile"))
{
if (node.SelectSingleNode("FirstName").InnerText == fname)
{
node.ParentNode.RemoveChild(node);
}
}
xmlDoc.Save(conn);
return "Deleted";
}
public string ModifyProfile(string fname, string lname, string phone, string gender)
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(conn);
foreach (XmlNode node in xmlDoc.SelectNodes("Profiles/Profile"))
{
if (node.SelectSingleNode("FirstName").InnerText == fname)
{
node.SelectSingleNode("FirstName").InnerText = fname;
node.SelectSingleNode("LastName").InnerText = lname;
node.SelectSingleNode("Phone").InnerText = phone;
node.SelectSingleNode("Gender").InnerText = gender;
}
}
xmlDoc.Save(conn);
return "Updated";
}
Working with edm and Excel-grouped report
public void Function_Create_Sales()
{
DBContext = new PubsEntities();
var linqStores = from Sales in DBContext.sales
orderby Sales.stor_id
select Sales;
var lstStore = linqStores.ToList();
Excel.Application xlApp = new Excel.Application();
xlApp.Visible = true;
Excel.Workbook xlBook = xlApp.Workbooks.Add(1);
Excel.Worksheet xlSheet = (Excel.Worksheet)xlBook.Worksheets[1];
int GroupTotal = 0;
int GrandTotal = 0;
int ExcelRow = 5;
string intTemp = lstStore[0].stor_id;
xlSheet.Cells[4, 1] = lstStore[0].stor_id;
//Create column headers
xlSheet.Cells[1, 1] = "Sales Grouped By Store";
xlSheet.Cells[3, 1] = "Group header";
xlSheet.Cells[3, 2] = "Store ID";
xlSheet.Cells[3, 3] = "Order Number";
xlSheet.Cells[3, 4] = "Order Date";
xlSheet.Cells[3, 5] = "Quantity";
xlSheet.Cells[3, 6] = "payments";
xlSheet.Cells[3, 7] = "title ID";
for (int count = 0; count < lstStore.Count; count++)
{
if (intTemp == lstStore[count].stor_id)
{
xlSheet.Cells[ExcelRow, 2] = lstStore[count].stor_id.ToString();
xlSheet.Cells[ExcelRow, 3] = lstStore[count].ord_date.ToString();
xlSheet.Cells[ExcelRow, 4] = lstStore[count].qty.ToString();
xlSheet.Cells[ExcelRow, 5] = lstStore[count].payterms.ToString();
xlSheet.Cells[ExcelRow, 5] = lstStore[count].title_id.ToString();
ExcelRow++;
GroupTotal++;
GrandTotal++;
}
else
{
xlSheet.Cells[ExcelRow, 5] = "Total for: " + intTemp + " = " + GroupTotal.ToString();
ExcelRow++;
intTemp = lstStore[count].stor_id;
xlSheet.Cells[ExcelRow, 1] = lstStore[count].stor_id;
count--;
GroupTotal = 0;
ExcelRow++;
}
}
xlSheet.Cells[ExcelRow, 5] = "Total for: " + intTemp + " = " + GroupTotal.ToString();
ExcelRow++;
xlSheet.Cells[ExcelRow, 5] = "Grand Total = " + GrandTotal.ToString();
xlSheet.Rows.Columns.AutoFit();
}

PIVOT with LINQ from Datatable [duplicate]

I have a collection of items that contain an Enum (TypeCode) and a User object, and I need to flatten it out to show in a grid. It's hard to explain, so let me show a quick example.
Collection has items like so:
TypeCode | User
---------------
1 | Don Smith
1 | Mike Jones
1 | James Ray
2 | Tom Rizzo
2 | Alex Homes
3 | Andy Bates
I need the output to be:
1 | 2 | 3
Don Smith | Tom Rizzo | Andy Bates
Mike Jones | Alex Homes |
James Ray | |
I've tried doing this using foreach, but I can't do it that way because I'd be inserting new items to the collection in the foreach, causing an error.
Can this be done in Linq in a cleaner fashion?
I'm not saying it is a great way to pivot - but it is a pivot...
// sample data
var data = new[] {
new { Foo = 1, Bar = "Don Smith"},
new { Foo = 1, Bar = "Mike Jones"},
new { Foo = 1, Bar = "James Ray"},
new { Foo = 2, Bar = "Tom Rizzo"},
new { Foo = 2, Bar = "Alex Homes"},
new { Foo = 3, Bar = "Andy Bates"},
};
// group into columns, and select the rows per column
var grps = from d in data
group d by d.Foo
into grp
select new {
Foo = grp.Key,
Bars = grp.Select(d2 => d2.Bar).ToArray()
};
// find the total number of (data) rows
int rows = grps.Max(grp => grp.Bars.Length);
// output columns
foreach (var grp in grps) {
Console.Write(grp.Foo + "\t");
}
Console.WriteLine();
// output data
for (int i = 0; i < rows; i++) {
foreach (var grp in grps) {
Console.Write((i < grp.Bars.Length ? grp.Bars[i] : null) + "\t");
}
Console.WriteLine();
}
Marc's answer gives sparse matrix that can't be pumped into Grid directly.
I tried to expand the code from the link provided by Vasu as below:
public static Dictionary<TKey1, Dictionary<TKey2, TValue>> Pivot3<TSource, TKey1, TKey2, TValue>(
this IEnumerable<TSource> source
, Func<TSource, TKey1> key1Selector
, Func<TSource, TKey2> key2Selector
, Func<IEnumerable<TSource>, TValue> aggregate)
{
return source.GroupBy(key1Selector).Select(
x => new
{
X = x.Key,
Y = source.GroupBy(key2Selector).Select(
z => new
{
Z = z.Key,
V = aggregate(from item in source
where key1Selector(item).Equals(x.Key)
&& key2Selector(item).Equals(z.Key)
select item
)
}
).ToDictionary(e => e.Z, o => o.V)
}
).ToDictionary(e => e.X, o => o.Y);
}
internal class Employee
{
public string Name { get; set; }
public string Department { get; set; }
public string Function { get; set; }
public decimal Salary { get; set; }
}
public void TestLinqExtenions()
{
var l = new List<Employee>() {
new Employee() { Name = "Fons", Department = "R&D", Function = "Trainer", Salary = 2000 },
new Employee() { Name = "Jim", Department = "R&D", Function = "Trainer", Salary = 3000 },
new Employee() { Name = "Ellen", Department = "Dev", Function = "Developer", Salary = 4000 },
new Employee() { Name = "Mike", Department = "Dev", Function = "Consultant", Salary = 5000 },
new Employee() { Name = "Jack", Department = "R&D", Function = "Developer", Salary = 6000 },
new Employee() { Name = "Demy", Department = "Dev", Function = "Consultant", Salary = 2000 }};
var result5 = l.Pivot3(emp => emp.Department, emp2 => emp2.Function, lst => lst.Sum(emp => emp.Salary));
var result6 = l.Pivot3(emp => emp.Function, emp2 => emp2.Department, lst => lst.Count());
}
* can't say anything about the performance though.
You can use Linq's .ToLookup to group in the manner you are looking for.
var lookup = data.ToLookup(d => d.TypeCode, d => d.User);
Then it's a matter of putting it into a form that your consumer can make sense of. For instance:
//Warning: untested code
var enumerators = lookup.Select(g => g.GetEnumerator()).ToList();
int columns = enumerators.Count;
while(columns > 0)
{
for(int i = 0; i < enumerators.Count; ++i)
{
var enumerator = enumerators[i];
if(enumator == null) continue;
if(!enumerator.MoveNext())
{
--columns;
enumerators[i] = null;
}
}
yield return enumerators.Select(e => (e != null) ? e.Current : null);
}
Put that in an IEnumerable<> method and it will (probably) return a collection (rows) of collections (column) of User where a null is put in a column that has no data.
I guess this is similar to Marc's answer, but I'll post it since I spent some time working on it. The results are separated by " | " as in your example. It also uses the IGrouping<int, string> type returned from the LINQ query when using a group by instead of constructing a new anonymous type. This is tested, working code.
var Items = new[] {
new { TypeCode = 1, UserName = "Don Smith"},
new { TypeCode = 1, UserName = "Mike Jones"},
new { TypeCode = 1, UserName = "James Ray"},
new { TypeCode = 2, UserName = "Tom Rizzo"},
new { TypeCode = 2, UserName = "Alex Homes"},
new { TypeCode = 3, UserName = "Andy Bates"}
};
var Columns = from i in Items
group i.UserName by i.TypeCode;
Dictionary<int, List<string>> Rows = new Dictionary<int, List<string>>();
int RowCount = Columns.Max(g => g.Count());
for (int i = 0; i <= RowCount; i++) // Row 0 is the header row.
{
Rows.Add(i, new List<string>());
}
int RowIndex;
foreach (IGrouping<int, string> c in Columns)
{
Rows[0].Add(c.Key.ToString());
RowIndex = 1;
foreach (string user in c)
{
Rows[RowIndex].Add(user);
RowIndex++;
}
for (int r = RowIndex; r <= Columns.Count(); r++)
{
Rows[r].Add(string.Empty);
}
}
foreach (List<string> row in Rows.Values)
{
Console.WriteLine(row.Aggregate((current, next) => current + " | " + next));
}
Console.ReadLine();
I also tested it with this input:
var Items = new[] {
new { TypeCode = 1, UserName = "Don Smith"},
new { TypeCode = 3, UserName = "Mike Jones"},
new { TypeCode = 3, UserName = "James Ray"},
new { TypeCode = 2, UserName = "Tom Rizzo"},
new { TypeCode = 2, UserName = "Alex Homes"},
new { TypeCode = 3, UserName = "Andy Bates"}
};
Which produced the following results showing that the first column doesn't need to contain the longest list. You could use OrderBy to get the columns ordered by TypeCode if needed.
1 | 3 | 2
Don Smith | Mike Jones | Tom Rizzo
| James Ray | Alex Homes
| Andy Bates |
#Sanjaya.Tio I was intrigued by your answer and created this adaptation which minimizes keySelector execution. (untested)
public static Dictionary<TKey1, Dictionary<TKey2, TValue>> Pivot3<TSource, TKey1, TKey2, TValue>(
this IEnumerable<TSource> source
, Func<TSource, TKey1> key1Selector
, Func<TSource, TKey2> key2Selector
, Func<IEnumerable<TSource>, TValue> aggregate)
{
var lookup = source.ToLookup(x => new {Key1 = key1Selector(x), Key2 = key2Selector(x)});
List<TKey1> key1s = lookup.Select(g => g.Key.Key1).Distinct().ToList();
List<TKey2> key2s = lookup.Select(g => g.Key.Key2).Distinct().ToList();
var resultQuery =
from key1 in key1s
from key2 in key2s
let lookupKey = new {Key1 = key1, Key2 = key2}
let g = lookup[lookupKey]
let resultValue = g.Any() ? aggregate(g) : default(TValue)
select new {Key1 = key1, Key2 = key2, ResultValue = resultValue};
Dictionary<TKey1, Dictionary<TKey2, TValue>> result = new Dictionary<TKey1, Dictionary<TKey2, TValue>>();
foreach(var resultItem in resultQuery)
{
TKey1 key1 = resultItem.Key1;
TKey2 key2 = resultItem.Key2;
TValue resultValue = resultItem.ResultValue;
if (!result.ContainsKey(key1))
{
result[key1] = new Dictionary<TKey2, TValue>();
}
var subDictionary = result[key1];
subDictionary[key2] = resultValue;
}
return result;
}

Entity Framework ObjectContext.SaveChanges fails on unique key column update

Consider very simple database table:
CREATE TABLE UkTest(
id int NOT NULL,
uk int NOT NULL
)
Primary Key on id
Unique Key on uk
Then add 2 rows:
INSERT INTO UkTest (id,uk) VALUES(1,1);
INSERT INTO UkTest (id,uk) VALUES(2,2);
Then do 2 tests.
OK:
var db = new Database1Entities();
var element1 = db.UkTest.FirstOrDefault(e => e.id == 1);
var element2 = db.UkTest.FirstOrDefault(e => e.id == 2);
element1.uk = 0;
element2.uk = 1; // overrides previous element1.uk value
var count = db.SaveChanges();
Fails (before test revert uk values to 1 and 2!):
var db = new Database1Entities();
var element1 = db.UkTest.FirstOrDefault(e => e.id == 1);
var element2 = db.UkTest.FirstOrDefault(e => e.id == 2);
element2.uk = 0;
element1.uk = 2; // overrides previous element2.uk value
var count = db.SaveChanges();
// Cannot insert duplicate key row in object 'dbo.UkTest' with unique index 'UK_UkTest'
See that ObjectContext.SaveChanges() checks rows in the order of primary index.
Is there any way to force own order?
Unless you call SaveChanges() twice, no, there is no way to control the order of SQL statements Entity Framework will send to the database. You could wrap the multiple SaveChanges() calls into an outer transaction to ensure still a transactional behaviour for the whole operation:
using (var scope = new TransactionScope())
{
using (var db = new Database1Entities())
{
var element1 = db.UkTest.FirstOrDefault(e => e.id == 1);
var element2 = db.UkTest.FirstOrDefault(e => e.id == 2);
element2.uk = 0;
db.SaveChanges();
element1.uk = 2;
db.SaveChanges();
}
scope.Complete();
}
Thanks to Slauma. I've found the solution. The key is to keep elements in several ObjectContext instances.
public class SavingElementsWithTransactionInOwnOrder
{
public void SaveElements ()
{
var db = new Database1Entities();
var element1 = db.UkTest.FirstOrDefault(e => e.id == 1);
element1.db = db;
db = new Database1Entities();
var element2 = db.UkTest.FirstOrDefault(e => e.id == 2);
element2.db = db;
element2.uk = 0;
element1.uk = 2;
var scope = new TransactionScope();
try{
element2.db.SaveChanges();
element1.db.SaveChanges();
scope.Complete();
}
finally{
scope.Dispose();
}
}
}
public partial class UkTest
{
public Database1Entities db { get; set; }
}

LINQ Union with Constant Values

Very primitive question but I am stuck (I guess being newbie). I have a function which is supposed to send me the list of companies : ALSO, I want the caller to be able to specify a top element for the drop-down list as well.. (say for "None"). I have following piece of code, how I will append the Top Element with the returning SelectList?
public static SelectList GetCompanies( bool onlyApproved, FCCIEntityDataContext entityDataContext, SelectListItem TopElement )
{
var cs = from c in entityDataContext.Corporates
where ( c.Approved == onlyApproved || onlyApproved == false )
select new
{
c.Id,
c.Company
};
return new SelectList( cs.AsEnumerable(), "Id", "Comapny" );
}
Thanks!
This should work for you:
List<Corporate> corporates =
(from c in entityDataContext.Corporates
where (c.Approved == onlyApproved || onlyApproved == false)
select c).ToList();
corporates.Add(new Corporate { Id = -1, Company = "None" });
return new SelectList(corporates.AsEnumerable(), "Id", "Comapny");
This method has always worked for me.
public static SelectList GetCompanies( bool onlyApproved, FCCIEntityDataContext entityDataContext, SelectListItem TopElement )
{
var cs = from c in entityDataContext.Corporates
where ( c.Approved == onlyApproved || onlyApproved == false )
select new SelectListItem {
Value = c.Id,
Text = c.Company
};
var list = cs.ToList();
list.Insert(0, TopElement);
var selectList = new SelectList( list, "Value", "Text" );
selectList.SelectedValue = TopElement.Value;
return selectList;
}
Update forgot the lesson I learned when I did this. You have to output the LINQ as SelectListItem.
cs.ToList().Insert(0, new { TopElement.ID, TopElement.Company });
You could convert it to a list as indicated or you could union the IQueryable result with a constant array of one element (and even sort it):
static void Main(string[] args)
{
var sampleData = new[] {
new { Id = 1, Company = "Acme", Approved = true },
new { Id = 2, Company = "Blah", Approved = true }
};
bool onlyApproved = true;
var cs = from c in sampleData
where (c.Approved == onlyApproved || onlyApproved == false)
select new
{
c.Id,
c.Company
};
cs = cs.Union(new [] {new { Id = -1, Company = "None" }}).OrderBy(c => c.Id);
foreach (var c in cs)
{
Console.WriteLine(String.Format("Id = {0}; Company = {1}", c.Id, c.Company));
}
Console.ReadKey();
}

How To Project a Line Number Into Linq Query Results

How can I project the row number onto the linq query result set.
Instead of say:
field1, field2, field3
field1, field2, field3
I would like:
1, field1, field2, field3
2, field1, field2, field3
Here is my attempt at this:
public List<ScoreWithRank> GetHighScoresWithRank(string gameId, int count)
{
Guid guid = new Guid(gameId);
using (PPGEntities entities = new PPGEntities())
{
int i = 1;
var query = from s in entities.Scores
where s.Game.Id == guid
orderby s.PlayerScore descending
select new ScoreWithRank()
{
Rank=i++,
PlayerName = s.PlayerName,
PlayerScore = s.PlayerScore
};
return query.ToList<ScoreWithRank>();
}
}
Unfortunately, the "Rank=i++" line throws the following compile-time exception:
"An expression tree may not contain an assignment operator"
Well, the easiest way would be to do it at the client side rather than the database side, and use the overload of Select which provides an index as well:
public List<ScoreWithRank> GetHighScoresWithRank(string gameId, int count)
{
Guid guid = new Guid(gameId);
using (PPGEntities entities = new PPGEntities())
{
var query = from s in entities.Scores
where s.Game.Id == guid
orderby s.PlayerScore descending
select new
{
PlayerName = s.PlayerName,
PlayerScore = s.PlayerScore
};
return query.AsEnumerable() // Client-side from here on
.Select((player, index) => new ScoreWithRank()
{
PlayerName = player.PlayerName,
PlayerScore = player.PlayerScore,
Rank = index + 1;
})
.ToList();
}
}
Ok, that did the trick. Thanks.
Here is my final code...
Server:
public List<Score> GetHighScores(string gameId, int count)
{
Guid guid = new Guid(gameId);
using (PPGEntities entities = new PPGEntities())
{
var query = from s in entities.Scores
where s.Game.Id == guid
orderby s.PlayerScore descending
select s;
return query.ToList<Score>();
}
}
Client:
void hsc_LoadHighScoreCompleted(object sender, GetHighScoreCompletedEventArgs e)
{
ObservableCollection<Score> list = e.Result;
_listBox.ItemsSource = list.Select((player, index) => new ScoreWithRank()
{
PlayerName = player.PlayerName,
PlayerScore = player.PlayerScore,
Rank = index+=1
}).ToList();
}
You could also make just a slight adjustment to your original code to get it working. Word of caution, if you databind or access the object again, the Rank will increment each time. In those cases the top answer is better.
let Rank = i++
and
Rank.ToString()
Full code:
public List<ScoreWithRank> GetHighScoresWithRank(string gameId, int count)
{
Guid guid = new Guid(gameId);
using (PPGEntities entities = new PPGEntities())
{
int i = 1;
var query = from s in entities.Scores
let Rank = i++
where s.Game.Id == guid
orderby s.PlayerScore descending
select new ScoreWithRank()
{
Rank.ToString(),
PlayerName = s.PlayerName,
PlayerScore = s.PlayerScore
};
return query.ToList<ScoreWithRank>();
}
}
This solution worked for me.
http://www.dotnetfunda.com/articles/article1995-rownumber-simulation-in-linq.aspx
.Select((x, index) => new
{
SequentialNumber = index + 1
,FieldFoo = x.FieldFoo
}).ToList();
List<Emp> Lstemp = GetEmpList();
int Srno = 0;
var columns = from t in Lstemp
orderby t.Name
select new {
Row_number=++Srno,
EmpID = t.ID,
Name = t.Name,
City = t.City
};

Resources