ChangeTracker equivalent on Entity Framework 4 - c#

I would like to log added records using Entity Framework
Table1
CatID | Col1 | Col2 | Col3 | Col4 |
----------------------------------
1 | a | b | c | d |
----------------------------------
Code for saving
var context = new DBEntities();
Table1 obj1 = new Table1{
Col1 = "a", Col2 = "b"
};
context.Table1.AddObject(obj1)
context.SaveChanges();
Dummy code for saving audit
var propWithValue = //get only Col1 and Col2
foreach(var list1 in propWithValue)
{
//save audit
}
How can i do this in EF4?

Related

Linq select tablemapping(Entity framework)

I have following tables
**Track Table**
+---------------+---------------+--------------------+
| TrackId | TrackName | Path |
+===============+===============+====================+
| 1 | jb | http://domain/1.mp3|
+---------------+---------------+--------------------+
| 2 | remix | http://domain/2.mp3|
+---------------+---------------+--------------------+
**Favorite Table**
+---------------+---------------+--------------------+
| favoriteId | ProfileId | TrackId |
+===============+===============+====================+
| 10 | 2130 | 1 |
+---------------+---------------+--------------------+
| 11 | 2132 | 2 |
+---------------+---------------+--------------------+
I need to select tracks into a model where I have to include a boolean field IsFavorite.
Right now my logic is as follows:
1.Select Tracks
2.Select all favorites by profile
3.Iterate to both list and fill field "IsFavorite"
Is there any better method that can work out the same?
Following is my current logic code
Var ActiveTracks = jukeboxTrackService.GetActiveTracks();
var UserFavorites = jukeboxTrackService.GetFavoritesByProfile(ProfileId);
foreach (var item in ActiveTracks )
{
foreach (var fav in UserFavorites)
{
if (item.JukeBoxTrackId == fav.TrackId)
{
item.IsFavorite = true;
}
}
}
return ActiveTracks ;
Thanks in advance.
(from tr in ActiveTracks
join usrfav in UserFavorites on tr.TrackId equals usr.TrackId into UsrTracks
from usrtr in UsrTracks.DefaultIfEmpty()
select new Track
{
IsFavorite = (usrfav.TrackId == null) ? false : true
}

What is the fastest way to compare two tables?

For example there are two tables with same schema, but different contents:
Table1
| field1 | field2 | field3 |
----------------------------------------
| 1 | aaaaa | 100 |
| 2 | bbbbb | 200 |
| 3 | ccccc | 300 |
| 4 | ddddd | 400 |
Table2
| field1 | field2 | field3 |
----------------------------------------
| 2 | xxxxx | 200 |
| 3 | ccccc | 999 |
| 4 | ddddd | 400 |
| 5 | eeeee | 500 |
The expected comparison result would be:
Deleted in B:
| 1 | aaaaa | 100 |
Mismatch:
Table1:| 2 | bbbbb | 200 |
Table2:| 2 | xxxxx | 200 |
Table1:| 3 | ccccc | 300 |
Table2:| 3 | ccccc | 999 |
Newly added in B
| 5 | eeeee | 500 |
Using C#, what is the fastest way to compare two tables?
Currently my implementation is:
Check if each row in table1 has an exact match in table2;
Check if each row in table2 has an exact match in table1.
The efficiency is n*n so for 100k rows it takes 20 mins to run on a server.
Many thanks
You can try something like this, should be quite fast:
class objType
{
public int Field1 { get; set; }
public string Field2 { get; set; }
public int Field3 { get; set; }
public bool AreEqual(object other)
{
var otherType = other as objType;
if (otherType == null)
return false;
return Field1 == otherType.Field1 && Field2 == otherType.Field2 && Field3 == otherType.Field3;
}
}
var tableOne = new objType[] {
new objType { Field1 = 1, Field2 = "aaaa", Field3 = 100 },
new objType { Field1 = 2, Field2 = "bbbb", Field3 = 200 },
new objType { Field1 = 3, Field2 = "cccc", Field3 = 300 },
new objType { Field1 = 4, Field2 = "dddd", Field3 = 400 }
};
var tableTwo = new objType[] {
new objType { Field1 = 2, Field2 = "xxxx", Field3 = 200 },
new objType { Field1 = 3, Field2 = "cccc", Field3 = 999 },
new objType { Field1 = 4, Field2 = "dddd", Field3 = 400 },
new objType { Field1 = 5, Field2 = "eeee", Field3 = 500 }
};
var originalIds = tableOne.ToDictionary(o => o.Field1, o => o);
var newIds = tableTwo.ToDictionary(o => o.Field1, o => o);
var deleted = new List<objType>();
var modified = new List<objType>();
foreach (var row in tableOne)
{
if(!newIds.ContainsKey(row.Field1))
deleted.Add(row);
else
{
var otherRow = newIds[row.Field1];
if (!otherRow.AreEqual(row))
{
modified.Add(row);
modified.Add(otherRow);
}
}
}
var added = tableTwo.Where(t => !originalIds.ContainsKey(t.Field1)).ToList();
Might be worth overriding Equals instead of AreEqual (or making AreEqual a helper method outside the class definition), but that depends on how your project is setup.

Unknown column 'Project1.ID' in 'where clause'

I have the following Tables
+--------+ +---------+ +---------+
| Class |1 N| Student |1 N| Test |
+--------+<--->+---------+<--->+---------+
|ClassID | |ClassID | |TestID |
|Desc | |StudentID| |StudentID|
+--------+ |Name | |Name |
+---------+ |Score |
+---------+
and I need to determine the total score of the first student of a class. there can be one, multiple or none existing tests for this student
So the result should look like
ClassDesc | StudentName | ScoreCount
----------+-------------+-----------
C1 | Max | 201
C2 | Tom | null
I have the following code
using (myEntities ctx = new myEntities())
{
var Reslut = ctx.Class
.Select(x => new
{
ClassDesc = x.Desc,
StudentName = x.Student.FirstOrDefault().Name,
ScoreCount = x.Student.FirstOrDefault().Test.Sum(t => t.Score) //Error here
}).ToList();
}
but it gives me the error
Unknown column 'Project1.ClassID' in 'where clause'
Try the following:
var result = (from cls in ctx.Class
join stdt in ctx.Student on cls.ClassID equals stdt.ClassID
select new
{
ClassDesc = cls.Desc,
StudentName = stdt.Name,
ScoreCount = stdt.Test.Sum(t => t.Score)
}).ToList();
Which should generate SQL similar to the following:
SELECT
Class.ClassID,
Class.[Desc],
Student.Name,
(
SELECT
SUM(Test.Score)
FROM dbo.Test
WHERE Student.StudentID = Test.StudentID
) AS ScoreSum
FROM dbo.Class
INNER JOIN dbo.Student ON Class.ClassID = Student.ClassID

Compare 2 Datatables to find difference/accuracy between the columns

So, I have 2 separate datatables, that look pretty identical but the values in their rows might be different for instance.
EDIT:
I can have an unique ID BY creating a temporary identity column that can be used as primary key if that will make it easier. so think of ID column as the primary key than.
Table A
ID | Name | Value1 | Value2 | Value3
-------------------------------------
1 | Bob | 50 | 150 | 35
2 | Bill | 55 | 47 | 98
3 | Pat | 10 | 15 | 45
4 | Cat | 70 | 150 | 35
Table B
ID | Name | Value1 | Value2 | Value3
-------------------------------------
1 | Bob | 30 | 34 | 67
2 | Bill | 55 | 47 | 98
3 | Pat | 100 | 15 | 45
4 | Cat | 70 | 100 | 20
Output Should be:
Table C
ID | Name | TableAValue1 | TableBValue1 | DiffValue1 ....Samething for Value2 .....samething for value3
------------------------------------------------------
1 | Bob | 50 | 30 | 20
2 | Bill | 55 | 55 | 0
3 | Pat | 10 | 100 | 90
4 | Cat | 70 | 70 | 0
I Know the tedious method to do this is by using a forloop and looping through each row comparing column rows with each other. But I am not sure how to create a new Table C with the results I want. Also I think there might be a simpler solution using Linq which I am not very familiar with but I would be interested in the solution with linq if it faster and less lines of code. I am looking for the most optimal/efficient way of going about this. as these datatables can be anywhere between 5,000 to 15,000+ rows in size so memory usage becomes an issue.
LINQ is not faster, at least not in general. But it can help to increase readability.
You can use Enumerable.Join which might be more efficient than nested loops, but you need a loop to fill your third table anyway. So the first two columns are the identifiers and the rest are the values:
var query = from r1 in table1.AsEnumerable()
join r2 in table2.AsEnumerable()
on new { ID = r1.Field<int>("ID"), Name = r1.Field<string>("Name") }
equals new { ID = r2.Field<int>("ID"), Name = r2.Field<string>("Name") }
select new { r1, r2 };
var columnsToCompare = table1.Columns.Cast<DataColumn>().Skip(2);
foreach (var rowInfo in query)
{
var row = table3.Rows.Add();
row.SetField("ID", rowInfo.r1.Field<int>("ID"));
row.SetField("Name", rowInfo.r1.Field<int>("Name"));
foreach (DataColumn col in columnsToCompare)
{
int val1 = rowInfo.r1.Field<int>(col.ColumnName);
int val2 = rowInfo.r2.Field<int>(col.ColumnName);
int diff = (int)Math.Abs(val1-val2);
row.SetField(col.ColumnName, diff);
}
}
var tableC = new DataTable();
tableC.Columns.Add(new DataColumn("ID"));
tableC.Columns.Add(new DataColumn("Name"));
tableC.Columns.Add(new DataColumn("TableAValue1"));
tableC.Columns.Add(new DataColumn("TableBValue1"));
tableC.Columns.Add(new DataColumn("DiffValue1"));
foreach (DataRow rowA in tableA.Rows)
{
foreach (DataRow rowB in tableB.Rows)
{
if (Convert.ToInt32(rowA["ID"]) == Convert.ToInt32(rowB["ID"]) &&
rowA["Name"].ToString() == rowB["Name"].ToString() &&
Convert.ToInt32(rowA["Value1"]) != Convert.ToInt32(rowB["Value1"]))
{
var newRow = tableC.NewRow();
newRow["ID"] = rowA["ID"];
newRow["Name"] = rowA["Name"];
newRow["TableAValue1"] = rowA["Value1"];
newRow["TableBValue1"] = rowB["Value1"];
newRow["DiffValue1"] = Convert.ToInt32(rowA["Value1"]) - Convert.ToInt32(rowB["Value1"]);
tableC.Rows.Add(newRow);
}
}
}
Using LINQ, create an anonymous type as follows
var joinedRows = (from rowA in TableA.AsEnumerable()
from rowB in TableB.AsEnumerable()
where rowA.Field<String>("Name") == rowB.Field<String>("Name")
select new
{
ID = rowA.Field<int>("ID"),
Name = rowA.Field<String>("Name"),
TableAValue1 = rowA.Field<int>("Value1"),
TableBValue1 = rowB.Field<int>("Value1"),
DiffValue1 = Math.Abs(rowA.Field<int>("Value1") - rowB.Field<int>("Value1")),
TableAValue2 = rowA.Field<int>("Value2"),
TableBValue2 = rowB.Field<int>("Value2"),
DiffValue2 = Math.Abs(rowA.Field<int>("Value2") - rowB.Field<int>("Value2")),
TableAValue3 = rowA.Field<int>("Value3"),
TableBValue3 = rowB.Field<int>("Value3"),
DiffValue3 = Math.Abs(rowA.Field<int>("Value3") - rowB.Field<int>("Value3"))
});
Table.AsEnumerable() will give you an IEnumerable(of DataRow)
row.Field will cast it to the correct type for you
You can now use the anonymous type of joinedRows and create your new dataTable from it
This uses a strategy similar to kippermand's, but will probably perform slightly better on large sets of data by avoiding the O(n²) complexity of checking every ID against every other ID, and by reusing the values extracted from the data table:
// joining by row location
var joinedTableRows =
dt1.AsEnumerable().Zip(dt2.AsEnumerable(),
(r1, r2) => new{r1, r2});
// or, joining by ID
var joinedTableRows2 =
dt1.AsEnumerable().Join(dt2.AsEnumerable(),
r => r.Field<int>("ID"),
r => r.Field<int>("ID"),
(r1, r2) => new{r1, r2});
var result =
from row in joinedTableRows
let rowA = row.r1
let rowB = row.r2
let tableAValue1 = rowA.Field<int>("Value1")
let tableBValue1 = rowB.Field<int>("Value1")
let tableAValue2 = rowA.Field<int>("Value2")
let tableBValue2 = rowB.Field<int>("Value2")
let tableAValue3 = rowA.Field<int>("Value3")
let tableBValue3 = rowB.Field<int>("Value3")
select new
{
ID = row.r1.Field<int>("ID"),
Name = row.r1.Field<string>("Name"),
TableAValue1 = tableAValue1,
TableBValue1 = tableBValue1,
DiffValue1 = Math.Abs(tableAValue1 - tableBValue1),
TableAValue2 = tableAValue2,
TableBValue2 = tableBValue2,
DiffValue2 = Math.Abs(tableAValue2 - tableBValue2),
TableAValue3 = tableAValue3,
TableBValue3 = tableBValue3,
DiffValue3 = Math.Abs(tableAValue3 - tableBValue3)
};
Depending on how your data needs to be consumed, you could either declare a class matching this anonymous type, and consume that directly (which is what I'd prefer), or you can create a DataTable from these objects, if you have to.

join without forgetting data that wasn't affected by the join

I have three tables I'm interested in, here's the relevant information:
+---------------+ +---------------+ +---------------+ +---------------+
| Class | | Tests | | StudentClasses| | Student |
+---------------+ +---------------+ +---------------+ +---------------+
| ClassID | | ClassID | | ClassID | | StudentID |
| | | WholeYearTest | | StudentID | | StudentYear |
| | | TestYear | | | | |
+---------------+ +---------------+ +---------------+ +---------------+
I have a variable login, which is a Student.
I am running this query in LINQ:
from tests in App.db.Tests
join studentClasses in App.db.StudentClasses on tests.ClassID equals studentClasses.ClassID
where (
(tests.WholeYearTest == true && tests.TestYear == login.StudentYear)
||
studentClasses.StudentID == login.StudentID
)
select tests;
Unfortunately, I'm only getting results which correspond to the condition studentClasses.StudentID == login.StudentID
What I want:
All tests that the student has to sit due to StudentYear being equal to TestYear and WholeYearTest being true
All tests that the student has to sit because one of the possibly multiple StudentClasses associated with them is listed under Tests.ClassID.
I think it's probably due to a blatant misunderstanding of how JOIN works, but I can't think of any other way to implement this. Can anyone put forward an implementation suggestion other than looping all tests? If not, I suppose I'll use a loop, but I'm sure there must be some way of implementing this in LINQ.
I think this will do what you want:
from tests in App.db.Tests
join studentClasses in App.db.StudentClasses
on new { tests.ClassID, login.StudentID }
equals new { studentClasses.ClassID, studentClasses.StudentID }
into gj
from subStudentClass in gj.DefaultIfEmpty()
where (
(tests.WholeYearTest == true && tests.TestYear == login.StudentYear)
||
(subStudentClass != null && subStudentClass.StudentID == login.StudentID)
)
select tests
The biggest difference is that there is now an outer join so that you can still find results where the join fails.
Also, the join is now by both ClassID and StudentID, so that you won't get matches for other students.
With the following test data:
var App = new { db = new {
Tests = new[] {
new Test { ClassID = 1, WholeYearTest = true, TestYear = 1999 },
new Test { ClassID = 2, WholeYearTest = true, TestYear = 1999 },
new Test { ClassID = 3, WholeYearTest = false, TestYear = 1999 },
new Test { ClassID = 4, WholeYearTest = false, TestYear = 1999 },
},
StudentClasses = new[] {
new StudentClass { ClassID = 1, StudentID = 1 },
new StudentClass { ClassID = 1, StudentID = 2 },
new StudentClass { ClassID = 4, StudentID = 1 },
new StudentClass { ClassID = 3, StudentID = 2 },
}
} };
var login = new { StudentID = 1, StudentYear = 1999 };
Console.WriteLine(string.Join(Environment.NewLine, (
//above query
).Select(x => string.Join(",", x.ClassID, x.WholeYearTest, x.TestYear))));
It prints
1,True,1999
2,True,1999
4,False,1999
A single inner join is not sufficient; your query actually needs to be executed with an inner join AND a union.
Give this a try - I didn't test it out because I figure you can do that :)
Note - you may need to insert a Distinct() in there as well.
var q1 = (from tests in App.db.Tests
join studentClasses in App.db.StudentClasses
on tests.ClassID equals studentClasses.ClassID
select tests).Concat(App.db.Tests.Where(t=>t.WholeYearTest && t.TestYear == login.StudentYear));

Categories

Resources