I am working with Entity Framework database first, and I have a table that stores historical values using baseline ids. We store parent/child links on this table using the baseline id. The following columns makeup this design:-
Id int (primary key - unique)
BaselineId int (not unique, but does uniquely identity revisions of the same item)
ParentBaselineId int nullable (refers to the baseline of the linked entity, no FK)
Latest bit (indicated that this is the most recent baseline in a series)
Example data for clarity
Id BaselineId ParentBaselineId Latest
1 1 NULL 0
2 1 NULL 1
3 2 1 0
4 2 1 1
This shows two items, each with two revisions. Baseline 1 is the parent of baseline 2.
My issue is that for the reasons listed below I lookup the next available baseline in C# and manually specify the BaselineId/ParentBaselineId to be saved. When two users trigger this method at the same time, they save the same Baseline ids, as the save does not complete before the second users looks up the next available baseline id.
The method can add many items at once that must be linked together by baseline ids
This must be a single SQL transaction so it can rollback completely on error
SQL trigger cannot be used to set the baselines because they are needed ahead of time to indicate the relationships
What measures can I take to ensure that the same baseline won't be used by two users running the method at the same time?
My C# looks something like this
using (var tx = new TransactionScope())
{
using (var context = new DbContext(connectionString))
{
int baseline = context.MyTable.Max(e => e.BaselineId);
context.MyTable.Add(new MyTable() {BaselineId = baseline + 1, Latest = true});
context.MyTable.Add(new MyTable() { BaselineId = baseline + 2, ParentBaselineId = baseline + 1, Latest = true });
context.SaveChanges();
}
tx.Complete();
}
Using #Steve Greene 's suggestion, I was able to use an SQL sequence. After creating a new sequence in my database and setting the start value to match my existing data, I updated my code to the following.
public long NextBaseline(DbContext context)
{
DataTable dt = new DataTable();
var conn = context.Database.Connection;
var connectionState = conn.State;
try
{
if (connectionState != ConnectionState.Open)
conn.Open();
using (var cmd = conn.CreateCommand())
{
cmd.CommandText = "SELECT NEXT VALUE FOR MySequence;";
using (var reader = cmd.ExecuteReader())
{
dt.Load(reader);
}
}
}
catch (Exception ex)
{
throw new HCSSException(ex.Message, ex);
}
finally
{
if (connectionState != ConnectionState.Open)
conn.Close();
}
return Convert.ToInt64(dt.AsEnumerable().First().ItemArray[0]);
}
public void Save()
{
using (var tx = new TransactionScope())
{
using (var context = new DbContext(connectionString))
{
var parent = new MyTable() { BaselineId = NextBaseline(context), Latest = true };
var child = new MyTable() { BaselineId = NextBaseline(context), ParentBaselineId = parent.BaselineId, Latest = true }
context.MyTable.Add(parent);
context.MyTable.Add(child);
context.SaveChanges();
}
tx.Complete();
}
}
Related
When using the C# code below to construct a DB2 SQL query the result set only has one row. If I manually construct the "IN" predicate inside the cmdTxt string using string.Join(",", ids) then all of the expected rows are returned. How can I return all of the expected rows using the db2Parameter object instead of building the query as a long string to be sent to the server?
public object[] GetResults(int[] ids)
{
var cmdTxt = "SELECT DISTINCT ID,COL2,COL3 FROM TABLE WHERE ID IN ( #ids ) ";
var db2Command = _DB2Connection.CreateCommand();
db2Command.CommandText = cmdTxt;
var db2Parameter = db2Command.CreateParameter();
db2Parameter.ArrayLength = ids.Length;
db2Parameter.DB2Type = DB2Type.DynArray;
db2Parameter.ParameterName = "#ids";
db2Parameter.Value = ids;
db2Command.Parameters.Add(db2Parameter);
var results = ExecuteQuery(db2Command);
return results.ToArray();
}
private object[] ExecuteQuery(DB2Command db2Command)
{
_DB2Connection.Open();
var resultList = new ArrayList();
var results = db2Command.ExecuteReader();
while (results.Read())
{
var values = new object[results.FieldCount];
results.GetValues(values);
resultList.Add(values);
}
results.Close();
_DB2Connection.Close();
return resultList.ToArray();
}
You cannot send in an array as a parameter. You would have to do something to build out a list of parameters, one for each of your values.
e.g.: SELECT DISTINCT ID,COL2,COL3 FROM TABLE WHERE ID IN ( #id1, #id2, ... #idN )
And then add the values to your parameter collection:
cmd.Parameters.Add("#id1", DB2Type.Integer).Value = your_val;
Additionally, there are a few things I would do to improve your code:
Use using statements around your DB2 objects. This will automatically dispose of the objects correctly when they go out of scope. If you don't do this, eventually you will run into errors. This should be done on DB2Connection, DB2Command, DB2Transaction, and DB2Reader objects especially.
I would recommend that you wrap queries in a transaction object, even for selects. With DB2 (and my experience is with z/OS mainframe, here... it might be different for AS/400), it writes one "accounting" record (basically the work that DB2 did) for each transaction. If you don't have an explicit transaction, DB2 will create one for you, and automatically commit after every statement, which adds up to a lot of backend records that could be combined.
My personal opinion would also be to create a .NET class to hold the data that you are getting back from the database. That would make it easier to work with using IntelliSense, among other things (because you would be able to auto-complete the property name, and .NET would know the type of the object). Right now, with the array of objects, if your column order or data type changes, it may be difficult to find/debug those usages throughout your code.
I've included a version of your code that I re-wrote that has some of these changes in it:
public List<ReturnClass> GetResults(int[] ids)
{
using (var conn = new DB2Connection())
{
conn.Open();
using (var trans = conn.BeginTransaction(IsolationLevel.ReadCommitted))
using (var cmd = conn.CreateCommand())
{
cmd.Transaction = trans;
var parms = new List<string>();
var idCount = 0;
foreach (var id in ids)
{
var parm = "#id" + idCount++;
parms.Add(parm);
cmd.Parameters.Add(parm, DB2Type.Integer).Value = id;
}
cmd.CommandText = "SELECT DISTINCT ID,COL2,COL3 FROM TABLE WHERE ID IN ( " + string.Join(",", parms) + " ) ";
var resultList = new List<ReturnClass>();
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
var values = new ReturnClass();
values.Id = (int)reader["ID"];
values.Col1 = reader["COL1"].ToString();
values.Col2 = reader["COL2"].ToString();
resultList.Add(values);
}
}
return resultList;
}
}
}
public class ReturnClass
{
public int Id;
public string Col1;
public string Col2;
}
Try changing from:
db2Parameter.DB2Type = DB2Type.DynArray;
to:
db2Parameter.DB2Type = DB2Type.Integer;
This is based on the example given here
I'm doing a software which can manage electronics component quantity. My software is linked to a first DB (T_importReel) that will have all the new reels component when the factory receive them. The machine that uses component is counting how many it has used in each reels and marks the count in its own DB (SiplaceSetupCenter). I'm using an Unique ID for each reel so T_importReel and SiplaceSetupCenter are using that Unique ID to differentiate each bobine. Basically, I have in the two DB two columns that are the same.
Now I would like to link the two DB with a simple SQL request. This is supposed to be simple : for every Unique ID where the quantity is different between the two DB, T_importReel changes its quantity for the Unique ID concerned.
But when I've tried to do so, my soft didn't access DB_center...
How can I enable my soft to access SetUpCenter? What do I do to update T_importreels?
Thank you in advance.
private void BT_croiserlesdonnées_Click(object sender, EventArgs e)
{
SqlConnection con = new SqlConnection("Server=SIPLACESERVER;Database=SiplaceSetupCenter;User=**;Password=****;");
con.Open();
// using (SqlCommand command = new SqlCommand(
// "SELECT packagingunitid,Quantity FROM PackagingUnit",
// con))
//using (SqlDataReader reader = command.ExecuteReader())
// {
// while (reader.Read())
// {
// for (int i = 0; i < reader.FieldCount; i++)
// {
// Console.WriteLine(reader.GetValue(i));
// }
// Console.WriteLine();
// }
// }
string queryString =
"Select Quantity FROM PackagingUnit, T_ImportReels WHERE PackagingUnit.id = T_ImportReels.id";
SqlCommand command = new SqlCommand(
queryString, con);
SqlDataReader reader = command.ExecuteReader();
try
{
while (reader.Read())
{
Console.WriteLine(String.Format("{0}, {1}",
reader[0], reader[1]));
}
}
finally
{
// Always call Close when done reading.
reader.Close();
}
//_siplaceSetupCenter = new SiplaceSetupCenterEntities();
//string queryString = #"SELECT * FROM PackagingUnit";
//using (SiplaceSetupCenterEntities context = new SiplaceSetupCenterEntities())
//{
// //ObjectQuery<PackagingUnit> contactQuery =_siplaceSetupCenter.CreateQuery<PackagingUnit>(queryString);
// // new ObjectParameter("fn", "Frances"));
// Console.WriteLine("on y go"); Console.WriteLine("on y go"); Console.WriteLine("on y go"); Console.WriteLine("on y go");
// var tableresult = _siplaceSetupCenter.PackagingUnit.FirstOrDefault(id => id.PackagingUnitId == "100717132736");
//var contactQuery = from contact in context.PackagingUnit
// where contact.PackagingUnitId == "200717100643"
// select contact;
//// Iterate through the collection of Contact items.
//foreach (PackagingUnit result in contactQuery)
// Console.WriteLine("First Name: {0}, Last Name: {1}",result.PackagingUnitId, result.Quantity);
//Console.WriteLine(table_result.Count); Console.WriteLine(table_result.Count); Console.WriteLine(table_result.Count); Console.WriteLine(table_result.Count);
//foreach (var value in table_result)
//{
// Console.WriteLine(value); Console.WriteLine(value); Console.WriteLine(value); Console.WriteLine(value);
// Console.WriteLine("a"); Console.WriteLine("a"); Console.WriteLine("a"); Console.WriteLine("a");
//}
//string dataid = tableresult.PackagingUnitId;
//Console.WriteLine(dataid);
}
}
I
I don't know how it could help you?
You can make two connections that each one connects to each database then select your Data from two Db. In this situation you have 2 DataTable/ SqlDataReader objects. Now You can compare these Datatables/SqlDataReader row by row with each others or combine them into a single datatable/SqlDataReader or update each one base another.
Sql Server's name should be sth like this SERVERNAME\INSTANCENAME. But your server name is this: SIPLACESERVER/SIPLACE_2012EX.
If you use "\" character in your server name, in connection string change it into "\\".
i mean change SERVERNAME\INSTANCENAME into SERVERNAME\\INSTANCENAME.
I am trying to insert a text file formatted in C Sharp to a Microsoft SQL server. I have 2 tables Transaction and TMatch in which I want to populate the data. 4 attributes each. I have created 2 classes for each. I am aware of how to input data manually into the database through the .Add() and .SaveChanges().
Here is what I have so far:
//Database insertions
TTransaction txn = new TTransaction();
**txn.Amount = 56; //I want a variable used below (AMOUNT) to go into amount.
txn.TRN = "sdfgsdfg";** //(TxnNo) to go into TRN
ScotiaNYAEntities context = new ScotiaNYAEntities();
context.TTransactions.Add(txn);
context.SaveChanges();
Traversing the text file using a while loop.
{
if (line.Contains("AMOUNT:")) //Look where to end for Transaction Text
{
// For Amount
IsAmount=true;
if(IsAmount)
{
Amount = line.Replace("AMOUNT:", String.Empty).Trim();
Console.WriteLine("AMOUNT: ********");
Console.WriteLine(Amount);
}
}..............................................
I am not sure how to reference a variable instead of just values.
Thank you.
leap of faith but you could have something like this
using (ScotiaNYAEntities context = new ScotiaNYAEntities())
{
foreach (string line in File.ReadLines(pathToFile))
{
if (line.Contains("AMOUNT:"))
{
if (IsAmount)
{
string amount = line.Replace("AMOUNT:", string.Empty).Trim();
TTransaction txn = new TTransaction();
txn.Amount = amount;
txn.TRN = "sdfgsdfg";
context.TTransactions.Add(txn);
}
}
}
context.SaveChanges();
}
This is what I did:
In the for loop for reading the file line by line
String TxnLOC = null;
IsTransactionLocation= false;
if (line.Contains("TRANSACTION LOC:"))
{
IsTransactionLocation = true;
if (IsTransactionLocation)
{
TxnLOC = line.Replace("TRANSACTION LOC:", String.Empty).Trim();
Console.WriteLine("The Transaction Location: ********");
Console.WriteLine(TxnLOC);
//Database insertion fot TTransaction Table
TTransaction txn = new TTransaction();
txn.TRN = txnNo;
txn.Amount = Convert.ToDecimal(Amount);
txn.TransactionLocation = TxnLOC;
context.TTransaction.Add(txn); //Adding to the database
context.SaveChanges();
IsTxnSection = false;//For 1 to many relationship
}
}
I have been struggling with this problem for some time now and am not able to resolve this. Have read multiple posts and tried several different solutions but nothing has worked. Now I feel like I have come to a stop and really need help with this.
I am using EF 5+ with an DB first and edmx file. I have 3 different tables in my DB:
1. Settlement
2. Cost
3. Shift
Settlement has a collection of both Cost and Shift (with a link table) connected by Association in my edmx file.
I need to insert a new Settlement in my db with a reference to an already existing Cost and Shift collections.
Shifts and Costs included in my Settlement entity I am trying to insert contains all there related data and none of those are modified in any way (same as I retrieved from db).
Here in my method of inserting the entity into my db.
public bool CreateSettlement(Settlement settlement)
{
bool _success;
var _context = new EtaxiEnteties();// ObjectFactory.Get<IETaxiEntitiesContext>();
try
{
var _newSettlement = new Settlement
{
CreateDate = settlement.CreateDate,
Driver = settlement.Driver,
DriverID = settlement.DriverID,
Car = settlement.Car,
CarID = settlement.CarID,
DocPath = settlement.DocPath
};
foreach (var _shift in settlement.Shifts)
{
//var _sh = _context.Shifts.Find(_shift.ShiftID);
//_context.Entry(_sh).CurrentValues.SetValues(_shift);
_newSettlement.Shifts.Add(_shift);
}
foreach (var _cost in settlement.Costs)
{
////var _sh = _context.Costs.Find(_cost.CostID);
////_context.Entry(_sh).CurrentValues.SetValues(_cost);
_newSettlement.Costs.Add(_cost);
}
_context.Settlements.Add(_newSettlement);
_success = _context.SaveChanges() > 0;
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}
return _success;
}
Any help on the issue would be MUCH appreciated.
Here is how I am adding the Cost and Shift to my collection:
I create a Settlement in page:
_settlement = new Settlement
{
CreateDate = DateTime.Now,
Driver = _driver,
DriverID = _driver.DriverID,
Car = _car,
CarID = _car.CarID,
DocPath = _path
};
then when I create a pdf file with selected rows from 2 separated grid views:
foreach (GridDataItem _selectedRow in gwShifts.MasterTableView.Items)
{
if (_selectedRow.Selected)
{
var _shift =
_diaryRepository.GetShiftByID((int) _selectedRow.GetDataKeyValue("ShiftID")).FirstOrDefault();
if (_shift != null)
{
_settlement.Shifts.Add(_shift);
_settlementData.Shifts.Add(_shift);
_settlementData.SplitPercentace = GetTemplateValue(_selectedRow, "lblSplit");
_settlementData.SettlementAmount = GetTemplateValue(_selectedRow, "lblSettlementAmount");
if (_settlementData.Shifts != null)
{
_tableShifts.AddCell(
new PdfPCell(
new Phrase(
_settlementData.Shifts.FirstOrDefault().ShiftDate.ToShortDateString(),
_bodyFont)) {Border = 0});
_tableShifts.AddCell(
new PdfPCell(
new Phrase(
string.Format("{0:c}", _settlementData.Shifts.FirstOrDefault().GrossAmount),
_bodyFont)) {Border = 0});
_tableShifts.AddCell(
new PdfPCell(
new Phrase(
string.Format("{0:c}", _settlementData.Shifts.FirstOrDefault().MoneyAmount),
_bodyFont)) {Border = 0});
_tableShifts.AddCell(new PdfPCell(new Phrase(_settlementData.SplitPercentace, _bodyFont))
{Border = 0});
_tableShifts.AddCell(
new PdfPCell(new Phrase(_settlementData.SettlementAmount, _boldTableFont))
{Border = 0});
_totalAmount.AddRange(new[]
{
Convert.ToInt32(
_settlementData.SettlementAmount.Replace(".", "").
Replace(",", "").Replace("kr", ""))
});
_settlementData.Shifts.Remove(_shift);
}
}
}
}
var _summaryCell =
new PdfPCell(new Phrase("Upphæð: " + string.Format("{0:c}", _totalAmount.Sum()), _boldTableFont))
{
Border = 0,
Colspan = 5,
HorizontalAlignment = Element.ALIGN_RIGHT,
Padding = 5,
BorderWidthTop = 1
};
_tableShifts.AddCell(_summaryCell);
if (_totalAmount.Count != 0)
_totalAmount.Clear();
}
there you see how I add the Shift to this Settlement entity:
var _shift =
_diaryRepository.GetShiftByID((int) _selectedRow.GetDataKeyValue("ShiftID")).FirstOrDefault();
if (_shift != null)
{
_settlement.Shifts.Add(_shift);
then I send this to the reporistory (see method above)
if(_driverRepository.CreateSettlement(_settlement))
{
SetMessage("Uppgjör hefur verið skapað og sent bílstjóra ef e-póstur er skráður á viðkomandi bílstjóra.", "Uppgjör skapað");
pnlSettlement.Visible = false;
pnlDocCreation.Visible = false;
pnlResult.Visible = false;
}
I also tried to simply add param settlement directly to the context but got similar error.
I think this has to do with how you're populating the Shifts and Costs collection. You're trying to add already created records (i.e. they already have their primary key values set) to be saved with the new Settlement entity, but I believe that Entity Framework isn't trying to create new ones that are linked to your new Settlement entity but rather save them to the table as is. In such a case you would indeed have a situation where multiple entities have the same primary key.
I would try the following (I'll show you using the Shifts loop only, but you should be able to apply it to the Costs loop as well):
foreach (var _shift in settlement.Shifts)
{
var newShift = new Shift { /*Copy all of the values from _shift here*/ };
_newSettlement.Shifts.Add(_shift);
context.Shifts.Add(newShift);
}
If that doesn't work I would suggest debugging Costs and Shifts to make sure that you don't have any duplicates in those collections.
If you don't want new Shifts & Costs then I can only assume you need to repoint the existing ones to the new Settlement
foreach (var shift in settlement.Shifts)
{
//either
shift.Settlement = newSettlement;
//or
shift.SettlementId = newSettlement.SettlementId;
//depending on your object model
}
I've just realised that I have misunderstood the question. There are 2 additional tables not shown in the diagram (Costs & Shifts). The problem is trying to create SettlementCost and SettlementShift entities that connect Settlement to Costs\Shifts.
OK, came up with a "ugly" solution on this..but it is resolved.
Changed the Model to be one(Settlement) -> many(Shift or Cost) relationship.
I create a new Settlement and save this one to the DB.
Retrieve each Shift and Cost from the DB update SettlementID on each and save these to the DB.
try
{
var _newSettlement = new Settlement
{
CreateDate = settlement.CreateDate,
DriverID = settlement.DriverID,
CarID = settlement.CarID,
DocPath = settlement.DocPath
};
Add(_newSettlement);
_success = SaveWithSuccess() > 0;
var _settlement = GetAll().FirstOrDefault(x => x.SettlementID == _newSettlement.SettlementID);
if (_success)
{
foreach (var _shift in settlement.Shifts)
{
var _sh = _diaryRepository.GetShiftByID(_shift.ShiftID).FirstOrDefault();
_sh.SettlementID = _settlement.SettlementID;
_diaryRepository.UpdateShift(_sh);
}
foreach (var _cost in settlement.Costs)
{
var _ch = _costRepository.GetCostByID(_cost.CostID);
_ch.SettlementID = _settlement.SettlementID;
_costRepository.UpdateCost(_ch);
}
}
}
not a pretty one, but it resolves the problem.
I am not concerned about the DB request load..it will not be that high in this case.
I would think there is a nicer solution to this but I was not able to find it at this time.
Thanks for all your efforts to help :)
I have two functions that each return the same list of objects. But, the one that uses TSQL is much faster than the one using Entity Framework and I do not understand why one would be faster than the other. Is it possible to modify my EF function to work as fast as the TSQL one?
Any help will be appreciated. My code is below:
TSQL:
public static List<ChartHist> ListHistory_PureSQL()
{
List<DataRow> listDataRow = null;
string srtQry = #"Select LoginHistoryID,
LoginDuration as LoginDuration_Pass,
0 as LoginDuration_Fail,
LoginDateTime,
LoginLocationID,
LoginUserEmailID,
LoginApplicationID,
LoginEnvironmentID,
ScriptFrequency,
LoginStatus,
Reason
From LoginHistory
Where LoginStatus = 'Pass'
UNION
Select LoginHistoryID,
0 as LoginDuration_Pass,
LoginDuration as LoginDuration_Fail,
LoginDateTime,
LoginLocationID,
LoginUserEmailID,
LoginApplicationID,
LoginEnvironmentID,
ScriptFrequency,
LoginStatus,
Reason
From LoginHistory
Where LoginStatus = 'Fail'";
using (SqlConnection conn = new SqlConnection(Settings.ConnectionString))
{
using (SqlCommand objCommand = new SqlCommand(srtQry, conn))
{
objCommand.CommandType = CommandType.Text;
DataTable dt = new DataTable();
SqlDataAdapter adp = new SqlDataAdapter(objCommand);
conn.Open();
adp.Fill(dt);
if (dt != null)
{
listDataRow = dt.AsEnumerable().ToList();
}
}
}
var listChartHist = (from p in listDataRow
select new ChartHist
{
LoginHistoryID = p.Field<Int32>("LoginHistoryID"),
LoginDuration_Pass = p.Field<Int32>("LoginDuration_Pass"),
LoginDuration_Fail = p.Field<Int32>("LoginDuration_Fail"),
LoginDateTime = p.Field<DateTime>("LoginDateTime"),
LoginLocationID = p.Field<Int32>("LoginLocationID"),
LoginUserEmailID = p.Field<Int32>("LoginUserEmailID"),
LoginApplicationID = p.Field<Int32>("LoginApplicationID"),
LoginEnvironmentID = p.Field<Int32>("LoginEnvironmentID"),
ScriptFrequency = p.Field<Int32>("ScriptFrequency"),
LoginStatus = p.Field<String>("LoginStatus"),
Reason = p.Field<String>("Reason")
}).ToList();
return listChartHist;
}
EF:
public static List<ChartHist> ListHistory()
{
using (var db = new LatencyDBContext())
{
var loginHist = (from hist in db.LoginHistories
select new { LoginHistory = hist }).ToList();
//PUT LOGIN HISTORY RECORDS INTO A LOCAL LIST
var listHistory = new List<ChartHist>();
foreach (var item in loginHist)
{
var localHistData = new ChartHist();
localHistData.LoginHistoryID = item.LoginHistory.LoginHistoryID;
//split up the duration for pass and fail values
if (item.LoginHistory.LoginStatus.ToUpper() == "PASS")
{
localHistData.LoginDuration_Pass = Convert.ToDouble(item.LoginHistory.LoginDuration);
localHistData.LoginDuration_Fail = 0;
}
else if (item.LoginHistory.LoginStatus.ToUpper() == "FAIL")
{
localHistData.LoginDuration_Pass = 0;
localHistData.LoginDuration_Fail = Convert.ToDouble(item.LoginHistory.LoginDuration);
}
localHistData.LoginDateTime = item.LoginHistory.LoginDateTime;
localHistData.LoginLocationID = item.LoginHistory.LoginLocationID;
localHistData.LoginUserEmailID = item.LoginHistory.LoginUserEmailID;
localHistData.LoginApplicationID = item.LoginHistory.LoginApplicationID;
localHistData.LoginEnvironmentID = item.LoginHistory.LoginEnvironmentID;
localHistData.LoginStatus = item.LoginHistory.LoginStatus;
localHistData.Reason = item.LoginHistory.Reason;
localHistData.ScriptFrequency = item.LoginHistory.ScriptFrequency;
listHistory.Add(localHistData);
}
return listHistory;
}
}
Of course EF will take longer to execute than a plain old SQL query, and there's very little that you can do about it (except write the most optimal LINQ queries that you can).
There's a very simple reason why this is so. Running a direct SQL command will just send back the data, with no muss and no fuss attached to it, waiting for you to do the data manipulations to get it to the point where it fits nicely into whatever data structure you want it in. Running EF, on the other hand, means that not only does it run the SQL command, but it massages the data for you into objects that you can manipulate right away. That extra action of going through ADO.NET and converting the data into the objects automatically means that it will take longer than just doing the plain SQL query.
On the flip side of that coin, however, EF does provide a very nice and simple way to debug and solve whatever problems you might have from a specific query/function (like by any exceptions thrown).
I can't performance test this, but try this solution instead before you remove EF entirely:
var loginHist = db.LoginHistories.Where(item => item.LoginStatus.ToUpper() == "PASS" || item.LoginStatus.ToUpper() == "FAIL")
.Select(item => new ChartHist()
{
LoginHistoryID = item.LoginHistoryID,
LoginDuration_Pass = item.LoginStatus.ToUpper() == "PASS" ? Convert.ToDouble(item.LoginDuration) : 0,
LoginDuration_Fail = item.LoginStatus.ToUpper() == "FAIL" ? Convert.ToDouble(item.LoginDuration) : 0,
LoginDateTime = item.LoginDateTime,
LoginLocationID = item.LoginLocationID,
LoginUserEmailID = item.LoginUserEmailID,
LoginApplicationID = item.LoginApplicationID,
LoginEnvironmentID = item.LoginEnvironmentID,
LoginStatus = item.LoginStatus,
Reason = item.Reason,
ScriptFrequency = item.ScriptFrequency,
});
return loginHist.ToList();
This is the "correct" way to populate a new object from a select. It will only retrieve the data you care about, and will put it directly into the object, rather than converting it into an object and then converting it again, from one object to another.
Note: I prefer the functional calls to the from / select form, but it'd be correct either way.