Replacing/Updating record in database without null values? - asp.net

I am using the .AddOrUpdate-method for taking a model(Entity) and updating the existing record in my database. The problem is that I don't want the null values coming from the model to overwrite the existing values in the record. It's suppose to only overwrite the values that differ from the existing ones except the null-values coming form the model. What method/technique should I use for this. I dont wanna retrieve the record from the db and replace the null-values in the new object before putting it back into the db in order to do this.
Alos I don't wanna be forced to update single values in the record since several values in the model may be changed. Therefore I'm sending the entire object instead of sending values as parameters to the method executing the database commands.
Thanks alot. I hope my description makes some sence... Otherwise I'll try to elaborate :)

Related

How to introduce a new column in dynamo DB running in production?

I have a use case where DynamoDB is running in production and I need to add a new column IDUpdatedAt which will also be serving as a sort key for one of the GSIs.
I tried a thing in test where my application adds the new rows with IDUpdatedAt, it's working fine but what about the existing rows? How to add the values for those?
Also the new rows will not be added without IDUpdatedAt, but how will the search be impacted for older rows?
PS: IDUpdatedAt is being used as a filter in the application, i.e., user can search for specific ID and can get results sorted by date. That's why IDUpdatedAt is also a part of GSI (sort key).
Please help.
You've got the right idea by adding the field to new items. After all, DynamoDB does not enforce a particular schema outside of the primary key.
This also happens to be a very useful feature, especially when defining a GSI on that attribute; if the atttibute exists on the item, it ends up in the index! For example, imagine modeling an email inbox in DDB where each item represents an email. You could include an attribute 'is_read' and define a GSI using that atttibute.
If the 'is_read' attribute exists on the item, it's in the index. Otherwise, it's not. A cool way to use GSIs to implement filtering.
Pretty neat stuff!
However, there is no way to retroactively update all items with a new attribute other than manually updating each item (or in batches). The equivalent in SQL databases is defining a new column. Unfortunately, an analogous operation in DDB does not exist.

Conditional insert in Dynamodb

I am creating a leave tracker app where I want to store the user ID along with the from date and to date. I am using Amazon's DynamoDB as the database, and the user enters a leave through a custom command.
Eg: apply-leave from-date to-date
I want to avoid duplicate entries in the database. For example, if a user has already applied for a leave between 06-10-2019 to 10-10-2019 and applies for a leave between the same dates again, they should get a message saying that this already exists and a new record should not be created for the same.
However, a user can apply for multiple leaves and two users can take a leave between the same dates.
I tried using a conditional statement as follows:
table.put_item(
Item={
'leave_id': leave_id,
'user_id': user_id,
'from_date': from_date,
'to_date': to_date,
},
ConditionExpression='attribute_not_exists(user_id) AND attribute_not_exists(from_date) AND attribute_not_exists(to_date)'
)
where leave_id is the partition key. However, this does not work and a new row is added every time, even if it is the same dates. I have looked through similar other questions, but haven't been able to understand how to get this configured correctly.
Any ideas on how I should go about this, or if there is a different design that I should follow?
If you are calling your code with the leave_id that doesn't yet exist in the table, the item will always be inserted. If you call your code with leave_id that does already exist in your table you should be getting An error occurred (ConditionalCheckFailedException) when calling the PutItem operation: The conditional request failed error message.
I have two suggestions:
If you don't want to change your table, you can create a secondary index with user_id as the partition key and then query the index for all the items where the given user has some from_date and to_date attributes.
Like this:
table.query(
IndexName='user_id-index',
KeyConditionExpression=Key('user_id').eq(user_id),
FilterExpression=Attr('from_date').exists() & Attr('from_date').exists()
)
Then you will need to check for overlapping leave requests, etc. (eg. leave request that starts before the one that is already in place finishes). After deciding that the leave request is a valid one you will call put_item.
Another suggestion and probably a better one would be to create a composite primary key on your table with user_id as a partition key and leave_id as a sort key. That way you could execute a query for all leave requests from a particular user without the need to create a secondary index.

How to handle an exception in VB code if the stored procedure doesn't return one of the fields?

