Linq To Sql - Update a value when another value changes - c#

With Linq To Sql - what is the best way to update the value of one table in a database when a value changes in a different table?
For example in TableA, there is a column called DateModified. TableA has an association to TableB. Now I want to set the value of the DateModified field to the current date every time the record changes in tableA AND everytime a child record is created/updated/deleted in TableB.
How can this be achieved?

If the change was just in the one record, you can use either the On*Changed partial methods, or you can override SubmitChanges on the data-context, call GetChangeSet, and apply the changes just before they are updated. The latter (data-context) approach is useful for catching a broad spectrum of changes, where-as the On*Changed approach is useful for logic specific to individual properties.
However, in the case presented (non-homogeneous updates), I expect a database trigger may be more appropriate.

You could register a PropertyChanged event handler on the child entity in OnCreated (implemented in a partial class) and have that handler update the parent property. If you don't care about the already loaded classes getting the change, using a DB trigger as #Marc suggests would also be an option.
EDIT: If you need to catch deletes as well, you can implement the delete partial method for the data context to call the same handler method to update the parent of the entity being deleted.

Either do Insert/Update/Deletes on TableB via a StoredProcedure which allows you to perform the update on TableA or have Insert/Update/Delete triggers on TableB which updates TableA
I wouldn't try to accomplish this in LINQ as it will involve either overriding various IDE-generated code structures (maintenance nightmare) or extra calls to the Database to get the related TableA item.

Related

Fluent NHibernate - do not return entity if one of its dependencies do not exists

Lets assume that I have table "Cells", table "Divisions" and joining table between them.
Moreover, I can't map table "Cells", because I have "CellsView" which I have to map (it can show cells dependent on current user permissions)
Next.. there can be a situation when Division is assigned to one cell but current user wont see that cell if he want to list them.
While retrieving list of Divisions, our user gets exception that says that expecting Cell with ID "1" does not exist. (ID is correct and Cell exists but our View does not allow to show that cell)
How can I prevent Divisions list from throwing an exception (lazy loading is enabled) **and from showing division which would throw that exception **?
I've tried to null current entity on OnPreLoad event and OnPostLoad event
if my Cell entity cannot be resolved but it doesn't work. All divisions entities are returned and while reading them fluent tries to resolve Cell and throws exception.
That CellsView connection is used in many places inside application, so generic solution like this one with PostLoad events would be great to deal with it globally.
I am not using Fluent but if it duplicates what .hbm.xml mappings can do, you should be able to define and apply a filter on your collection. This should allow you to handle your case.
Filters allows to define some parameterized restriction on the elements of an entity collection. You can parameterize and activate filters after session opening, where you should know who is your user.
Indeed, filters can be defined outside of mappings, so even if Fluent does not handle them, you should still be able to use them.
Example from Nhibernate reference documentation:
ICollection<Cat> blackKittens = session.CreateFilter(
pk.Kittens, "where this.Color = ?", Color.Black, NHibernateUtil.Enum(typeof(Color))
).List<Cat>();
More details after comments:
Filters should be able to navigate sub-entities too. But as your case is a bit wicked (foreign key is defined but foreign entity will not be found), you should test a non-nullable property of your sub-entity, but not its primary key. (Otherwise NHibenate will very likely simplify it by a test on parent foreign key.)
"where this.Cell.SomeNonNullableProperty is not null"
This would be translated to SQL and should be executed without exceptions, and would filter out your non-accessible divisions.
By the way, filters may not be the answer for you, if all you do to get Division lists is explicitly querying them. (Not getting them through some over entities referencing them as a collection.) In such case, simply use above condition directly on your queries.
For only addressing the exception, you may also tinker your mapping with the not-found="ignore|exception" option of many-to-one relations. But I am not sure it plays well with lazy-loading, I have never tried it. (And I do not know if this option is available with fluent.)

how does .AttachAll() work for updating and inserting

This question is pretty simple , but I can't seem to get a clear answer from MSDN docs.
I have a list of objects that map to a table in my database. Some of these objects have the ID filled out, some don't. I want to use something like
myContext.myTable.AttachAll(myList);
now , I was hoping that the objects that have an ID - which is Primary Key in the database will get attached and perform an update to those rows, and my objects that don't have an ID will be treated like an INSERT , and the db will automatically create an ID. Is that how this works?
Taken from documentation:
This method attaches all entities of a collection to the DataContext
in either a modified or unmodified state. If attaching as modified,
the entity must either declare a version member or must not
participate in update conflict checking. If attaching as unmodified,
the entity is assumed to represent the original value. After calling
this method, the entity's fields can be modified with other
information from the client before SubmitChanges is called. For more
information, see Data Retrieval and CUD Operations in N-Tier
Applications (LINQ to SQL).
When a new entity is attached, deferred loaders for any child
collections (for example, EntitySet collections of entities from
associated tables) are initialized. When SubmitChanges is called,
members of the child collections are put into an Unmodified state. To
update members of a child collection, you must explicitly call Attach
and specify that entity.

