Writing code that can work with multiple database objects - c#

I basically have two different databases that have different tables in them, but they both have "Table1" with the exact same structure.
var db = new ???;
if(mode == "PRODUCTION"){
db = new Database1("Connection string for Database1");
}
else{
db = new Database2("Connection string for Database2");
}
var result = db.Table1.Where(a=>a.Value==1).First();
How can I make the above work so I can assign "result" from two different databases (depending on "mode") without writing two different definitions for "result"?

Store the connection strings separately in your config file. You can then swap the connection strings at runtime to point the database object to the right database:
if(mode == "PRODUCTION")
{
db = new Database(ConfigurationManager.ConnectionStrings["production-key"]);
}
else
{
db = new Database(ConfigurationManager.ConnectionStrings["dev-key"]);
}

Justin's point is a good one, here is another -
Why do this at the db level?
Something like this would work
var table;
if(mode == "PRODUCTION"){
db = new Database1("Connection string for Database1");
table = db.table1;
}
else{
db = new Database1("Connection string for Database2");
table = db.table1
}
var result = table.Where(a=>a.Value==1).First();
If you don't have the same exact db then you would need to do something like this (you could also add an interface to db1 and db2 to return commonElements -- as you wish.
class commonElements {
/// some code
}
public commoneElements GetCommon(Database1 inDB1) {
/// some code
}
public commoneElements GetCommon(Database2 inDB2) {
/// some code
}
commonElements common;
if(mode == "PRODUCTION"){
db1 = new Database1("Connection string for Database1");
common = GetCommon(db1);
}
else{
db2 = new Database2("Connection string for Database2");
common = GetCommon(db2);
}
var result = common.Where(a=>a.Value==1).First();

Related

Getting double data in DB

