SubmitChanges() not updating the database - c#

I am an old hand at ADO.NET but new to linq. I have written the below function to change one column of int in the database to a new value. The query part works fine, but after me updating the KeyStates in the database doesn't seem to work. Nothing changes. No errors, but also no update.
public static void UpdateKeySetToDatabase(IChangeTrackableAsSet set,
int tableId, State newState)
{
try
{
using (var c = new SqlConnection(ConnectionString.ConnectionString))
{
c.Open();
List<int> keyList = set.trackedKeys.Select(k => k.KeyId).ToList();
DataContext dc = new DataContext(c);
Table<TrackedKey> tableKeys = dc.GetTable<TrackedKey>();
var KeyStates =
from k in tableKeys
where (keyList.Contains(k.KeyId) && k.TableId == tableId)
select k;
foreach (var k in KeyStates)
{
EventHandling.Logging.CreateTextEvent($"Key {k.ShowKeyAsJson()}");
k.StatusOfKey = (int)newState;
EventHandling.Logging.CreateTextEvent($"New Key {k.ShowKeyAsJson()}");
};
tableKeys.Context.SubmitChanges();
}
}
catch (Exception ex)
{
EventHandling.Logging.CreateTextEvent($"linq error {ex.ToString()}");
}
}

I think you should specify to the Entity Framework context, that the entity has changed, in order to update it.
...
k.StatusOfKey = (int)newState;
EventHandling.Logging.CreateTextEvent($"New Key {k.ShowKeyAsJson()}");
dc.Entry(k).State = EntityState.Modified;
...
When you execute the SaveChanges() method, all your objects will be modified.

Related

Rollback Entity Framework SaveChanges()

For example I am adding the peoples data to database per state (this is not what I am doing exactly but the model is same). We have list of states and each state has millions of people. So initially in code, I am saving the state to get the State ID and then use that ID to bulk insert peoples data.
If something goes wrong while adding the peoples data, let's say 20th million record threw some exception, is there a way to revert back the data already saved in both Peoples and State table?
Any suggestion is highly appreciated..
List <Peoples> PeopleList = new List<Peoples>();
int peopleCounter = 0;
foreach (var stateVal in States)
{
using (var context = new StateEntities())
{
State st = new State();
st.ID = stateVal.ID;
st.Name = stateVal.Name;
context.State.Add(st);
context.SaveChanges();
if (stateVal.Peoples != null )
{
foreach (var _p in stateVal.Peoples)
{
Peoples _people = new Peoples();
_people.Name = _p.Name;
_people.Age = _P.Age;
_people.State_ID = stateVal.ID; // Getting state ID from State object as it already saved to DB
PeopleList.Add(_people)
peopleCounter++;
if (peopleCounter == 100000)
{
InsertPeople(PeopleList, context); // does bulk insert when PeopleList reaches 100k
PeopleList.Clear();
peopleCounter = 0;
}
}
}
}
}
private static void InsertPeople(List<Peoples> PeopleList, StateEntities context)
{
context.Configuration.AutoDetectChangesEnabled = false;
context.Configuration.ValidateOnSaveEnabled = false;
using (var transactionScope = new TransactionScope(TransactionScopeOption.Required, new System.TimeSpan(0, 30, 0)))
{
context.BulkInsert(PeopleList, options => options.BatchTimeout = 0);
context.SaveChanges();
transactionScope.Complete();
}
}
You can use transaction of SQL to rollback. It's supported by EF.
using (var context = new SchoolContext())
{
using (DbContextTransaction transaction = context.Database.BeginTransaction())
{
try
{
//**TODO: Do your bulk insert codes here**
// Save changes data in context
context.SaveChanges();
// Commit changes
transaction.Commit();
}
catch (Exception ex)
{
// Rollback all changes
transaction.Rollback();
}
}
}
Ref: https://learn.microsoft.com/en-us/ef/core/saving/transactions

