I have trigger on table A to change the column value on table B during new row insertion on table A.
Due to current design I am getting RowVersion error. So I am thinking to perform current trigger functionality on entity SubmitChanges. I believe that INotifyPropertyChanged fired on property change, in my case will be fired on table A property change, which is I not what I want. I want to perform operation on table A row insertion to update value on table B.
Anyone have idea what is better way to handle it?
You can overwrite SaveChanges, get all entities to be inserted to table A and perform your updates on entities from table B.
public override int SaveChanges(SaveOptions options)
{
foreach(TableA a in ObjectStateManager.GetObjectStateEntries(EntityState.Added)
.Where(e => !e.IsRelationship)
.Select(e => e.Entity)
.OfType<TableA>())
{
// Here you can get your TableB entity and do an update
// If you don't have those entities already loaded you will load
// them one by one so it will probably need some additional
// adjustments for better performance
}
return base.SaveChanges(options);
}
Related
I found related question but my issue seems to be different.
Running the following code:
var dbitem = context.MyDatabaseItems.Single(p => p.Id == someId);
context.Update(dbitem);
context.SaveChanges();
Results in "Cannot update identity column 'Id'". Table behind is a bit special. "Id" is NOT the primary key for different reasons. Primary key consists of combination of other fields. No matter what I do: detaching, reattaching etc etc the existing item I am unable to save the entity even if I do not change it (see the code).
However this Id is unique and auto generated.
The builder is the following:
builder.Property(p => p.Id)
.ValueGeneratedOnAdd();
builder.HasKey(p => new { p.BusinessDay, p.ClientId, p.Version });
BusinessDay is dateTime, CLientId and Version are integers.
What is going on here?
There are two metadata properties which control the update behavior called BeforeSaveBehavior and AfterSaveBehavior.
For auto generated keys the later is assumed to be Ignore, i.e. never update. For non key auto generated properties it must be configured explicitly (note that there is no fluent API for that so far, so you have to use the metadata API directly), e.g.
// First define the new key
builder.HasKey(p => new { p.BusinessDay, p.ClientId, p.Version });
// Then configure the auto generated column
// This (especially the `SetAfterUpdateBehavior` call) must be after
// unassociating the property as a PK, otherwise you'll get an exception
builder.Property(p => p.Id)
.ValueGeneratedOnAdd()
.Metadata.SetAfterSaveBehavior(PropertySaveBehavior.Ignore); // <--
This does not change the database schema (model), hence no migration is needed. Just the EF Core update entity behavior.
I have a table, call it Transaction, and a view, call it TransactionExtra, which is based on Transaction, e.g., create view TransactionExtra select t.id as t_id, x.* from Transaction t join X x on ....
In Entity Framework, I'd like to be able to access TransactionExtras objects from Transaction objects, ("create a navigation property") e.g., transaction.TransactionExtra, just as if TransactionExtra were a table with a foreign key to T.
How can I do this in the EDMX visual editor?
At present I'm doing it manually:
static TransactionExtra extra(Entities db, transaction t)
{
return db.TransactionExtra.Where(x => x.t_id == t.id).FirstOrDefault();
}
For every Transaction there is zero or one TransactionExtra.
I can't seem to figure out how to tell EntityFrameworkEDMX to map the property.
Thanks.
I am using EntityFramework in my Apllication. If I want to fetch one table value means that will return values with all the referenced table values. It takes time to fetch all the table values. If I need one specified reference table values means
How do I Prevent fetching other reference table values?
If you want to extract single table record without reference tables so you need to define LazyLoadingEnabled false in your DBContext class.
Example your database name is EmpReview
public class UserDataLayerContext : DbContext
{
public UserDataLayerContext()
: base("name=EmpReview")
{
this.Configuration.LazyLoadingEnabled = false;
}
So all the DBSet classes which in UserDataLayerContext filter time only get the single table record without any reference class.
Using this.Configuration.LazyLoadingEnabled = false; none of return reference table calss but in that you need to get single reference table check below example,
Suppose one table class is UserMaster and it is link to GroupMaster master
So
return context.UserMasters.Include("GroupMaster").Where(x => (x.UserID == id) && (x.IsActive ==
true)).ToList();
In that case you get all the usermaster record with group master table.
And
return context.UserMasters.Where(x => (x.UserID == id) && (x.IsActive == true)).ToList();
above case you get all the record of usermaster with out group master table.
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);
// ...
I have run into an interesting problem with Entity Framework and based on the code I had to use to tackle it I suspect my solution is less than ideal. I have a 1-to-Many relationship between Table A and Table B where entities in TableB have a reference to TableA. I have a scenario where I want to simultaneously delete all children of a row in TableA and I thought this could be achieve by simply clearing the collection:
Entity.Children.Clear()
Unfortunately, when I attempted to save changes this produced as a Foreign Key violation.
A relationship is being added or
deleted from an AssociationSet
'FK_EntityB_EntityA'. With cardinality
constraints, a corresponding 'EntityB'
must also be added or deleted.
The solution I came up with was to manually delete object via the entity context's DeleteObject(), but I just know this logic I am using has got to be wrong.
while (collection.Any())
Entities.DeleteObject(collection.First());
For one, the fact that I had to use a Where() loop seems far less than ideal, but I suppose that's purely a semantic assessment on my part. In any case, is there something wrong with how I am doing this, or is there perhaps a better way to clear a child entity collection of an entity such that Entity Framework properly calls a data store delete on all of the removed objects?
Clear() removes the reference to the entity, not the entity itself.
If you intend this to be always the same operation, you could handle AssociationChanged:
Entity.Children.AssociationChanged +=
new CollectionChangeEventHandler(EntityChildrenChanged);
Entity.Children.Clear();
private void EntityChildrenChanged(object sender,
CollectionChangeEventArgs e)
{
// Check for a related reference being removed.
if (e.Action == CollectionChangeAction.Remove)
{
Context.DeleteObject(e.Element);
}
}
You can build this in to your entity using a partial class.
You can create Identifying relationship between parent and child entities and EF will delete child entity when you delete it from parent's collection.
public class Parent
{
public int ParentId {get;set;}
public ICollection<Child> Children {get;set;}
}
public class Child
{
public int ChildId {get;set;}
public int ParentId {get;set;}
}
Mapping configuration:
modelBuilder.Entity<Child>().HasKey(x => new { x.ChildId, x.ParentId });
modelBuilder.Entity<Parent>().HasMany(x => x.Children).WithRequired().HasForeignKey(x => x.ParentId);
Trick: When setting up the relationship between Parent and Child, you'll HAVE TO create a "composite" key on the child. This way, when you tell the Parent to delete 1 or all of its children, the related records will actually be deleted from the database.
To configure composite key using Fluent API:
modelBuilder.Entity<Child>().HasKey(t => new { t.ParentId, t.ChildId });
Then, to delete the related children:
var parent = _context.Parents.SingleOrDefault(p => p.ParentId == parentId);
var childToRemove = parent.Children.First(); // Change the logic
parent.Children.Remove(childToRemove);
// you can delete all children if you want
// parent.Children.Clear();
_context.SaveChanges();
Done!