DbContext with read-only set properties

I was reading this article http://blogs.msdn.com/b/adonet/archive/2011/01/27/using-dbcontext-in-ef-feature-ctp5-part-2-connections-and-models.aspx and was trying to figure out how to create private setters (the section in the article DbContext with read-only set properties is right before the summary). How would you create private setters? I was playing around with different methods but nothing seemed to work. I am doing this because I need to group the original table based on a query I have because the original table is a heap and I need a primary key for the entity. So anytime a client asks for this table it is already grouped. Not even sure if this is the correct way to do that. Thanks.
EDIT: sorry for being vague. I am doing code first. For example there exists a SQL Table with JobNbr, Qty and Date and I need to group by JobNumber, sum on Qty and take the oldest expiration date, and that will be my entity since this table has no primary key. The way I am doing it now gives me the error below from a method I created in the DbContext class. I do have a EntityTypeConfiguration class. Do I do this in that class?
EDIT: : you might be wondering why I am doing this. Basically I need to get data from the heap and save it in another database. My original approach was database.SqlQuery() to get grouped rows from the heap, but sometimes I have too many parameters for execute_sql. So I decided to create an entity for the grouped query without tracking changes (since all I am doing is reading from the table and saving to another DB). See my post here with the issue I am having https://stackoverflow.com/questions/22106030/entity-framework-6-this-database-sqlquery-character-limitation-with-sp-executes. The only way I know to get around it is to create an entity (even though in this case the entity is a query and not a table).
The entity or complex type
' cannot be
constructed in a LINQ to Entities query.

Entity Framework doesn't update value which is modified by a trigger

