Get mapped delete proc to use changed Entity data - entity-framework

I am trying to get a mapped delete procedure in EF (5.0, database first) to use updated properties in the entity as parameters.
The mapped procedure takes two parameters:
DeleteRow:
#Id : int -> (Key) Id : int32
#Modifiedby : char -> Modifiedby : string
In the controller I want to change the Modifiedby value before the delete procedure is called.
Subscription subscription = context.Subscription.Find(id);
subscription.Modifiedby = "Test";
context.Subscription.Remove(subscription);
context.ChangeTracker.DetectChanges();
context.SaveChanges();
However, when the procedure is called it is always the old value of Modifiedby that is passed to the delete procedure.
I don't want to do an update call to the database before deleting the entity.

Ended up solving this by using none-tracked entities. Got the idea from this blog post: http://blogs.msdn.com/b/alexj/archive/2009/03/27/tip-9-deleting-an-object-without-retrieving-it.aspx
The AsNoTracking() method call is added and after the modifications the entities are attached to the context and then removed.
Subscription subscription = context.Subscription.AsNoTracking()
.Include(i => i.RelatedEntity)
.FirstOrDefault(c => c.SubscriptionId == id);
subscription.Modifiedby = "TEST-Subcription";
subscription.RelatedEntity.ToList().ForEach(f => f.Modifiedby = "TEST_Related");
context.Subscription.Attach(subscription);
context.Subscription.Remove(subscription);
context.SaveChanges();
This allows the new Modifiedby values to be sent to the mapped delete procedure. I can also set new values for the related entities, which also have mapped procedures and for which cascade deletion is set.

Related

Get Record ID in Entity Framework 5 after insert

I realize this must be a relatively simple thing to do, but I'm not getting what I'm looking for with Google.
I need to get the record ID of the record I just saved using the Entity Framework. With SQL queries we used "Select ##IDENTITY as 'Identity';"
If anyone can help it would be greatly appreciated.
The default behavior of Entity Framework is it sets identity fields on entities from the database right after SaveChanges is called.
In the following sample code, before SaveChanges is called, my employee has a default ID of 0. After SaveChanges my employee has a generated ID of 1.
using (TestDbEntities context = new TestDbEntities())
{
Employee e = new Employee ();
e.FirstName = "John";
e.LastName = "Doe";
context.Employee.Add(e);
context.SaveChanges();
Console.WriteLine("Generated ID: {0}", e.ID);
Console.ReadKey();
}

EF delete entity with unloaded navigation property

