Insert and update multi item into database in the same time - c#

I have an empty data base.
I want to add multi records into data base.
while inserting record to data base i want to check if my product inserted in same date donot add it again(i want to change some it's filed and update it's content).
i used this code but it just add some data into data base (it can't check for existing product.)
var AllData = ClsDataBase.Database.InsertProductTbls;
foreach(item in AllData)
{
//Update
if (Exist(datefa))
{
var query = ClsDataBase.Database.CustomerProductTbls.SingleOrDefault
(data => data.CustomerId == AllData .CustomerId );
int? LastProductTotal = query.CustomerProducTtotal;
query.CustomerProducTtotal = LastProductTotal + ClsInsertProduct._InsertProductNumber;
}
//Insert
else
{
_CustomerProductTbl = new CustomerProductTbl();
_CustomerProductTbl.CustomerId = AllData ._CustomerId;
_CustomerProductTbl.CustomerProductDateFa = AllData.datefa
.
.
.
ClsDataBase.Database.AddToCustomerProductTbls(_CustomerProductTbl);
}
}
}
ClsDataBase.Database.SaveChanges();
if i use ClsDataBase.Database.SaveChanges(); for both update and insert part i will return this error:
An error occurred while starting a transaction on the provider connection. See the inner exception for details.
please help.

I got the solution by opening database conection for each repeat loop:
foreach(item in AllData)
{
using (StorageEntities context = new StorageEntities())
{
//Update
if (Exist(datefa))
{
var query = ClsDataBase.Database.CustomerProductTbls.SingleOrDefault
(data => data.CustomerId == AllData .CustomerId );
int? LastProductTotal = query.CustomerProducTtotal;
query.CustomerProducTtotal = LastProductTotal + ClsInsertProduct._InsertProductNumber;
}
//Insert
else
{
_CustomerProductTbl = new CustomerProductTbl();
_CustomerProductTbl.CustomerId = AllData ._CustomerId;
_CustomerProductTbl.CustomerProductDateFa = AllData.datefa;
ClsDataBase.Database.AddToCustomerProductTbls(_CustomerProductTbl);
}
ClsDataBase.Database.SaveChanges();
}
}

Related

How to write LINQ query for fetching the specific records and generating new result at the same time?

I have to update one field in the row of the table after fetching two records from the same row. As an easiest practice I have fetched two records individually, created a new value and then updating that particular property through Entity framework. I think there is a better way to do the same thing with less code. If any body can suggest please.
if (objModel.amountpaid==0)
{
using (estatebranchEntities db=new estatebranchEntities())
{
int rentVar = Convert.ToInt32(db.PropertyDetails.Where(m => m.propertyid == objVM.propertyid).Select(m => m.rent).SingleOrDefault());
int balanceVar = Convert.ToInt32(db.PropertyDetails.Where(m => m.propertyid == objVM.propertyid).Select(m => m.balance).SingleOrDefault());
int balanceUpdateVar = (rentVar + balanceVar);
var propInfo = new PropertyDetail() { balance = balanceUpdateVar };
//var result = (from a in db.PropertyDetails
// where a.propertyid == objVM.propertyid
// select new PropertyDetail
// {
// rent = a.rent,
// balance = a.balance
// }).ToList();
db.PropertyDetails.Attach(propInfo);
db.Entry(propInfo).Property(z => z.balance).IsModified = true;
db.SaveChanges();
}
}
Here is what I think you can do.
Fetch the data once and update once.
using (estatebranchEntities db=new estatebranchEntities())
{
var propDetails = db.PropertyDetails.FirstOrDefault(m => m.propertyid == objVM.propertyid);
if (propDetails != null)
{
int rentVar = Convert.ToInt32(propDetails.rent);
int balanceVar = Convert.ToInt32(propDetails.balance);
int balanceUpdateVar = rentVar + balanceVar;
//now do the update
propDetails.balance = balanceUpdateVar;
db.Entry(proDetails).State = EntityState.Modified;
db.SaveChanges();
}
}
if you need to use the rentVar,balanceVar or the balanceUpdateVar, outside of the using statement then declare them outside it.