My table Sections (SQL Server) has ID as a primary key (int, identity) and SortIndex column (int) for sorting purposes.
The database has a trigger which sets SortIndex := ID at each INSERT. Obviously I want to change the sorting index later, by swapping the values for two rows.
I access the data using Entity Framework, all with MVC3 web application.
The problem is, Entity Framework doesn't update the value of SortIndex after I insert a new object into the table. It also caches all the data, so the following call to get all objects from this table will also give wrong SortIndex value for this object.
I tried changing StoreGeneratedPattern for this column in EDMX. This seems to be great and elegant but doesn't solve the problem.
If I set to Identity, it causes EF to properly update the value, but it becomes read only (exception thrown when trying to change). Setting it to Computed is similar, but instead of exception being thrown the values are just not written to the DB.
I can recreate the EF object every time if I need to use it after inserting an object, just by doing:
DatabaseEntities db = new DatabaseEntities()
But it seems like ugly workaround for me.
What's a solution to this problem?
Obviously something, what doesn't require me to do some action after every insert (and take a risk that it's forgotten and unnoticed) is preferred.
In short StoreGeneratedPattern means: the value is handled by the store and your application will never modify it. In such case you will get store generated value automatically after you call SaveChanges.
If you don't use StoreGeneratedPattern you will not get value and you will have to force another query execution to refresh your entity. You can for example do:
objectContext.Refresh(RefreshMode.StoreWins, yourSection);
Generally situations where you need to update values in both database through triggers and application don't play very nicely with EF (and probably also other ORM tools).
I found the answer from 'Ladislav Mrnka' being exact and marked it as accepted. Here are other workarounds, which I found while trying to find some solution. However, the solution I was looking for is in general not possible.
One of possibilities is to set StoreGeneratedPattern = Computed to let EF know, this value is calculated. And then, make a Stored Procedure to actually change the value of SortIndex. Typically it would change values in two rows (swap them), to change the sorting order. This procedure along with a trigger at INSERT gives guarantee the data stays consistent in the DB. It's not possible to create new row without proper value set in SortIndex, it's not possible to make two objects have the same value (unless stored procedure has a bug) and it's not possible to manually break the value somehow, because it's not possible to edit through EF. Looks like a great solution.
It's easily possible to have stored procedures mapped to functions in EF.
The problem is, it's now fine to enter a new row and EF properly updates data in its cache, but the cache is not updated after calling the stored procedure. Still some manual updated or refresh function is needed. Otherwise the following call to get objects sorted by SortIndex will give wrong results.
Other than that, it's possible to set MergeOption = MergeOption.OverwriteChanges for several entities, which causes EF to update data from the DB somewhat better. With this being done, it's possible to reread the object after inserting it or calling stored procedure and it will get refreshed. However, reading a collection of objects with db.Section.OrderBy(o => o.SortIndex) will still return cached results with wrong sorting order.
If anyone is interested, it's possible to make MergeOption default to something else by adding EF partial class and then partial method OnContextCreated, like here:
public partial class DatabaseEntities
{
partial void OnContextCreated()
{
Subsection.MergeOption = MergeOption.OverwriteChanges;
Section.MergeOption = MergeOption.OverwriteChanges;
Function.MergeOption = MergeOption.OverwriteChanges;
}
}
Do you know if you'll work with that column again in the same request?
I would use the context per request scenario, which usually gets you out of many problem, because a new EF context is created with every request, so you have a fresh data once per request.
With long lived context, there can grow incosistencies as you described.
Anyways the StoreGeneratedPattern setted to computed should be right. But it updates itself only when you're storing the actual entity. It's not getting updated by inserting or updating any other entity.
from http://msdn.microsoft.com/en-us/library/dd296755(v=vs.90).aspx
If you create a new entity or change an existing entity, the values of properties with StoreGeneratedPattern set to Computed are retrieved from the server when you call the SaveChanges method in your application.
If you assign a value to a property with StoreGeneratedPattern set to Computed in your application, the value will be overwritten with the server-generated value when you call the SaveChanges method.
We're using the computed value option for SQL sequenced GUID, and it's working OK.
I had a similar situation with a Sql Server Quote table with a varchar QuoteNumber column that is a non-primary unique key whose value is generated by an after-insert trigger. The trigger is used because the generated value is derived by fetching data from a foreign key table. Sql Server schema identity declarations do not allow you to pull information from other tables.
I'd like EF to treat this varchar column like an identity and do nothing to it on update and reread it after insert. EF will do so if there is a .HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity) property to a non-identity column in the code it generates to configure the Entity like so (scroll right):
public QuoteConfiguration(string schema)
{
ToTable("Quote", schema);
HasKey(x => x.ID);
Property(x => x.ID).HasColumnName(#"ID").HasColumnType("int").IsRequired().HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity);
Property(x => x.QuoteNumber).HasColumnName(#"Quote_Number").HasColumnType("varchar").IsOptional().IsUnicode(false).HasMaxLength(64).HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity);
}
My EF model is code first and generated by Simon Hughes' EntityFramework Reverse POCO Generator. At first, I could not figure out how to make the generator add this property to a column that is not declared as an identity in Sql Server.
Rereading the entire Quote entity after insert did not retrieve the auto-generated QuoteNumber. Then I discovered that re-reading just the QuoteNumber column after insert defeated the entity cache. But, I felt dirty doing it.
Finally, I worked with Simon Hughes to discover how to get his EF Reverse POCO to do it for me. You just extend the UpdateColumn function in your *.tt file like so:
Settings.UpdateColumn = (Column column, Table table) =>
{
if (table.Name.Equals("Quote", StringComparison.InvariantCultureIgnoreCase)
&& column.Name.Equals("Quote_Number", StringComparison.InvariantCultureIgnoreCase))
{
column.IsStoreGenerated = true;
}
}

LINQ to SQL: Change timestamp forcefully of a table

I am using LINQ to SQL and require some help in updating a timestamp column in a table.
Car candidate = Context.Cars.Where(c => c.CarID == car.Id).SingleOrDefault();
candidate.CarName = carToUpdate.CarName;
candidate.CarDescription = carToUpdate.CarDescription;
candidate.IsActive = carToUpdate.IsActive;
candidate.IsCab = carToUpdate.IsCab;
candidate.StockTypeId = carToUpdate.StockTypeId;
Context.SubmitChanges();
If there are no changes in the properties of the car entity, the timestamp is not changed.
(Looks like LINQ to SQL is intelligent enough to not send an update to database).
Is there anyway to forefully change timestamp from LINQ to SQL??
Please help.
Thanks.
How about adding a LastUpdatedDate column and setting that to DateTime.Now , that should ensure it will always do an update and will then change your timestamp column.
Wouldn't a trigger on the SQL table be an option?
EDIT: I think this thread answers your question force LinqToSql to submit
I think the answer is don't.
If the entity's properties are the same after you modify them as they are when you pulled the entity out of the db, it hasn't actually changed.
What's the purpose of forcing a new timestamp to generate when the row was in fact not updated?
I was reading through Object States and Change-Tracking (LINQ to SQL) and it states:
You can detect Updates by observing
notifications of changes.
Notifications are provided through the
PropertyChanging event in property
setters. When LINQ to SQL is notified
of the first change to an object, it
creates a copy of the object and
considers the object a candidate for
generating an Update statement.
For objects that do not implement
INotifyPropertyChanging, LINQ to SQL
maintains a copy of the values that
objects had when they were first
materialized. When you call
SubmitChanges, LINQ to SQL compares
the current and original values to
decide whether the object has been
changed.
Would you be able to create a partial class that adds a property that calls PropertyChanging to update a the property that isn't mapped and so only exists in code?

Categories