I have a VB function which calls a stored procedure to get 2 kinds of reports (actives users/inactive users). The stored procedure returns the data (columns) based on the report we choose. If we choose to get active users report, the sproc doesn't return an Inactive_Date for the users, but it will return the same field, Inactive_Date field for Inactive users report. I'm getting an error when I choose to get active users report, because the sproc doesn't return Inactive_Date field, but my VB.NET code is same for both active/inactive users reports. Here are some of the ways I tried to resolve, but no luck.
If IsDBNull(dr("Inactive_Date")) Then
Result.Inactive_Date = Nothing
Else
Result.Inactive_Date = SafeStr(dr("Inactive_Date"))
End If
and
If IsDBNull(dr("Inactive_Date")) Then
Result.Inactive_Date = DateTime.Now
Else
Result.Inactive_Date = SafeStr(dr("Inactive_Date"))
End If
You're getting the exception because you're basically trying to access a dictionary entry that doesn't exist. You have a few options.
Have your stored procedure for Active Users return Inactive_Date as null. This would allow you to use code you have posted in VB with no changes.
Create a function that checks if the datareader has that column before you attempt to access it, as in this answer: Check for column name in a SqlDataReader object - this would be akin to checking if the dictionary has the key first
Write two separate functions in your VB code - one for the active report and one for the inactive report. Or, add a parameter to your function to check if this is a request for an active user, and only try to access the "Inactive_Date" field if that parameter is set to true.
If it were up to me, I'd update the stored procedure (option 1). That would probably be the least amount of work, shouldn't hurt anything semantically (unless you have queries that SELECT * from the results of that procedure), and requires no other changes to your codebase. The only other problem I can see is confusion that might arise from having a function in your VB code that's used for both active and inactive users.
The second option will involve iterating through all the fields, and that just feels wrong to me (seems like it's asking for performance issues if used incorrectly). It's the least invasive though if you can't change the stored proc.
The third option probably creates more problems elsewhere in your code base. That said, you probably shouldn't have one function do two very different things - I'd avoid the parameterized version if you end up going down this route, and go for two separate functions.
From your code and the fact that your variable is named dr, I am going to assume that you are using a DataReader.
The DataReader class exposes the column names as indexed values.
Dim columnNames = Enumerable.Range(0, dr.FieldCount).Select(dr.GetName).ToList()
Will call the method dr.GetName(index) for all columns, returning a list of column names you can check using columnNames.Contains("yourColumnName")
There is also dr.GetOrdinal, but that will throw an exception if the column doesn't exist.

Prevent null updates in detached entity framework objects

Using EF6 Framework4.5 – Creating my first n-tier app and first EF experience. I have CRUD working but one issue that I have a work-a-around but don’t like it. There must be a better way.
When data object is returned from my UI layer to the DAL layer, it has been detached, so I flag EntityState as “Modified.” But then it updates all columns in the db. Values that were not loaded in the form view (and not submitted) obviously are null and updated to such in the db.
1) My first solution does work:
Store the object in session in the UI layer and loop through the object updating edited values when the form is submitted. Thus, original values are passed back unchanged and updated to original values. I don’t think this would be best practice though.
2) The solution I think I want:
I am looking for a helper function in the DAL layer to loop through all values in the returned object and flag only non-null values as “IsModified” before calling SaveChanges.
I have found examples in C# on how to check for changed value but not null. (I am still a vb guy anyway. Don't hate.)
A) Is solution #2 a good way to do this?
B) Has anyone a piece of good to help me?
Thank you.
BTW, this is my best stab at it so far: (Errors on “CurrentValues”)
Public Overridable Function MarkEntriesModified(entity As Object)
Dim dbEntityEntry = DbContext.Entry(entity)
'Ensure only non-null values are inserted
For Each [property] In dbEntityEntry.CurrentValues.PropertyNames
If Not IsDBNull(dbEntityEntry.CurrentValues.GetValue(Of Object)([property])) Then
dbEntityEntry.[Property]([property]).IsModified = True
End If
Next
Return entity
Try this architecture. So if you are using EF then I suppose you have edmx updated and correctly representing your database objects.
eg: Say you wan to update Customer data in customer table
Create a Customer class.
when you extract a particular customer, get an instance of the
customer created.
pass this instance to the UI
and pass the instance back to Business layer to save
this way you don't loose anything in between.
some sample code

Why data binding does not remember the old values?

I have a classic ObjectDataSource and a ListView in my page. The List view just displays some data and when switched to edit template it allows the user to change the values. I want the user to edit just some values -- so I bind just these ones in the edit template.
The problem is that the other values suddenly turn to nulls or 0. I tried to bind all of the values at once and it works fine, but I cannot understand why the old/original values just disappear. Is there any way how to bind the old values?
Thanks for help.
The problem is, that only the data that is included into a round-trip to the server will be available in the postback. That includes all that that is bound to BoundFields, TemplateFields or if the Propertyname is included in the DataKey (or DataKeyNames, don't know right now).
The best approach to fix this, and to keep the overhead to a minimum is to add your primary key to the DataKeyNames collection. This allows you to have access to your custom object that contains an unique identifier and all properties that have just changes.
In your Update Method of the ODS (in your custom class) you now need to retrieve the old object by its unique identifier, manually assign the new values and saves your object back to the database

Resources