EF 5 Conditional Mapping - c#

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

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

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 .

How to achieve partial update in Entity Framework 5/6?

I am working on Entity framework with database first approach and I came across below issue.
I have a Customer table with columns col1, col2, col3 ,....,col8. I have created an entity for this table and this table has around 100 records already. Out of above 8 columns, col4 is marked as Non-null.
Class Customer
{
member col1;
member col2;
member col3;
member col4;
.
.
member col8;
}
class Main
{
//main logic to read data from database using EF
Customer obj = object of Customerwith values assigned to col1,col2 and col3 members
obj.col2=some changed value.
DBContext.SaveChanges(); //<- throws an error stating it is expecting value of col4.
}
In my application, I am trying to read the one of the record using the stored procedure using EF and stored procedure only returns col1,col2 and col3.
I am trying to save the modified value of col2 and trying to save back to database using DBContext. But it thows an error stating value of required field col4 is not provided.
FYI: I have gone through couple of forums and question and option to go with disabled verfication on SaveChanges is not feasible for me.
Is there any other way through which I can achieve partial update?
I guess EntityFramework.Utilities satisfies your conditions.
This code:
using (var db = new YourDbContext())
{
db.AttachAndModify(new BlogPost { ID = postId }).Set(x => x.Reads, 10);
db.SaveChanges();
}
will generate single SQL command:
exec sp_executesql N'UPDATE [dbo].[BlogPosts]
SET [Reads] = #0
WHERE ([ID] = #1)
',N'#0 int,#1 int',#0=10,#1=1
disabled verfication on SaveChanges is not feasible for me
Sure it is. You even have to disable validation on Save. But then you can't mark the whole entity as modified, which I think you did. You must mark individual properties as modified:
var mySmallCustomer = someService.GetCustomer(); // from sproc
mySmallCustomer.col2 = "updated";
var myLargeCustomer = new Customer();
context.Customers.Attach(myLargeCustomer);
Entry(myLargeCustomer).CurrentValues.SetValues(mySmallCustomer);
// Here it comes:
Entry(myLargeCustomer).Property(c => c.col2).IsModified = true;
context.Configuration.ValidateOnSaveEnabled = false;
context.SaveChanges();
So you see it's enough to get the "small" customer. From this object you create a stub entity (myLargeCustomer) that is used for updating the one property.

How to retrieve inserted identity in migration of FluentMigrator

During a migration how do i Insert into my table, then retrieve the ID, and then use it to insert a related data in another table.
what i have now is an hardoced ID to insert, but I don't know what it's gonna be when i'll run the migration.
var contactId = 2;
var phoneNumber = 2;
Insert.IntoTable("Contacts")
.WithIdentityInsert()
.Row(new
{
Id = contactId,
TimeZoneId = contact.TimeZoneId,
contact.CultureId,
Type = (byte)(int)contact.Type,
Email = contact.Email.ToString(),
EntityId = entityId
});
Insert.IntoTable("PhoneNumbers")
.WithIdentityInsert()
.Row(new
{
Id = phoneNumberId,
phone.Number,
Type = (byte)(int)phone.Type,
ContactId = contactId
});
I'd like to be able to retrieve the inserted ID and use it for the second insert instead of harcoding it.
I'm using SQL Server if it's any help...
I Thought this would be trivial, but seems like it's not, after googling for it, and not seing any answers here.
You are able to manually insert the Id by chaining .WithInsertIdentity() after your .Row() call.
This will let you keep it in memory for use within other objects as Foreign Keys. Unfortunately, FluentMigrator doesn't actually execute any SQL until after all code within the Up() or Down() methods finish executing.
I use Execute.Sql() with ##IDENTITY in sql-query for same cases

Self Tracking Entities not returning modified object

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.

Categories