DataAdapter .Update does not update back table

my problem is very common, but I have not found any solution.
This is my code:
public async Task<QueryResult> RollbackQuery(ActionLog action)
{
var inputParameters = JsonConvert.DeserializeObject<Parameter[]>(action.Values);
var data = DeserailizeByteArrayToDataSet(action.RollBackData);
using (var structure = PrepareStructure(action.Query, action.Query.DataBase, inputParameters))
{
//_queryPlanner is the implementor for my interface
return await _queryPlanner.RollbackQuery(structure, data);
}
}
I need to load DataTable (from whereever) and replace data to database. This is my Rollback function. This function use a "CommandStructure" where I've incapsulated all SqlClient objects. PrepareStructure initialize all objects
//_dataLayer is an Helper for create System.Data.SqlClient objects
//ex: _dataLayer.CreateCommand(preSelect) => new SqlCommand(preSelect)
private CommandStructure PrepareStructure(string sql, string preSelect, DataBase db, IEnumerable<Parameter> inputParameters)
{
var parameters = inputParameters as IList<Parameter> ?? inputParameters.ToList();
var structure = new CommandStructure(_logger);
structure.Connection = _dataLayer.ConnectToDatabase(db);
structure.SqlCommand = _dataLayer.CreateCommand(sql);
structure.PreSelectCommand = _dataLayer.CreateCommand(preSelect);
structure.QueryParameters = _dataLayer.CreateParemeters(parameters);
structure.WhereParameters = _dataLayer.CreateParemeters(parameters.Where(p => p.IsWhereClause.HasValue && p.IsWhereClause.Value));
structure.CommandBuilder = _dataLayer.CreateCommandBuilder();
structure.DataAdapter = new SqlDataAdapter();
return structure;
}
So, my function uses SqlCommandBuilder and DataAdapter to operate on Database.
PreSelectCommand is like "Select * from Purchase where CustomerId = #id"
The table Purchase has one primaryKey on ID filed
public virtual async Task<QueryResult> RollbackQuery(CommandStructure cmd, DataTable oldData)
{
await cmd.OpenConnectionAsync();
int record = 0;
using (var cmdPre = cmd.PreSelectCommand as SqlCommand)
using (var dataAdapt = new SqlDataAdapter(cmdPre))
using (var cmdBuilder = new SqlCommandBuilder(dataAdapt))
{
dataAdapt.UpdateCommand = cmdBuilder.GetUpdateCommand();
dataAdapt.DeleteCommand = cmdBuilder.GetDeleteCommand();
dataAdapt.InsertCommand = cmdBuilder.GetInsertCommand();
using (var tbl = new DataTable(oldData.TableName))
{
dataAdapt.Fill(tbl);
dataAdapt.FillSchema(tbl, SchemaType.Source);
tbl.Merge(oldData);
foreach (DataRow row in tbl.Rows)
{
row.SetModified();
}
record = dataAdapt.Update(tbl);
}
}
return new QueryResult
{
RecordAffected = record
};
}
I Execute the code and I don't have any errors, but the data are not updated.
variable "record" contain the right number of modified (??) record, but..... on the table nothing
can someone help me?
EDIT 1:
With SQL Profiler I saw that no query is executed on DB. Only select query on .Fill(tbl) command.
EDIT 2:
Now I have made one change:
tbl.Merge(oldData) => tbl.Merge(oldData, true)
so I see perform the expected query but, with reversed parameters.
UPDATE Purchase SET price=123 where id=6 and price=22
instead of
UPDATE Purchase SET price=22 where id=6 and price=123

Trying to access variable from outside foreach loop