I have an Entity. Mandate. Every mandate has a required:many relation to a Person (NavigationProperty). I use the DbContext API with (LazyLoadingEnabled, AutoDetectChangesEnabled, ValidateOnSaveEnabled, ProxyCreationEnabled)
Now I like to delete a Mandate entity. The mandate entities are loaded by another context with AsNoTracking().
message.Result.
ObserveOn(On<DataComposition>.Scheduler).
Where(r => r).
Subscribe(_ =>
{
using (var unit = UnitOfWork.Begin())
{
var mandate = this.SelectedItem.OriginalEntity;
this.mandateRepository.Attach(mandate);
// mandate.Person.ToString();
this.mandateRepository.Delete(mandate);
unit.Commit();
}
this.List.RemoveOnUi(this.SelectedItem);
});
Now during committing I get the following exception: Entities in 'CodeFirstContainer.Mandates' participate in the 'Mandate_Person' relationship. 0 related 'Mandate_Person_Target' were found. 1 'Mandate_Person_Target' is expected.
The delete works if I include the Person Property during the population/selection or if I visit the Property (lazyloading), but I DONT LIKE to materialize/hold many entities only for the deletion case and I DONT LIKE to trigger more than a single DELETE query to db!
The fact that, if you have the navigation property mandate.Person populated, the following SQL statement ...
delete [dbo].[Mandates]
where (([Id] = #0) and ([PersonId] = #1))
... is sent to the database, lets me think that the navigation property indeed must be populated with a person with the correct PersonId to delete the parent.
I have no idea why Entity Framework just doesn't send a delete statement with the primary key ...
delete [dbo].[Mandates]
where ([Id] = #0)
... as I had expected.
Edit
If the Mandate entity has a foreign key property PersonId for the Person navigation property, the expected SQL (the second above) is sent to the database. In this case the Person navigation property can be null and the value of the FK property PersonId doesn't matter.
Edit 2
If you don't want to introduce a FK property the way with the least DB-roundtrip-costs would probably be to fetch the person's Id and then create a dummy person with that key in memory:
// ...
var personId = context.Mandates
.Where(m => m.Id == mandate.Id)
.Select(m => m.Person.Id)
.Single();
mandate.Person = new Person { Id = personId };
this.mandateRepository.Attach(mandate);
this.mandateRepository.Delete(mandate);
// ...

Entity Framework Insert, Update, Delete During Same Transaction

I have a problem, I have a wizard that can update data AND insert data. So, if I have an existing list of team members, I can update their roles but If necessary, I can also add/insert a person to this team. So I can update roles and insert a new team member into the same table all during the same transaction. Data can be updated to and inserted to table teamMembers.
When I try to add a new teamMember, I also have an existing member where I simply want to update his role.
Both changes happen to the same table called TeamMember. When I debug the context, everything looks good. it shows that there are two changes that will occur for the TeamMember table. One transaction is the update and the other transaction is the insert. When I perform an update using:
var teamMember = new TeamMember
{
Name = user.FullName,
UserProfileId = user.UserProfileId,
RoleId = user.RoleId
};
TeamMemberList.Add(teamMember);
project.TeamMembers = TeamMemberList;
//And then call
this.Context.Projects.Attach(project);
this.Context.Entry(project).State = System.Data.EntityState.Modified;
it updates but the record that needs to be inserted fails.
HOW CAN I DO BOTH AN INSERT AND UPDATE TO THE SAME TABLE DURING THE SAME TRANSACTION?
CURRENT ERROR IS:
The changes to the database were committed successfully, but an error occurred while updating the object context. The ObjectContext might be in an inconsistent state. Inner exception message: A referential integrity constraint violation occurred: The property values that define the referential constraints are not consistent between principal and dependent objects in the relationship.
I think you need to add the TeamMember entity to the context's global list. Something like:
var teamMember = new TeamMember()
{
Name = user.FullName,
UserProfileId = user.UserProfileId,
RoleId = user.RoleId
}
project.TeamMembers.Add( teamMember );
this.Context.TeamMembers.Add( teamMember );
this.Context.SaveChanges();
How about loading the existing project entity first and then adding members.
var project = this.Context.Project.Where(p => p.ID = "bar").Include("TeamMembers").FirstOrDefault();
var teamMember= new TeamMember
{
Name = user.FullName,
UserProfileId = user.UserProfileId,
RoleId = user.RoleId
};
project.TeamMembers.Add(teamMember);
this.Context.SaveChanges()

EF 4.0 Entity does not pick up new values after insert (select entity after insert)

I am using Entity Framework 4.0 POCO entity
I have mapped custom stored procedure on insert
PROCEDURE [dbo].[usp_MyTable_Insert]
(
#Value1 char(1),
#Value2 varchar(5),
#Value3 varchar(20)
....
)
AS
BEGIN TRANSACTION
INSERT INTO "dbo"."MyTable"
(
"Value1",
"Value2",
"Value3"
)
VALUES
(
#Value1,
#Value2,
#Value3
)
DECLARE #Id int
--Get the latest Id.
SET #Id = ( SELECT CAST(##IDENTITY AS INT) )
--update the table with the some values
UPDATE "dbo"."MyTable"
SET Value3 = ( SELECT SomeTableColumn
FROM SomeTable
WHERE Something = Something
)
WHERE [Id] = #Id
COMMIT TRANSACTION
SELECT #Id AS "Id"
END
It is inserting entity into database and then updating some of the columns in database
then returning identity. All pretty simple.
public int InsertRecord(RecEntity recEntity)
{
context.AddObject("RecEntities", recEntity);
context.SaveChanges();
return recEntity.Id;
}
Method insert working well.
Then i need to update current entity with values which stored procedure inserted.
I have method in my repository to retrieve data
public RecEntity SingleRecEntity(Expression> where)
{
return context.RecEntities.Single(where);
}
When i am calling this method values values inserted by stored procedure doesn't come to entity.
id = repository.InsertRecord(recEntity);
recEntity = repository.SingleBrokerPreRegistration(x => x.Id == id); // new values didnt come here from database
I run the query generated by entity framework in query analyzer, it is returning all up to date values.
But fore some reason datacontext don't want to update this entity.
Probably there is should be some ways to change this.
May be some one may explain this behaviour.
Need help.
Try the Refresh method with the StoreWins parameter.
EF does not refresh the values in case there is already an attached object with Entity Key specified unless the Refresh method is not called explicitly
If you run .Load(Objects.MergeOption.OverwriteChanges) on the collection you'll get any newly added items. If you want the deleted items to be "refreshed" you'll need to detach the entities from the collection before running .Load
Putting it all together (sorry about the vb)
For Each child in Parent.ChildCollection.ToArray()
context.Detatch(child)
Next
Parent.ChildCollection.Load(Objects.MergeOption.OverwriteChanges)
This works for me, but if there's a more elegant way I'd love to see it!

How to create and store a (self-tracking) entity object on the server side?

I am trying to achieve the following using Entity framework 4.0 and self-tracking entities:
1) The client application request a book form the server by providing an ISBN number
2) The server performs a query on its database to see if the book is already present
3a) If the book is in the database, it returns it.
3b) If the book is not in the database, it will query Amazon for info, extract the required attributes, create a new book, store it in the database, and return it to the client
Now, 3b) is where the problems are... I can't find any information on how I can create an entity object (a book) on the server side, add it to the context and store it in the database. I have tried all sorts of things:
public class BookBrowserService : IBookBrowserService {
public Book GetBook(string ISBN) {
using (var ctx = new BookBrowserModelContainer()) {
Book book = ctx.Books.Where(b => b.ISBN == ISBN).SingleOrDefault();
if (book == null) {
book = new Book();
book.ISBN = ISBN; // This is the key
book.Title = "This title would be retrieved from Amazon";
Author author = new Author();
author.Name = "The author's name would be retrieved from Amazon";
book.Authors.Add(author);
ctx.Books.AddObject(book);
ctx.SaveChanges(); // This one always throws an exception...
}
return book;
}
}
}
Could anyone tell me what I am doing wrong?
It looks like the problem is related to the EDMX model.
I have a Book entity and an Author entity, with a many-to-many relationship.
The Book entity's Key is ISBN, which is a string of Max length 13.
StoreGeneratedPattern is set to None.
The Author entity's Key is Id, which is a Guid.
StoreGeneratedPattern is Identity.
The exception message is:
"Cannot insert the value NULL into column 'Id', table 'BookBrowser.dbo.Authors'; column does not allow nulls. INSERT fails. The statement has been terminated. "
But since StoreGeneratedPattern is set to Identity, shouldn't an Id value be created automatically?
Thanks,
Peter
It looks that the problem was that I used a Guid as Key in combination with StoreGeneratedPattern = Identity.
When I set StoreGeneratedPattern to None and create my own Guid using Id = Guid.NewGuid(), the problem is gone.
Apparently, the SQL server cannot generate Guids...
you can use StoreGeneratedPattern=Identity, but generated sql script based on your edmx doesn`t contain newid() in describing primary key(GUID). you can do this manually in generated sql script. 'BookId uniqueidentifier NOT NULL
DEFAULT newid()'. So id value will create GUID automatically.