DataTable RejectChanges does not reset all changes - datatable

I'm adding several rows to a DataTable in my strongly-typed DataSet and use a TableAdapterManager to insert the changes into my database. Using the UpdateAll function of the TableAdapterManager results in case of a failure in a database rollback of all inserted rows. Unfortunately DataTable.RejectChanges does not "rollback" the same rows in the DataTable.
In the call to DataTable.RejectChanges method only the last row is removed from the DataTable. I want the DataSet to have the same status as the database.
Isn't RejectChanges per MSDN documentation deleting all new (uncomitted) rows? Am I doing something wrong?
My code:
foreach (var item in List)
{
DataSet.customerRow custRow = ds.customer.NewcustomerRow();
custRow.name = item.Name;
try
{
ds.customer.AddcustomerRow(custRow);
}
catch (Exception ex)
{
ProcessException(ex, System.Reflection.MethodBase.GetCurrentMethod().Name);
valid=false;
}
}
if (valid)
{
DataSetTableAdapters.TableAdapterManager adapterManager = new DataSetTableAdapters.TableAdapterManager();
adapterManager.customerTableAdapter = new DataSetTableAdapters.customerTableAdapter();
try
{
retryPolicy.ExecuteAction(() =>
{
adapterManager.UpdateAll(ds);
});
}
catch (Exception ex)
{
ds.customer.RejectChanges();
}
}
else
{
ds.customer.RejectChanges();
}

The solution is to set adapterManager.BackupDataSetBeforeUpdate = true; This creates an internal backup copy of the dataset which is "reused" in case of failures.
MSDN: Hierarchical Update Overview
"The backup copy is only in memory during the execution of the TableAdapterManager.UpdateAll method. Therefore, there is no programmatic access to this backup dataset because it either replaces the original dataset or goes out of scope as soon as the TableAdapterManager.UpdateAll method has finished running."

Related

Why isn't my Where working as I think it should?

I'm trying to get some data from a database whose results can be more than one row.
I've the following code for that:
public System.Linq.IQueryable<Users> getUser2(string idUser)
{
try
{
using (Entities c = new Entities())
{
c.ContextOptions.LazyLoadingEnabled = false;
c.ContextOptions.ProxyCreationEnabled = false;
return c.Users.Include("Empresas").Where(x => x.Login == idUser && x.Empresas.Activa == true);
}
}
catch (Exception ex)
{
throw ex;
}
}
But it doesn't seem to get any result, it shows something like a badly formed Iqueryable, I mean if I expand its results view I can see a message that says "ObjectContext instance has been eliminated and cannot be used for operations that need a connection" If I try to access any Users element with the function ElementAt(index) I get an IndexOutOfBounds error as it looks like it has no data if watched on debug mode.
I've deduced that it's Where fault because this code Works fine in returning the first user it finds that fulfills the condition:
public Users getUser(string idUser)
{
try
{
using (Entities c = new Entities())
{
c.ContextOptions.LazyLoadingEnabled = false;
c.ContextOptions.ProxyCreationEnabled = false;
return c.Users.Include("Empresas").FirstOrDefault(x => x.Login == idUser && x.Empresas.Activa == true);
}
}
catch (Exception ex)
{
throw ex;
}
}
Does that Where work differently than what I think I should? If then, how could I get several data that fulfills the conditions I'm passing the same as in getUser but for several rows?
Thanks for your attention.
You need to enumerate the result, so after the "where" statement add. ToList() which will enumerate and execute the query against your database. FirstOrDefault is executing the query thats why you get a result.
You need to check the deferred methods and understand how they work.
EDIT
The following are some links to show you the deference between the Deferred method vs Immediate methods in LINQ
1- http://www.dotnetcurry.com/showarticle.aspx?ID=750
2- http://www.codeproject.com/Articles/627081/LINQ-Deferred-Execution-Lazy-Evaluation
3- http://visualcsharptutorials.com/linq/deferred-execution
Hope that helps.

ADO.NET - DataTable / Issue when Removing a column that has been previously renamed

