Self Tracking Entities not returning modified object - c#

I have the following function to update and insert a record in the table
public DASInput UpdateDASInputTable(DASInput fileSetData, Guid programID)
{
string connectionString = GetConnectionString(programID);
BI_ProgramConfigurationEntities dataContext = new BI_ProgramConfigurationEntities(connectionString);
dataContext.DASInputs.ApplyChanges(fileSetData);
dataContext.SaveChanges(System.Data.Objects.SaveOptions.DetectChangesBeforeSave);
fileSetData = dataContext.DASInputs.FirstOrDefault();
return fileSetData;
}
When I make first call with a new object of type DASInput, then it gets inserted correctly in the database. (DASInput table has the primary key as int with identity specification on).
But this first time insertion does not return the modified value of the primary key of the DASInput table.
So on every subsequent call a new record gets inserted in the database. I want the primary key(self generated by DB) to be returned to the client when the record gets inserted.

Isn't the syntax for adding an entity into a linq controlled database more along the lines of :
context.Table.AddObject(newStore);
//or
context.Table.Add(newStore);
context.SaveChanges();
I do answer this tentatively, not being hugely knowledgeable on LINQ.

Related

Default values doesn't set in SQL Server table when creating by Entity Framework mode

I have lots of tables that contain default values, such as CreatedDateTime (getutcdate()). But right now, the value 0001-01-01 00:00:00.0000000 gets stored instead.
https://stackoverflow.com/a/35093135/7731479 --> that is not effective, I have to do it for each table manually for every database model update (edmx). How can I update all StoreGeneratedPattern to Computed automatically? Or why it does not takes computed automatically?
https://stackoverflow.com/a/43400053/7731479 --> ado.net generates all properties and I can't generate again CreatedDateTime.
Are there any automatic solution?
I am using Entity Framework and ado.net.
Person person = new Person()
{
Id = id,
Name = name,
};
AddToPerson(person);
SaveChanges();
I want to use above. I don't want use the following and assign CreatedDeteTime again because it is assigned in MSSQL with default value getutcdate().
Person person = new Person()
{
Id = id,
Name = name,
CreatedDeteTime = DateTime.UtcNow;
};
AddToPerson(person);
SaveChanges();
The configured default constraint of the SQL Server table will only be applied if you have a SQL INSERT statement that omits the column in question.
So if you insert
INSERT INTO dbo.Person(Id, Name) VALUES (42, "John Doe")
--> then your CreatedDateTime will automatically be set to the GETUTCDATE() value.
Unfortunately, if you have mapped this column in your EF model class, then this is not what happens. If you create an instance of Person in your C# code, and the CreatedDateTime column is in fact part of the model class, then EF will use something like this to insert the new person:
INSERT INTO dbo.Person(Id, Name, CreatedDateTime) VALUES (42, "John Doe", NULL)
and since now NULL is in fact provided for the CreatedDateTime column, that's the value that will be stored - or maybe it's an empty string - no matter what, the column is specified in the INSERT statement and thus the configured default constraint is not applied.
So if you want to let SQL Server kick in with the defaults, you need to make sure not to provide the column(s) in question in the INSERT statement at all. You can do this by:
having a separate model class just for inserts, which does not include those columns in question - e.g. have a NewPerson entity, that also maps to the Person table, but only consists of Name and ID for instance. Since those properties aren't there, EF cannot and will not generate an INSERT statement with them - so then the SQL Server default constraints will kick in
map the INSERT method to a SQL Server stored procedure and handle the inserting inside that procedure, by explicitly not specifying those columns you want to have take on default values
May be I'm wrong, but I have a question.
If you need to save a default date in your DB Table, why you're trying to save another date from programm level? I mean, it's easy to create a procedure and on the procedure level save the date. Something like (select getdate()...).
I have found two solutions:
1- This solution solve for all entities that has same property such as CreatedDateTime
public partial class MyEntities : ObjectContext
{
public override int SaveChanges(SaveOptions options)
{
this.DetectChanges();
foreach (var insert in this.ObjectStateManager.GetObjectStateEntries(System.Data.EntityState.Added))
{
if (insert.Entity.GetType().GetProperty("CreatedDateTime") != null && insert.Entity.GetType().GetProperty("CreatedDateTime").GetType().Name == "DateTime" && (DateTime)(insert.Entity.GetType().GetProperty("CreatedDateTime").GetValue(insert.Entity)) == DateTime.Parse("0001-01-01 00:00:00.0000000"))
insert.Entity.GetType().GetProperty("CreatedDateTime").SetValue(insert.Entity, DateTime.UtcNow, null);
}
return base.SaveChanges(options);
}
}
referance: https://stackoverflow.com/a/5965743/7731479
2-
public partial class Person
{
public Person()
{
this.CreatedDateTime = DateTime.UtcNow;
}
}
referance : DB default value ignored when creating Entity Framework model