The application I am building allows a user to upload a .csv file, which will ultimately fill in fields of an existing SQL table where the Ids match. First, I am using LinqToCsv and a foreach loop to import the .csv into a temporary table. Then I have another foreach loop where I am trying to loop the rows from the temporary table into an existing table where the Ids match.
Controller Action to complete this process:
[HttpPost]
public ActionResult UploadValidationTable(HttpPostedFileBase csvFile)
{
var inputFileDescription = new CsvFileDescription
{
SeparatorChar = ',',
FirstLineHasColumnNames = true
};
var cc = new CsvContext();
var filePath = uploadFile(csvFile.InputStream);
var model = cc.Read<Credit>(filePath, inputFileDescription);
try
{
var entity = new TestEntities();
var tc = new TemporaryCsvUpload();
foreach (var item in model)
{
tc.Id = item.Id;
tc.CreditInvoiceAmount = item.CreditInvoiceAmount;
tc.CreditInvoiceDate = item.CreditInvoiceDate;
tc.CreditInvoiceNumber = item.CreditInvoiceNumber;
tc.CreditDeniedDate = item.CreditDeniedDate;
tc.CreditDeniedReasonId = item.CreditDeniedReasonId;
tc.CreditDeniedNotes = item.CreditDeniedNotes;
entity.TemporaryCsvUploads.Add(tc);
}
var idMatches = entity.Authorizations.ToList().Where(x => x.Id == tc.Id);
foreach (var number in idMatches)
{
number.CreditInvoiceDate = tc.CreditInvoiceDate;
number.CreditInvoiceNumber = tc.CreditInvoiceNumber;
number.CreditInvoiceAmount = tc.CreditInvoiceAmount;
number.CreditDeniedDate = tc.CreditDeniedDate;
number.CreditDeniedReasonId = tc.CreditDeniedReasonId;
number.CreditDeniedNotes = tc.CreditDeniedNotes;
}
entity.SaveChanges();
entity.Database.ExecuteSqlCommand("TRUNCATE TABLE TemporaryCsvUpload");
TempData["Success"] = "Updated Successfully";
}
catch (LINQtoCSVException)
{
TempData["Error"] = "Upload Error: Ensure you have the correct header fields and that the file is of .csv format.";
}
return View("Upload");
}
The issue in the above code is that tc is inside the first loop, but the matches are defined after the loop with var idMatches = entity.Authorizations.ToList().Where(x => x.Id == tc.Id);, so I am only getting the last item of the first loop.
So I would need to put var idMatches = entity.Authorizations.ToList().Where(x => x.Id == tc.Id); in the first loop, but then I can't access it in the second. If I nest the second loop then it is way to slow. Is there any way I could put the above statement in the first loop and still access it. Or any other ideas to accomplish the same thing? Thanks!
Instead of using multiple loops, keep track of processed IDs as you go and then exclude any duplicates.
[HttpPost]
public ActionResult UploadValidationTable(HttpPostedFileBase csvFile)
{
var inputFileDescription = new CsvFileDescription
{
SeparatorChar = ',',
FirstLineHasColumnNames = true
};
var cc = new CsvContext();
var filePath = uploadFile(csvFile.InputStream);
var model = cc.Read<Credit>(filePath, inputFileDescription);
try
{
var entity = new TestEntities();
var tcIdFound = new HashSet<string>();
foreach (var item in model)
{
if (tcIdFound.Contains(item.Id))
{
continue;
}
var tc = new TemporaryCsvUpload();
tc.Id = item.Id;
tc.CreditInvoiceAmount = item.CreditInvoiceAmount;
tc.CreditInvoiceDate = item.CreditInvoiceDate;
tc.CreditInvoiceNumber = item.CreditInvoiceNumber;
tc.CreditDeniedDate = item.CreditDeniedDate;
tc.CreditDeniedReasonId = item.CreditDeniedReasonId;
tc.CreditDeniedNotes = item.CreditDeniedNotes;
entity.TemporaryCsvUploads.Add(tc);
}
entity.SaveChanges();
entity.Database.ExecuteSqlCommand("TRUNCATE TABLE TemporaryCsvUpload");
TempData["Success"] = "Updated Successfully";
}
catch (LINQtoCSVException)
{
TempData["Error"] = "Upload Error: Ensure you have the correct header fields and that the file is of .csv format.";
}
return View("Upload");
}
If you want to make sure you get the last value for any duplicate ids, then store each TemporaryCsvUpload record in a dictionary instead of using only a HashSet. Same basic idea though.
Declare idMatches before the first loop, but don't instantiate it or set its value to null. Then you'll be able to use it inside both loops. After moving the declaration before the first loop, you'll still end up having the values from the last iteration using a simple Where. You'll need to concatenate the already existing list with results for the current iteration.