I pull student data from 2 databases. 1 from an online SOAP API which can handle async calls and 1 from a local DB with an older services that doesnt support async.
I compare these databases and write the differences in a local sqlDB through EF.
Problem:
I get double entries in my EF DB. He puts the correct data and amount in arrays inside the method, but it looks like once he hits the db.savechanges() he jumps back up a few line and saves again.
I don't even know where this extra thread comes from.
Some code might be still there from numerous tries to solve it. For instance I tried with addrange but I get an error when he tries to add the FullVarianceList.
public async Task<bool> FullStudentCompare(string date) //format DD/MM/YYYY
{
try
{
//DB context
using (var db = new SchoolDbContext())
{
//GET DATA
//SMT (async)
List<SmtStudent> smtStdudentList = await GetAllSmartschoolStudents();
//Wisa (sync)
//on date, or if emty on current systemdate
List<WisaStudent> wisaList;
if (date == "")
{
wisaList = GetWisaStudentData(DateTime.Now.ToShortDateString());
}
else
{
wisaList = GetWisaStudentData(date);
}
//Flags and props needed for DB entry after compare
bool existsInLocalDb = false;
List<Variance> vList = new List<Variance>();
//Full list to add to DB outside foreach
List<Variance> fullVarianceList = new List<Variance>();
//Full List of new Students to write to DB outside foreach
List<DbStudent> fullStudentList = new List<DbStudent>();
//Compare lists
foreach (WisaStudent wstd in wisaList)
{
//determine correct classCode
string klasCode;
if (wstd.klasgroep.Trim() == "Klasgroep 00")
{
klasCode = wstd.klas.Trim();
}
else
{
klasCode = wstd.klasgroep.Trim();
}
//Create SmtStudent object for compare
SmtStudent tempStd = new SmtStudent(true,
wstd.voornaam.Trim(),
wstd.naam.Trim(),
wstd.stamboeknummer.Trim(),
wstd.geslacht.Trim(),
wstd.geboortedatum.Trim(),
wstd.straat.Trim(),
wstd.huisnummer.Trim(),
wstd.busnummer.Trim(),
wstd.postcode.Trim(),
wstd.gemeente.Trim(),
wstd.emailadres.Trim(),
wstd.GSM_nummer.Trim(),
wstd.levensbeschouwing.Trim(),
wstd.coaccountmoedervoornaam.Trim(),
wstd.coaccountmoedernaam.Trim(),
wstd.coaccountmoederemailadres.Trim(),
wstd.coaccountmoederGSM_nummer.Trim(),
wstd.coaccountvadervoornaam.Trim(),
wstd.coaccountvadernaam.Trim(),
wstd.coaccountvaderemailadres.Trim(),
wstd.coaccountvaderGSM_nummer.Trim(),
klasCode,
wstd.nationaliteit,
wstd.geboorteGemeente,
wstd.geboorteLand
);
//Find matching SmtStudent
SmtStudent smtStd = smtStdudentList.Find(i => i.Internnummer == wstd.stamboeknummer);
//Find matching Std in local DB
DbStudent dbStd = await db.Students.Where(i => i.Stamboeknummer == wstd.stamboeknummer).FirstOrDefaultAsync();
//if none exists in the local DB create an entity to update and write to DB
if (dbStd == null)
{
dbStd = new DbStudent(wstd.voornaam.Trim(),
wstd.naam.Trim(),
wstd.stamboeknummer.Trim(),
wstd.geslacht.Trim(),
wstd.geboortedatum.Trim(),
wstd.straat.Trim(),
wstd.huisnummer.Trim(),
wstd.busnummer.Trim(),
wstd.postcode.Trim(),
wstd.gemeente.Trim(),
wstd.emailadres.Trim(),
wstd.GSM_nummer.Trim(),
wstd.levensbeschouwing.Trim(),
wstd.coaccountmoedervoornaam.Trim(),
wstd.coaccountmoedernaam.Trim(),
wstd.coaccountmoederemailadres.Trim(),
wstd.coaccountmoederGSM_nummer.Trim(),
wstd.coaccountvadervoornaam.Trim(),
wstd.coaccountvadernaam.Trim(),
wstd.coaccountvaderemailadres.Trim(),
wstd.coaccountvaderGSM_nummer.Trim(),
klasCode,
wstd.loopbaanDatum,
wstd.nationaliteit,
wstd.geboorteGemeente,
wstd.geboorteLand
);
db.Students.Add(dbStd);
fullStudentList.Add(dbStd);
}
else
{
existsInLocalDb = true;
}
if (smtStd == null)
{
//Std doesn't exist in Smt -> New student
dbStd.IsNewStudent = true;
dbStd.ClassMovement = true;
//remove from wisaList
wisaList.Remove(wstd);
}
else
{
//clear vlist from previous iterations
vList.Clear();
//get all properties on the obj, cycle through them and find differences
PropertyInfo[] props = smtStd.GetType().GetProperties();
vList.AddRange(props.Select(f => new Variance
{
Property = f.Name,
ValueA = f.GetValue(smtStd),
ValueB = f.GetValue(tempStd),
Checked = false
})
.Where(v => !v.ValueA.Equals(v.ValueB) && v.ValueB != null)
.ToList());
//If the users allrdy exists in LocalDb delete all previously recorded variances
if (existsInLocalDb)
{
if (db.Variances.Where(j => j.Student.StudentId.Equals(dbStd.StudentId)).FirstOrDefault() != null)
{ //if the student allready exists we will recreate the variancelist, hence deleting all current items first
List<Variance> existingList = db.Variances.Where(j => j.Student.StudentId.Equals(dbStd.StudentId)).ToList();
foreach (Variance v in existingList)
{
db.Variances.Remove(v);
}
}
}
//Add new variances if vList is not empty
if (vList.Count > 0)
{
//Check if KlasCode is a variance -> set classmovement to true
if (vList.Where(i => i.Property == "KlasCode").FirstOrDefault() != null)
{
dbStd.ClassMovement = true;
}
else
{
dbStd.ClassMovement = false;
}
//add the StudentObject to the variance to link them 1-many
foreach (Variance v in vList)
{
v.Student = dbStd;
fullVarianceList.Add(v);
db.Variances.Add(v);
}
}
}
}
//add the full lists of variances and new students to DB
//db.Variances.AddRange(fullVarianceList);
//db.Students.AddRange(fullStudentList);
db.SaveChanges();
return true;
}
}
catch(Exception ex)
{
return false;
}
}
A couple of things:
It is important to understand that EF uses a unit of work pattern where none of the changes to the entities are persisted until SaveChanges is called which explains the "once he hits the db.Savechanges() he jumps back up" phenomenon.
When you have a 1 to many relationsship and you assign a collection of entities to a navigation property on another entity and then add that parent entity to the DbContext, EF marks those child entities to be added too. In your case dbStd is added at the line "db.Students.Add(dbStd);" and at the line "v.Student = dbStd;". This is most likely what is causing your duplicates.