How can I UPSERT a row with serial identity in PostgreSQL using ADO.NET?

Given a table like:
CREATE TABLE myTable (
identity SERIAL PRIMARY KEY,
foo VARCHAR(32) NOT NULL
)
I would like to perform an UPSERT on that table using ADO.NET, my POCO in C# looks like:
public class myEntity {
//When null the entity has never been stored in the database
public int? Identity { get; set; }
public string Foo { get; set; }
}
I came up with the following query to use with a DbCommand
INSERT INTO myTable (identity, foo) VALUES (#identity, #foo)
ON CONFLICT (identity) DO UPDATE SET
foo = #foo
RETURNING identity;
The DbCommand is initialized as follows
using (var dbCommand = new NpgsqlCommand(INSERT_QUERY, dbConnection))
{
dbCommand.Parameters.AddWithValue("#identity", oneOfMyEntities.Identity);
dbCommand.Parameters.AddWithValue("#foo", oneOfMyEntities.Foo);
int idReturned = (int)await dbCommand.ExecuteScalar();
if (!oneOfMyEntity.Identity.HasValue) oneOfMyEntity.Identity = idReturned;
}
What I would like to achieve is that when an instance of myEntity has the property Identity set to null a new row is inserted into the database with the identity generated from the sequence, otherwise an update is performed.
The problem is that in PostgreSQL the only way to "force" the use of the next value in the sequence is to either omit the identity column from the INSERT query or to use DEFAULT. I can't simply put the literal "DEFAULT" in the dbCommand parameter #identity because, as far as I know, that would be interpreted as a string value a not the keyword DEFAULT.
I've tried to generate the query text at runtime so that if the entity has not an identity I replace the #identity parameter with DEFAULT, but this doesn't look good to me because I could simply remove ON CONFLICT, have two queries (one INSERT and one UPDATE) and execute an INSERT or an UPDATE depending on whether the entity identity is null or not thus not taking advantage of the ON CONFLICT clause.
How can I write this logic so that when an entity as a null identity I can somehow "force" PostgreSQL to use the next value in the sequence without resorting to generating the query text at runtime (and replacing the #identity parameter with DEFAULT) or using an UPDATE or an INSERT separately depending on the case ?

Unusual behavior of Entity Framework

I have the following code in C#:
public int AddSynonymBL(String[] syns, String word, User user)
{
int dismissedCounter = 0;
foreach (var item in syns)
{
BusinessLayerStatus.StatusBL res = this.dataAccess.AddSynonymDA(item.Trim().ToLowerInvariant(), word.Trim().ToLowerInvariant(), user);
if (res == BusinessLayerStatus.StatusBL.SynonymNotAdded)
++dismissedCounter;
}
int numberOfFailures = dismissedCounter;
return numberOfFailures;
}
And the following code is for AddSynonymDA method:
internal BusinessLayerStatus.StatusBL AddSynonymDA(string synonym, string word, User user)
{
try
{
Synonym newSyn = new Synonym()
{
Meaning = synonym
};
//The following if means that the searched word does not exist int the Searched table
if (this.context.Users.Where(a => a.Mail.Equals(user.Mail)).FirstOrDefault().Searcheds.Where(b => b.RealWord.Equals(word)).Count() == validNumberForKeyValues)
{
this.context.Users.Where(a => a.Mail.Equals(user.Mail)).FirstOrDefault().Searcheds.Where(b => b.RealWord.Equals(word)).FirstOrDefault().Synonyms.Add(newSyn);
this.context.SaveChanges();
return BusinessLayerStatus.StatusBL.SynonymAdded;
}
else
return BusinessLayerStatus.StatusBL.SynonymNotAdded;
}
catch (Exception ex)
{
ExceptionAction(ex);
return BusinessLayerStatus.StatusBL.SynonymNotAdded;
}
}
I am using Entity Framework. I have a table which contains an Id, a word column. Both of them together have unique key constraint in the database. My main code is as follows:
public static void Main()
{
EngineEntities context = new EngineEntities();
BusinessLogic bl = new BusinessLogic();
String[] s = new String[] { "java", "DB" };
Console.WriteLine(bl.AddSynonymBL(s, "Java", new User() { Mail = "media" }));
}
When I add a value which does not exist in the table everything is fine but when I add a value which already exists in the table, calling this.context.SaveChanges(); in the AddSynonymDA method, always throws an exception which was for the previous first exception which caused the first exception and nothing is added to database even if they do not exist in the database. Why is that?
I get the following error which shows that Java already exists. The problem is that Java is for the first call, as the second call, I have passed DB not Java.
{"Violation of UNIQUE KEY constraint 'IX_Searched'. Cannot insert duplicate key in object 'dbo.Searched'. The duplicate key value is (java, 2).\r\nThe statement has been terminated."}
I suspect that you have not set a column to be an Identity column in your database
In other words when you are inserting an entity you need a column to be automatically incrementing.
The way I do this is for example using SQL server:
ALTER TABLE [User] DROP COLUMN [ID];
ALTER TABLE [User]
ADD [ID] integer identity not null;
If you do not have an ID column already you do not need the first line.
After this, update your EF model in your project by deleting the User table and right clicking and Updating Model from Database and select the table.
So now when you insert new entries in you EF model, the ID column will be automatically incremented and you won't get an error.
you must initially check whether the item exists or not, since you seem to have a unique constraint, then you should utilize the attributes of reference in your code .

EF 5 Conditional Mapping

I'm using EF 5 Database first approach in my MVC application. all of my tables uses a Field called Deleted which is a boolean field to mark a record is deleted.
I'm trying to get rid of the requirement of having to check Deleted == false every time I query my database. The very straightforward way of doing this is to use a conditional mapping in the edmx file where EF always return data that are not deleted. That's all good.
But the problem of doing this condition mapping is that, when I want to allow the user to delete some record for e.g Address from their address book I don't have access to Delete field from EF as I used it in the conditional mapping and therefore I have to look for another option to allow user to delete a record.
The way I thought is to create a stored proc that handle the delete query and call it when I want to delete the record.
Is there a better way of doing this? Is it possible to make the Delete field accessible even it is used in the conditional mapping?
I have a working solution for Soft Delete in Entity Framework Code First that may help.
The key is that you add a discriminator to every model that you want to be able to soft delete. In code first that is done like this:
modelBuilder.Entity<Foo>().Map(m => m.Requires("IsDeleted").HasValue(false));
This makes it invisible to the context and therefore you have to do the deletes using sql.
If this is the equivalent of your "conditional mapping" in Database First then one way to modify the sql is to override SaveChanges and run sql from there:
public override int SaveChanges()
{
foreach (var entry in ChangeTracker.Entries()
.Where(p => p.State == EntityState.Deleted
&& p.Entity is ModelBase))//I do have a base class for entities with a single
//"ID" property - all my entities derive from this,
//but you could use ISoftDelete here
SoftDelete(entry);
return base.SaveChanges();
}
private void SoftDelete(DbEntityEntry entry)
{
var e = entry.Entity as ModelBase;
string tableName = GetTableName(e.GetType());
Database.ExecuteSqlCommand(
String.Format("UPDATE {0} SET IsDeleted = 1 WHERE ID = #id", tableName)
, new SqlParameter("id", e.ID));
//Marking it Unchanged prevents the hard delete
//entry.State = EntityState.Unchanged;
//So does setting it to Detached:
//And that is what EF does when it deletes an item
//http://msdn.microsoft.com/en-us/data/jj592676.aspx
entry.State = EntityState.Detached;
}
Method used to Get Table Name explained here
That is the way I used to do it. Probably irrelevant to your Database First approach in EF5, but I have now moved to doing it in stored procedures. EF6 Code First generates CreateStoredProcedure calls in Migration files. I replace these with this.CreateDeleteProcedure("dbo.Foo_Delete", "[dbo].[Foos]"); - which is a call to my own extension method:
public static class MigrationExtensions
{
internal static string DeleteSqlFormat
{
//I also hard delete anything deleted more than a day ago in the same table
get { return "DELETE FROM {0} WHERE IsDeleted = 1 AND DATEADD(DAY, 1, DeletedAt) < GETUTCDATE(); UPDATE {0} SET IsDeleted = 1, DeletedAt = GETUTCDATE() WHERE ID = #ID;"; }
}
internal static void CreateDeleteProcedure(this DbMigration migration, string procName, string tableName)
{
migration.CreateStoredProcedure(
procName,
p => new
{
ID = p.Int(),
},
body:
string.Format(MigrationExtensions.DeleteSqlFormat, tableName)
);
}
}

Exception On Foreign Constraint

I have a table StaffLevelMapping that has a foreign key column, which is a primary key another table AcademicLevel. It raises an exception that the Insert statement conflicted with a foreign key and it has its reference on the foreign key and the table in which it's a primary key. I know this usually happens if the value you are inserting does not exist in it's original table. But this does exist actually. I tried inserting it directly in SQL Server and it worked perfectly. But through my code it doesn't work. What surprises me the most is I used the same insert logic for the rest of my tables and I don't have problems save this one with this very table. Please I need a very quick help on this I have not slept in 2 days because of this error.
Here's my insert code below:
public static StaffLevelMapping AddStafflevel(int staffId, int levelId, bool isEnabled)
{
var context = ObjectContextHelper.CurrentObjectContext;
var staffLevel = context.StaffLevelMappings.CreateObject();
staffLevel.StaffID = staffId;
staffLevel.ID = levelId;
staffLevel.IsEnabled = isEnabled;
context.StaffLevelMappings.AddObject(staffLevel);
context.SaveChanges();
return staffLevel;
}
Many thanks in advance to that special person that can put me out of this misery.

Categories