filtered Query for objectContext Using Linq to SQL

i have tried to search some examples about my approach but all questions was not close enough to what i was trying to achieve .
for the TLDR sake , Question is : how do i make it work as in plain sql query?
using c# - Winforms with SqlCompact4 and Linq to SQL
my scenario involves a form with all the relevant Db table columns as availble filters to query
and then on text change event of each filtertextbox as a filter, the datasource of the gridview updates accordingly
and because i allow filtered search via many of them columns i was trying to avoid use of some extra
lines of code.
so lets say we only concentrate on 4 columns
custID, name, email, cellPhone
each has its corresponding TextBox.
i am trying to make a query as follows :
first i systematically collect all Textbox into a List
var AllFormsSearchFiltersTBXLst = new List<TextBox>();
code that collects all tbx on current form
var AllFormsSearchFiltersTBXLst = [currentFormHere].Controls.OfType<TextBox>();
so now i have all of textboxes as filters regardless if they have any value
then check who has some value in it
forech textbox in this filters textboxes if text length is greater than zero
it means that current filter is active
then.. a second list AllFormsACTIVESearchfiltersTBXLst will contain only active filters
what i was trying to achieve was in same way i didn't have to specify each of textbox objects
i just looped through each of them all as a collection and didn't have to specify each via it's id
now i want to make a filter on a dbContext using only those active filters
so i will not have to ask if current tbxName is email
like
query = db.Where(db=>db.email.Contains(TbxEmail.Text));
and again and again for each of 10 to 15 columns
what i have got so far is nothing that implements what i was heading to.
using (SqlCeConnection ClientsConn = new SqlCeConnection(ConfigurationManager.ConnectionStrings["Conn_DB_RCL_CRM2014"].ConnectionString))
{
System.Data.Linq.Table<ContactsClients> db = null;
// get all column names from context
var x =(System.Reflection.MemberInfo[]) typeof(ContactsClients).GetProperties();
using (DB_RCL_CRM2014Context Context = new DB_RCL_CRM2014Context(ClientsConn))
{
if (!Filtered)
db = Context.ContactsClients;//.Where(client => client.Name.Contains("fler"));
else
{
db = Context.ContactsClients;
// filters Dictionary contains the name of textBox and its value
// I've named TBX as Columns names specially so i could equalize it to the columns names when needed to automate
foreach (KeyValuePair<string,string> CurFltrKVP in FiltersDict)
{
foreach (var memberInfo in x)
{
// couldn't find out how to build the query
}
}
}
BindingSource BS_Clients = new BindingSource();
BS_Clients.DataSource = db;
GV_ClientInfo_Search.DataSource = BS_Clients;
what i normally do when working with plain sql is
foreach textbox take its value and add it into a string as filter
var q = "where " ;
foreach(tbx CurTBX in ALLFILTERTBX)
{
q +=CurTBX.Name +" LIKE '%" + CurTBX.Text + "%'";
// and some checking of last element in list off cores
}
then pass this string as a filter to the main select query ... that simple
how do i make it work as in plain sql query?
I think that you're trying to get the property of db dynamically, like: db.email according to the looped name of your textbox (here 'email'). However, I recommend you to do it some other way. I'd make a switch for each type of the property, like: email, name etc. Something like this:
// Create a list for the results
var results = new List<YourDBResultTypeHere>();
foreach(tbx CurTBX in ALLFILTERTBX)
{
switch(CurTBX.Name) {
case "email":
results.AddRange(db.Where(db => db.email.Contains(tbx.Text)).ToList());
break;
case "name":
results.AddRange(db.Where(db => db.name.Contains(tbx.Text)).ToList());
break;
}
}
try this
void UpdateGridViewData(bool Filtered=false, Dictionary<string,string> FiltersDict = null)
{
using (SqlCeConnection ClientsConn = new SqlCeConnection(ConfigurationManager.ConnectionStrings["Conn_DB_RCL_CRM2014"].ConnectionString))
{
System.Data.Linq.Table<ContactsClients> db = null;
IEnumerable<ContactsClients> IDB = null;
BindingSource BS_Clients = new BindingSource();
System.Reflection.MemberInfo[] AllDbTblClientsColumns = (System.Reflection.MemberInfo[])typeof(ContactsClients).GetProperties();
using (DB_RCL_CRM2014Context Context = new DB_RCL_CRM2014Context(ClientsConn))
{
if (!Filtered)
{
db = Context.ContactsClients;
BS_Clients.DataSource = db;
}
else
{
string fltr = "";
var and = "";
if (FiltersDict.Count > 1) and = "AND";
for (int i = 0; i < FiltersDict.Count; i++)
{
KeyValuePair<string, string> CurFltrKVP = FiltersDict.ElementAt(i);
if (i >= FiltersDict.Count-1) and = "";
for (int j = 0; j < AllDbTblClientsColumns.Length; j++)
{
if (AllDbTblClientsColumns[j].Name.Equals(CurFltrKVP.Key))
{
fltr += string.Format("{0} Like '%{1}%' {2} ", AllDbTblClientsColumns[j].Name, CurFltrKVP.Value, and);
}
}
}
try
{
IDB = Context.ExecuteQuery<ContactsClients>(
"SELECT * " +
"FROM ContactsCosmeticsClients " +
"WHERE " + fltr
);
BS_Clients.DataSource = IDB;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
GV_ClientInfo_Search.DataSource = BS_Clients;
}
}
}

LINQ EF copy and paste a record with a difference field

i need copy and paste some records of my table with just but change one field.
here is my code:
using (ClearWhiteDBEntities cwContext = new ClearWhiteDBEntities())
{
var qlstfld = from lstflds in cwContext.tblListFields
where lstflds.listId == theLongSrc
select lstflds;
foreach (var item in qlstfld)
{
tblListField lstFldRow = new tblListField
{
name = item.name,
filterFieldId = item.filterFieldId,
listId = theLongDes, //this field must be change in paste
continueById = item.continueById,
destinationId = item.destinationId,
conditionId = item.conditionId,
userId = userId,
date = Convert.ToDateTime(DateTime.Now.ToShortDateString()),
time = DateTime.Now.TimeOfDay,
IP = trueIp
};
cwContext.AddTotblListFields(lstFldRow);
cwContext.SaveChanges();
}
}
but i get this error:
an error accured white starting a transaction on the provider connection. see the inner exception for details.
what is best solution to copy and paste records but change one field?
If you are using change tracking:
using (ClearWhiteDBEntities cwContext = new ClearWhiteDBEntities())
{
var qlstfld = from lstflds in cwContext.tblListFields
where lstflds.listId == theLongSrc
select lstflds;
foreach (var item in qlstfld)
{
cwContext.ObjectStateManager.ChangeObjectState(item, System.Data.EntityState.Added);
item.Id = 0;
item.listId = theLongDes; //this field must be change in paste
}
cwContext.SaveChanges();
}

Categories

Resources