I have entity named DocumentItem. It has a natural key that consists of two columns : DocumentId (id of the document) and Index (the position of an item on a document).
This is a natural key so it has to change sometimes. Entity Framework prevents changes to the key. However I've managed to bind Inserts/Updates/Deletes to stored procedures. They get one extra parameter - NexIndex which becomes a new index for a DocumentItem when the sproc is executed.
There's however one problem: managing the object state manager so it has the current and valid information. Imagine a situation like this:
Document with document items:
1.
2.
3.
4.
5.
User deletes number 3 and adds a new position (always at the end, user cannot reorder items).
Changelist:
Delete 3 (ok)
Update 4 -> 3 (ok)
Update 5 -> 4 (ok)
Insert 5 - this is where it breaks
The problem is that Entity Framework is not aware of index changes. I've tried to bind Index back from database, but it always results in an exception "Cannot determine valid order of operations...".
Now the situation is as follows: EF believes that it already has item with index 5 so the last insert breaks.
I need to do something to clear the object state of the updated items so that I can add new or attach or download them from the db.
What can I do?
Doing this with database is pain in the ass generally. You can have your order column in the database but don't use it as a "real order" without gaps. If you delete item with order = 3 so be it. There will be a gap. When you insert new item add it with order = 6. Your items will be still ordered in correct order.
If you don't like this idea revert back to ADO.NET or don't use order as part of key.
Related
I have a folder filled with about 200 csv files, each containing about 6000 rows of data containing mutual fund data. I have to copy those comma separated data into the database via Entity Framework.
The two major objects are Mutual_Fund_Scheme_Details and Mutual_Fund_NAV_Details.
Mutual_Fund_Scheme_Details - this contains columns like Scheme_Name, Scheme_Code, Id, Last_Updated_On.
Mutual_Fund_NAV_Details - this contains Scheme_Id (foreign key), NAV, NAV_Date.
Each line in the CSV contains all of the above columns so before inserting, I have to -
Split each line.
Extract first the scheme related data and check if the scheme exists and get id. If it does not exist then insert the scheme details and get id.
Using the id obtained from step 2, check if an entry for NAV exists for the same date. If not, then insert it else skip it.
If an entry is inserted in Step 3 then the Last_Updated_On date might need to be updated for the scheme with the NAV date (depending on it is newer than existing value)
All the exists checks are done using ANY linq extension method and all the new entries are inserted into the DbContext but the SaveChanges method is called only at the end of processing of each file. I used to call it after each insert but that just takes even longer than right now.
Now since, this involves at least two exists checks, at the most two inserts and one update, the insertion of each file is taking too long close to 5-7 minutes per file. I am looking for suggestions to improve this. Any help would be useful.
Specifically, I am looking to:
Reduce the time it takes to process each file
Decrease the number of individual exists check (if I can possibly club them in some way)
Decrease individual inserts/updates (if I can possibly club them in some way)
It's going to be hard to optimize it with EF. Here is a suggestion:
Once you process the whole file (~6000) do the exists check with .Where( x => listOfIdsFromFile.Contains(x.Id)). This should work for 6000 ids and it will allow you separate inserts from updates.
I have an application that adds pets to an SQL database.
Currently the user must know what PetIDs are existing and so must know what is available to be added. If the user tries to enter an already existing ID the program gives an error.
Im thinking i need the PetID (top text box) value to be automatically decided upon page load, with a value which wont clash with an already existing value...
Can someone help? i have no idea how to do this
I need the page_load to automatically search the SQL database for PetIDs that are available, pick the one with the lowest value and have it in the text box automatically, ready for the user (so the user wont have to worry about picking one which isn't already taken)...
How can i have an ID which is available, waiting in the top text box, upon page load? also make it so the user cannot attempt to change it.
You can create the PetId as an Identity Column as below:
[PetId] [int] IDENTITY(10,1) NOT NULL
Thus you will not have to add it manually, each time you add a record of Pet, the PetId will be generated automatically and that will be unique. So, no clashing of PetId will occur.
Here 10 represents the first Id number and 1 represents how your PetIds will be incremented.
Regards,
Pratik
One option would be to use a sequence:
CREATE SEQUENCE pet_id_seq START WITH 7
I'm starting it with 7 because it is the first available pet_id
Then, on page load, get the next value for the sequence by executing the following query:
SELECT (NEXT VALUE FOR pet_id_seq) AS next_pet_id
And show the value returned by the query on the pet_id text box.
This approach has one disadvantage, it produce gaps, if you open the page, but you don't actually insert the pet row, the id is lost forever. But I think you should don't care, there are many numbers, you are not going to exhaust them.
Another option is to use the following query to fill the pet_id text box:
SELECT MAX(pet_id) + 1 AS next_pet_id FROM pets
But this one has another disadvantage. It doesn't work well on concurrent scenarios. If two users open the create pet page at the same time they get the same next_pet_id and one of them will get a primary key violation error.
It is usual to enter your data and return the ID after the record is added to the database (using the Identity Insert on the Id column).
If you want the next number to be displayed BEFORE the data is created then use an integer column for ID without the Identity Insert option and create a SQL Server sequence. Create a stored procedure to return the next sequence number to show in your creation page
Set the id column to auto increment and don't let the user insert it, let the database decide it for itself.
Or you can change the id column type and use Guid.NewGuid() to generate a new id.
I have a table Rules on my database. I insert rules like:
Rule[] rulesToInsert = // some array of rules to insert
using(var db = new MyEntities())
{
foreach(var rule in rulesToInsert)
db.Rules.Add(rule);
db.SaveChanges();
}
When I retrieve later the rules that I have just added I notice they are in a different order. What is the best way to retrieve them in the order I added them? Should I call db.SaveChanges() every time I add a new rule? Or should I add a new column called SortOrder? Why are the items not being added in the order I added them?
Edit
The id is a guid (string) because one rule can have other rules. In other words I am creating a tree structure. (The rules table has a foreign key to itself). It was crashing when I used the primary key as an integer and it autoincremented so I just used a guid instead. I guess I will add a separate column called sort order.
Tables have no sort order (new rows are not guaranteed to be added to the end or any other place). The only safe way to retrieve rows in any particular order is to have a query with Order by.
So yes you will need to add a SortOrder column. (Can just set it as an identity column.)
If you want your items to be inserted in the order you add them in the foreach statement, you have to make a big compromise, to call the db.SaveChanges in each iteration.
foreach(var rule in rulesToInsert)
{
db.Rules.Add(rule);
db.SaveChanges();
}
I say that's a big compromise, because for each rule you have to insert you have to make a round-trip to the database, instead of doing only one round-trip as in your original code.
One possible workaround, it would be to add an extra column in the corresponding table in your database, that would hold the information of order. If you do so, you could add one more property in the rule object and refactor a bit your code. Then you will have the expected result.
I started to use Xamarin.iOS, and the first app i'm trying to build is a Notes App, just like default Apple Notes app.
I'm using the recipe from Xamarin Recipes, and used UITableView to show the data, and TableSource class to manage cells and items.
The problem start when I want to save the data, so I will be able to load the notes after reopen the app. I'm using ADO.Net, and its working well. I tried to save the data to table contains to columns: ID & Text.
The issue is how to work with the DB table and at the same time with the UITableView.
For example: I loaded few items, let say that they will get the ID from Db: 1, 2, 3, same as they are in UITableView IndexPath. Now i'm deleting item number 2, so than item that was num 3, is now 2 in UITableView.
When trying to access and edit item number 2 (old 3) - Can't find him in the DB.
The reason for the problem is known, but how to fix it - I really dont know.
Any suggestion will be great.
Thanks, Tal.
you are doing little wrong lets start say you have your record id is unique may be 1 - 2 - 3
you now forget about indexPath just focus on your record id,
load the data and now you have deleted the record 2, no problem edited your data id 3 and updated into DB, but for this you have to use array of dictionary so that eavery record have there property like
id and message so you always update the Right dataset
I am using linq for entities to read and update data from a SQL server. This database is a Dynamic NAV database, and every time someone is changing a column in the database – my application need to be recompiled.
Is it possible to ignore or hide columns in the database from linq for entities, and still get update to work correctly? Let’s say there is 100 columns in a table, and that I am using on only 10, when I update a value – I want the remaining 90 values to stay in the row.
You can just tell the people that add new columns to either
Allow null for newer columns
Or add a default constraint so a good default value is added automatically added for newer rows
Either of these will allow linq to work correctly
The best way would be to create a custom view in your database. If you want to be able to insert / update / delete from that view, you can create the appropriate triggers on the view. Linq will treat the view just like any other table.