I'm running into an issue while trying to remove a column from a DataTable, when this column has been previously created and renamed from same DataTable.
Here is the code snipnet. I'm using .NET Frameword 4.5.
It is pretty simple to reproduce.
try
{
DataTable data = new DataTable();
data.CaseSensitive = false;
data.Columns.Add("column1", typeof(string));
data.Columns.Add("column2", typeof(string));
data.Columns.Add("column3", typeof(string));
DataRow newRow = data.NewRow();
newRow["column1"] = "AAAAA";
newRow["column2"] = "BBBBB";
newRow["column3"] = "CCCCC";
data.Rows.Add(newRow);
data.AcceptChanges();
// Rename first column
Console.WriteLine("Rename [column1] to [COLUMN1] ...");
data.Columns["column1"].ColumnName = "COLUMN1";
data.AcceptChanges();
// Delete Column
Console.WriteLine("Delete column [column1] ...");
if (data.Columns.Contains("column1"))
{
data.Columns.Remove("column1");
data.AcceptChanges();
// Somehow the column will still partially exists after the 2 above lines
// If you look at the DataTable, it is gone, but if you test existance of the column, it will still think it is there
if (data.Columns.Contains("column1"))
{
Console.WriteLine("[column1] still Exists! WHY?");
}
}
// Then i need to Recreate that same column ... of course it will fail
Console.WriteLine("Recreate column [column1] ...");
data.Columns.Add("column1", typeof(string));
// this throw Exception as the data seems to still somehow exists in the DataTable
}
catch (Exception ex)
{
Console.WriteLine("Error=" + ex.Message);
}
finally
{
Console.WriteLine("Press any key to exit!");
Console.ReadLine();
}
I add AcceptChanges call almost everywhere to see if it makes a difference but no diff.
Any idea?
Am i doing something wrong?
Thanks in advance for any comments/help

Update object in foreach loop

I am using EF4/LINQ for the first time and have run into an issue. I am looping thru the results of a LINQ query using a foreach loop as follows:
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
CallOutcomeSubmission los = new CallOutcomeSubmission();
client = connectToService();
try
{
using (var context = new CallOutcomeContext())
{
// List of available actions
private static string ACTION_CALL_ATTEMPT = "Call Attempt";
DateTime oneDayAgo = DateTime.Now.AddHours(-24);
var query = from co in context.T_MMCallOutcome
join ca in context.T_Call on co.CallID equals ca.CallID
join lv in context.T_LeadVendorEmailHeader on co.LeadVendorEmailID equals lv.LeadVendorEmailID
where co.EnteredOn > oneDayAgo && co.MMLeadActionID == null
select new
{
co.CallOutcomeID,
co.CallID,
co.LeadVendorEmailID,
MMLeadID = lv.email_text,
ca.OutcomeID,
lv.FranchiseNumber,
co.MMLeadActionID,
co.LeadAction
};
// if any results found for query
if (query.Any())
{
foreach (var call in query.ToList())
{
// if the franchise exists
if (client.FranchiseExists(int.Parse(call.FranchiseNumber)))
{
switch (call.OutcomeID)
{
case 39: // Not Answered
call.LeadAction = ACTION_CALL_ATTEMPT;
break;
case 43: // Remove from Call List
break;
default: // If the OutcomeID is not identified in the case statement
break;
} // switch
}
else
{
los.eventLog.WriteEntry("CallOutcomeSubmission.OnTimedEvent: No franchise found with franchise ID " + call.FranchiseNumber);
}
// Save any changes currently on context
context.SaveChanges();
} // foreach
}
// if no results found from query write system log stating such
else
{
los.eventLog.WriteEntry("CallOutcomeSubmission.OnTimedEvent: No new entries found");
}
} // using
client.Close();
}
catch (System.TimeoutException exception)
{
los.eventLog.WriteEntry("CallOutcomeSubmission.OnTimedEvent:" + exception.ToString());
client.Abort();
}
catch (System.ServiceModel.CommunicationException exception)
{
los.eventLog.WriteEntry("CallOutcomeSubmission.OnTimedEvent:" + exception.ToString());
client.Abort();
}
}
When I try to do the assignment:
call.LeadAction = ACTION_CALL_ATTEMPT;
I get a build error of
Property or indexer 'AnonymousType#2.LeadAction' cannot be assigned to -- it is read only
I can't seem to find anything on this specific error doing a Google search and am not sure what I am doing wrong. Is it because the original query contains a join?
How can I do the assignment of call.LeadAction within the foreach loop?
I would also like to know if there are design issue withe way I have written the query or performed any of the operations since this is my first foray into EF/LINQ.
You're creating a new anonymous type - with the Linq joins and then trying to set that value. What you're really wanting to do, is update the call's LeadAction correct?
How would EF know to translate your new query back to an entity so it can go back to the database? It would have to go through alot of hoops, and it's not capable of that.
What you could do, is retrieve the Call from your database and set the LeadAction that way - I'm using Find, assuming that CallID is your PK:
case 39: // Not Answered
var thisCall = context.T_Call.Find(call.CallID)
thisCall.LeadAction = ACTION_CALL_ATTEMPT;
break;