Insert into database using Entity Framework from C#

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
}
}

Insert data into database using LINQ

I wrote a very simple method. It saves data from class DayWeather to the database. Method checks if line with that day exist in table and update her or create a new line.
I am doing it by adding new class for LINQ and move table from Server Inspector to the constructor. It generate new class WeatherTBL.
Method itself looks like this:
public static void SaveDayWeather(DayWeather day)
{
using (DataClassesDataContext db = new DataClassesDataContext())
{
var existingDay =
(from d in db.WeatherTBL
where d.DateTime.ToString() == day.Date.ToString()
select d).SingleOrDefault<WeatherTBL>();
if (existingDay != null)
{
existingDay.Temp = day.Temp;
existingDay.WindSpeed = day.WindSpeed;
existingDay.Pressure = day.Pressure;
existingDay.Humidity = day.Humidity;
existingDay.Cloudiness = day.Cloudiness;
existingDay.TypeRecip = day.TypeRecip;
db.SubmitChanges();
}
else
{
WeatherTBL newDay = new WeatherTBL();
newDay.DateTime = day.Date;
newDay.Temp = day.Temp;
newDay.WindSpeed = day.WindSpeed;
newDay.Pressure = day.Pressure;
newDay.Humidity = day.Humidity;
newDay.Cloudiness = day.Cloudiness;
newDay.TypeRecip = day.TypeRecip;
db.WeatherTBL.InsertOnSubmit(newDay);
db.SubmitChanges();
}
}
}
When I tried to call him from UnitTest project:
[TestMethod]
public void TestDataAccess()
{
DayWeather day = new DayWeather(DateTime.Now);
DataAccessClass.SaveDayWeather(day);
}
It write, that test has passed successfully. But if look into table, it has`t chanched.
No error messages shows. Does anyone know whats the problem?
P.S. Sorry for my bad English.
UDP
Problem was in that:
"...db maybe copied to the debug or release folder at every build, overwriting your modified one". Thanks #Silvermind
I wrote simple method to save employee details into Database.
private void AddNewEmployee()
{
using (DataContext objDataContext = new DataContext())
{
Employee objEmp = new Employee();
// fields to be insert
objEmp.EmployeeName = "John";
objEmp.EmployeeAge = 21;
objEmp.EmployeeDesc = "Designer";
objEmp.EmployeeAddress = "Northampton";
objDataContext.Employees.InsertOnSubmit(objEmp);
// executes the commands to implement the changes to the database
objDataContext.SubmitChanges();
}
}
Please try with lambda expression. In your code, var existingDay is of type IQueryable
In order to insert or update, you need a variable var existingDay of WeatherTBL type.
Hence try using below..
var existingDay =
db.WeatherTBL.SingleOrDefault(d => d.DateTime.Equals(day.Date.ToString()));
if(existingDay != null)
{
//so on...
}
Hope it should work..
Linq to SQL
Detail tc = new Detail();
tc.Name = txtName.Text;
tc.Contact = "92"+txtMobile.Text;
tc.Segment = txtSegment.Text;
var datetime = DateTime.Now;
tc.Datetime = datetime;
tc.RaisedBy = Global.Username;
dc.Details.InsertOnSubmit(tc);
try
{
dc.SubmitChanges();
MessageBox.Show("Record inserted successfully!");
txtName.Text = "";
txtSegment.Text = "";
txtMobile.Text = "";
}
catch (Exception ex)
{
MessageBox.Show("Record inserted Failed!");
}

Convert Function Based on TSQL with Function Based with Entity Framework

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.

How to move records from one table to another in linq

I have a ProductLevel table in an SQL database. It contains the products for a store. I want to copy these records into a ProductLevelDaily table at the time the user logs onto the hand held device in the morning.
As they scan items the bool goes from false to true so at anytime they can see what items are left to scan/check.
From the mobile device I pass the siteID and date to the server:
int userID = int.Parse(oWebRequest.requestData[5]); and a few other things
IEnumerable<dProductLevelDaily> plditems
= DSOLDAL.CheckProductDailyLevelbySiteCount(siteID, currentDate);
This checks if there are any records already moved into this table for this store. Being the first time this table should be empty or contain no records for this store on this date.
if (plditems.Count() == 0) // is 0
{
IEnumerable<dProductLevel> ppitems = DSOLDAL.GetProductsbySite(siteID);
// this gets the products for this store
if (ppitems.Count() > 0)
{
dProduct pi = new dProduct();
foreach (dProductLevel pl in ppitems)
{
// get the product
pi = DSOLDAL.getProductByID(pl.productID, companyID);
dProductLevelDaily pld = new dProductLevelDaily();
pld.guid = Guid.NewGuid();
pld.siteID = siteID;
pld.statusID = 1;
pld.companyID = companyID;
pld.counted = false;
pld.createDate = DateTime.Now;
pld.createUser = userID;
pld.productID = pl.productID;
pld.name = "1000"; // pi.name;
pld.description = "desc"; // pi.description;
DSOLDAL.insertProductLevelDailyBySite(pld);
}
}
}
On the PDA the weberequest response returns NULL
I can't see what the problem is and why it wont work.
The insert is in DSOLDAL:
public static void insertProductLevelDailyBySite(dProductLevelDaily pld)
{
dSOLDataContext dc = new dSOLDataContext();
try
{
dc.dProductLevelDailies.InsertOnSubmit(pld);
// dProductLevelDailies.Attach(pld, true);
dc.SubmitChanges();
}
catch (Exception exc)
{
throw new Exception(getExceptionMessage(exc.Message));
}
finally
{
dc = null;
}
}
This code works until I put the foreach loop inside with the insert
IEnumerable<dProductLevelDaily> plditems
= DSOLDAL.CheckProductDailyLevelbySiteCount(siteID, s);
if (plditems.Count() == 0) // plditems.Count() < 0)
{
IEnumerable<dProductLevel> ppitems = DSOLDAL.GetProductsbySite(siteID);
if (ppitems.Count() > 0)
{
oWebResponse.count = ppitems.Count().ToString();
oWebResponse.status = "OK";
}
else
{
oWebResponse.count = ppitems.Count().ToString();
oWebResponse.status = "OK";
}
}
else
{
oWebResponse.count = "2"; // plditems.Count().ToString();
oWebResponse.status = "OK";
}
These kind of bulk operations aren't very well matched to what Linq-to-SQL does.
In my opinion, I'd do this using a stored procedure, which you could include in your Linq-to-SQL DataContext and call from there.
That would also leave the data on the server and just copy it from one table to the other, instead of pulling down all data to your client and re-uploading it to the server.
Linq-to-SQL is a great tool - for manipulating single objects or small sets. It's less well suited for bulk operations.

Categories