NullReferenceException when creating a table with DateTime Column(SMO, C#)

My application is used to copy tables from one database and duplicate them to another, I'm using smo and C#. My code:
private static void createTable(Table sourcetable, string schema, Server destinationServer,
Database db)
{
Table copiedtable = new Table(db, sourcetable.Name, schema);
createColumns(sourcetable, copiedtable);
copiedtable.AnsiNullsStatus = sourcetable.AnsiNullsStatus;
copiedtable.QuotedIdentifierStatus = sourcetable.QuotedIdentifierStatus;
copiedtable.TextFileGroup = sourcetable.TextFileGroup;
copiedtable.FileGroup = sourcetable.FileGroup;
copiedtable.Create();
}
private static void createColumns(Table sourcetable, Table copiedtable)
{
foreach (Column source in sourcetable.Columns)
{
Column column = new Column(copiedtable, source.Name, source.DataType);
column.Collation = source.Collation;
column.Nullable = source.Nullable;
column.Computed = source.Computed;
column.ComputedText = source.ComputedText;
column.Default = source.Default;
if (source.DefaultConstraint != null)
{
string tabname = copiedtable.Name;
string constrname = source.DefaultConstraint.Name;
column.AddDefaultConstraint(tabname + "_" + constrname);
column.DefaultConstraint.Text = source.DefaultConstraint.Text;
}
column.IsPersisted = source.IsPersisted;
column.DefaultSchema = source.DefaultSchema;
column.RowGuidCol = source.RowGuidCol;
if (server.VersionMajor >= 10)
{
column.IsFileStream = source.IsFileStream;
column.IsSparse = source.IsSparse;
column.IsColumnSet = source.IsColumnSet;
}
copiedtable.Columns.Add(column);
}
}
The project perfectly well works with North wind database, however, with some tables from AdventureWorks2014 database I get the following inner exception at copiedtable.Create();:
NullReferenceException: Object reference not set to an instance of an object.
I suspect, that AdventureWorks datetime column may be causing the problem (Data is entered like: 2008-04-30 00:00:00.000)
I have solved this problem myself and it was quite interesting. I couldn't find any null values neither in the Table itself, nor in it's columns.
Then I realized, that AdventureWorks2014 DB used User defined Data Types and XML Schema collections. As I haven't copied them, they couldn't be accessed and the creation of the table failed. It was only necessary to copy XML Schema Collections and User Defined Data Types to second database:
private static void createUserDefinedDataTypes(Database originalDB, Database destinationDB)
{
foreach (UserDefinedDataType dt in originalDB.UserDefinedDataTypes)
{
Schema schema = destinationDB.Schemas[dt.Schema];
if (schema == null)
{
schema = new Schema(destinationDB, dt.Schema);
schema.Create();
}
UserDefinedDataType t = new UserDefinedDataType(destinationDB, dt.Name);
t.SystemType = dt.SystemType;
t.Length = dt.Length;
t.Schema = dt.Schema;
try
{
t.Create();
}
catch(Exception ex)
{
throw (ex);
}
}
}
private static void createXMLSchemaCollections(Database originalDB, Database destinationDB)
{
foreach (XmlSchemaCollection col in originalDB.XmlSchemaCollections)
{
Schema schema = destinationDB.Schemas[col.Schema];
if (schema == null)
{
schema = new Schema(destinationDB, col.Schema);
schema.Create();
}
XmlSchemaCollection c = new XmlSchemaCollection(destinationDB, col.Name);
c.Text = col.Text;
c.Schema = col.Schema;
try
{
c.Create();
}
catch(Exception ex)
{
throw (ex);
}
}
}

C# LinqToSql SubmitChanges() does not update, even though PK is set

I have the following code:
public int DeActivate(User entity) {
try {
using (UsersDataContext usersDC = new UsersDataContext()) {
users user = new users();
user = usersDC.users.Where(x => x.id == entity.Id).
Select(x => new users {active = x.active}).FirstOrDefault();
//user.active = entity.Active;
user.active = false;
usersDC.SubmitChanges();
return 1;
}
}
catch {
return 0;
}
}
While running an NUnit test on the method, the method returns 1, as it is supposed to do, and while de-bugging no exceptions are thrown. But, when i cross check with the DB the records have not being affected. I have tried the following: Re-created DBML file, checked for existance of PK, and checked the following sites:
MSDN question, StackOverflow question, but to no avail.
Your select statement is wrong. Try this.
public int DeActivate(User entity) {
try {
using (UsersDataContext usersDC = new UsersDataContext()) {
var user = usersDC.users.Single(x => x.id == entity.Id);
user.active = false;
usersDC.SubmitChanges();
return 1;
}
} catch {
return 0;
}
}

Does EF upsert have to be done manually?

I want to upsert reference members of an existing entity.
Do I have to write specific code for the upsert?
meaning: I have to check if I'm handling an existing reference member or a new one.
Is there any other simple way to do so?
What happens when you do only Save ?
public void SaveCofiguration(MamConfiguration_V1Ui itemUi)
{
var itemEf = mMamConfiguration_V1UiToEfConvertor.ConvertToNewEf(itemUi);
using (var maMDBEntities = new MaMDBEntities())
{
IDal<MamConfiguration_V1> mamConfigurationDal = mDalFactory.GetDal<MamConfiguration_V1>(maMDBEntities);
mamConfigurationDal.Save(itemEf);
}
}
public MamConfiguration_V1 GetById(object id)
{
id.ThrowIfNull("id");
int configurationId = Convert.ToInt32(id);
var result =
mMaMDBEntities.MamConfiguration_V1.SingleOrDefault(item => item.ConfigurationId == configurationId);
return result;
}
public MamConfiguration_V1 Save(MamConfiguration_V1 item)
{
item.ThrowIfNull("item");
var itemFromDB = GetById(item.ConfigurationId);
if (itemFromDB != null)
{
UpdateEfItem(itemFromDB, item);
// if (mMaMDBEntities.ObjectStateManager.GetObjectStateEntry(itemFromDB).State == EntityState.Detached)
// {
// mMaMDBEntities.MamConfiguration_V1.AddObject(itemFromDB);
// }
// Attached object tracks modifications automatically
mMaMDBEntities.SaveChanges();
return item;
}
private void UpdateEfItem(MamConfiguration_V1 itemFromDb, MamConfiguration_V1 itemFromUi)
{
itemFromDb.UpdatedDate = DateTime.Now;
itemFromDb.Description = itemFromUi.Description;
itemFromDb.StatusId = itemFromUi.StatusId;
itemFromDb.Name = itemFromUi.Name;
itemFromDb.NumericTraffic = itemFromUi.NumericTraffic;
itemFromDb.PercentageTraffic = itemFromUi.PercentageTraffic;
itemFromDb.Type = itemFromUi.NumericTraffic;
foreach (var item in itemFromDb.MamConfigurationToBrowser_V1.ToList())
{
if (itemFromUi.MamConfigurationToBrowser_V1.All(b => b.BrowserVersionId != item.BrowserVersionId))
{
mMaMDBEntities.MamConfigurationToBrowser_V1.DeleteObject(item);
}
}
for (int i = 0; i < itemFromUi.MamConfigurationToBrowser_V1.Count; i++)
{
var element = itemFromUi.MamConfigurationToBrowser_V1.ElementAt(i);
var item = itemFromDb.MamConfigurationToBrowser_V1.SingleOrDefault(b => b.BrowserVersionId == element.BrowserVersionId);
if (item != null)
{
// copy properties from element to item
}
else
{
element.Browser = mMaMDBEntities.Browsers.Single(browserItem =>
browserItem.BrowserID == element.BrowserID);
//element.MamConfiguration_V1 = itemFromDb;
//have also tried: element.MamConfiguration_V1 = null;
//element.MamConfiguration_V1Reference = null;
itemFromDb.MamConfigurationToBrowser_V1.Add(element);
}
}
}
But I would have expecte Save(itemUi) and SaveChanges() to work fine. No?
public void InsertOrUpdate(DbContext context, UEntity entity)
{
context.Entry(entity).State = entity.Id == 0 ?
EntityState.Added :
EntityState.Modified;
context.SaveChanges();
}
http://forums.asp.net/t/1889944.aspx/1
To avoid the overhead of a query and then insert, or throwing exceptions, you can take advantage of the underlying database support for merges or upserts.
This nuget package does the job pretty well: https://www.nuget.org/packages/FlexLabs.EntityFrameworkCore.Upsert/
Github: https://github.com/artiomchi/FlexLabs.Upsert
Example:
DataContext.DailyVisits
.Upsert(new DailyVisit
{
// new entity path
UserID = userID,
Date = DateTime.UtcNow.Date,
Visits = 1,
})
// duplicate checking fields
.On(v => new { v.UserID, v.Date })
.WhenMatched((old, #new) => new DailyVisit
{
// merge / upsert path
Visits = old.Visits + 1,
})
.RunAsync();
The underlying generated sql does a proper upsert. This command runs right away and does not use change tracking, so that is one limitation.
See 'AddOrUpdate' method of System.Data.Entity.Migrations.
http://msdn.microsoft.com/en-us/library/system.data.entity.migrations.idbsetextensions.addorupdate%28v=vs.103%29.aspx
using System.Data.Entity.Migrations;
public void Save(Person person) {
var db = new MyDbContext();
db.People.AddOrUpdate(person);
db.SaveChanges();
}
"optimistic" approach for simple scenarios (demos)...
dbContext.Find()'s intellisense help tells us that it either retrieves entity by key if already present in current context, or queries the database to get it... then we know if it exists to either add or update. i'm using EFCore v2.2.0.
var existing = _context.Find<InventoryItem>(new object[] {item.ProductId});
if (existing == null) _context.Add(item);
else existing.Quantity = item.Quantity;
_context.SaveChanges();
DbContext.Update Method
For entity types with generated keys if an entity has its primary key value set then it will be tracked in the Modified state. If the primary key value is not set then it will be tracked in the Added state. This helps ensure new entities will be inserted, while existing entities will be updated. An entity is considered to have its primary key value set if the primary key property is set to anything other than the CLR default for the property type.
For entity types without generated keys, the state set is always Modified.
read this article
you can use this sample

Inserting data in a table with a auto increment key C# .NET Linq to SQL

I want to input data into my table (sql 2008) using linq to sql:
public static bool saveEmail(Emailadressen email)
{
TBL_Emailadressen saveMail = new TBL_Emailadressen();
destil_loterijDataContext db = new destil_loterijDataContext();
saveMail.naam = email.naam;
saveMail.emailadres = email.emailadres;
saveMail.lotnummer = email.lotnummer;
try
{
saveMail.naam = email.naam;
saveMail.lotnummer = email.lotnummer;
saveMail.emailadres = email.emailadres;
db.TBL_Emailadressens.InsertOnSubmit(saveMail);
return true;
}
catch (Exception ex)
{
Console.WriteLine("Opslaan niet gelukt!" + ex.ToString());
return false;
}
}
For some reason nothing is being added to this table.
My table has the following fields:
ID (Auto incr int)
Naam (varchar50)
lotnummer (varchar50)
emailadres (varchar50)
My object im trying to save (saveMail) always has an ID = 0 , and i don't know why. I think that is preventing me from saving to the DB?
Be sure to call SubmitChanges on your DataContext-derived class:
using(var dc = new MyDataContext())
{
saveEmail(new Emailadressen(...));
dc.SubmitChanges();
}

Categories

Resources