how to iterate through an Icollection In MVC3 nhibernate

i am developing an application in mvc3.
I have two dropdowns and on the basis of value selected in first dropdown the second dropdown is populated.
The first dropdown is Course and on the basis of course selected the second dropdown populates the states where the course is available.
Foreg.if the course is 'MCA' the states should be Maharashtra,rajasthan and so-on.
For this i have written an ajax function which is working fine.
But the problem is i am not able to fetch multiple states at a time that is i can fetch only One state at a time.
I have written the following Code to fetch the state name:
HobbyHomeAdress Table contains ProvincialStateID which i fetch through some other method.
Then i compare that value with the value in ProvincialStateID in ProvincialState Table and fetch the data of that table but with it gives me the last record only.
public ICollection<ProvincialState> FetchStateByStateid(ICollection<HobbyHomeAddress> hobbyhomeaddresslist)
{
log.Debug("Start");
ISession session = DataAccessLayerHelper.OpenWriterSession();
ITransaction transaction = session.BeginTransaction();
ICollection<ProvincialState> provincialstate = null;
try
{
foreach (var state in hobbyhomeaddresslist)
{
provincialstate = session.CreateCriteria(typeof(ProvincialState))
.Add(Expression.Eq("ProvincialStateID", state.ProvincialState.ProvincialStateID))
.List<ProvincialState>();
}
transaction.Commit();
}
catch (SessionException ex)
{
if (transaction != null && transaction.IsActive)
transaction.Rollback();
log.Error(ex);
provincialstate = null;
}
finally
{
if (transaction != null)
transaction.Dispose();
if (session != null && session.IsConnected)
session.Close();
log.Debug("End");
}
return provincialstate;
}
you are recreating the provincialstate collection for each state in hobbyhomeaddresslist. So you end up with a collection with a single entry, usually the last one. Instead you should create the collection upfront and after retrieving an item, just add it to that collection.
...snip
...
List<ProvincialState> provincialstate = new List<ProvincialState>();
try
{
foreach (var state in hobbyhomeaddresslist)
{
var list = session.CreateCriteria(typeof(ProvincialState))
.Add(Expression.Eq("ProvincialStateID", state.ProvincialState.ProvincialStateID))
.List<ProvincialState>();
provincialstate.AddRange(list);
}
transaction.Commit();
}
...
Update: single query using a Disjunction.
IList<ProvincialState> provincialstate = null;
Disjunction dj = new Disjunction();
try
{
foreach (var state in hobbyhomeaddresslist)
{
dj.Add(Expression.Eq("ProvincialStateID", state.ProvincialState.ProvincialStateID));
}
provincialstate = session.CreateCriteria(typeof(ProvincialState))
.Add(dj)
.List<ProvincialState>();
transaction.Commit();
}
if you look at the generated SQL, you should now see a single select with several where clauses instead of several selects with a single where clause.

Telerik RADGrid - linq and updating

Telerik's RADGrid, basing on their example on http://demos.telerik.com/aspnet-ajax/grid/examples/dataediting/programaticlinqupdates/defaultcs.aspx
Problem: I can insert and delete, however updating doesn't work. No error trapped. Data just doesn't change.
From the code below it looks like Telerik Grid is doing some kung-fu behind the scenes to wire things up. I can't see the db receiving any update statements.
Question: anything obvious I'm missing?
protected void RadGrid1_UpdateCommand(object source, GridCommandEventArgs e)
{
var editableItem = ((GridEditableItem) e.Item);
var raceId = (Guid) editableItem.GetDataKeyValue("RaceID");
//retrive entity form the Db
var race = DbContext.races.Where(n => n.raceid == raceId).FirstOrDefault();
if (race != null)
{
//update entity's state
editableItem.UpdateValues(race);
try
{
//submit chanages to Db
DbContext.SubmitChanges();
}
catch (Exception f)
{
ShowErrorMessage(f);
}
}
}
Think I may have to go back to their example.. get their db.. and attack from that point of view.
Cheers!
Do a Rebind after your update. Trying adding
RadGrid1.DataSource = null;
RadGrid1.Rebind();
After your call to DbContext.SubmitChanges(); call, assuming you have implemented _NeedDataSource().

Resources