I have been using EF for awhile but I am not sure what is missing as I am following a pattern I have used several times in the past. This is the SQL table definition:
Table LogTable
Columns
LogID (int, Identity)
fk_ref (int, not null)
action (nvarchar(60))
notes (nvarchar(200))
This is the code (names changed for ease of reading/understanding)
using(myEntity _me = new myEntity)
{
LogTable _lt = new LogTable();
_lt.fk_ref = 10;
_lt.action = "Some action";
_lt.notes = "even more text";
_me.LogTable.Add(_lt);
_me.SaveChanges();
}
This is where it blows up claiming that the field "fk_ref" is null.
When I go to the edmx and ModelBrowser all the fields are represented.
When I check the select SQL on the table name "_me.LogTable" during debug the SELECT statement is missing the field it claims as NULL.
I hope I have given enough information to turn on the light bulb in my head.
NOTE: I have tried dropping and re-adding the table. Gone as far as drop, clean, rebuild, re-add and no change.
Would really appreciate any help.
UPDATE: Since this is new functionality I took the liberty of breaking the foreign key enforcement on the reference table and ran the code as demonstrated above. I also removed the Not Null limitation. It wrote out the record but put a NULL in the fk_ref field.
UPDATE 2 As someone asked for it. This is the CS modified to match the shortened definition above.
public LogTable()
{
this.fk_ref = 0;
}
public int LogID { get; set; }
public Nullable<int> fk_ref { get; set; }
public string action { get; set; }
public string notes { get; set; }
prior to the changes I mentioned in the first update it was
public LogTable()
{
}
public int LogID { get; set; }
public fk_ref { get; set; }
public string action { get; set; }
public string notes { get; set; }
UPDATE 3 moving ahead with this I saved a record via the code above and while debugging checked the DB for the value inserted in the fk_ref field and it was null. So, i fetch the record back to the app via the LogID, manually set the field value to a random number and called SaveChanges again. Still null. Here is the code following the SaveChanges() above
//... prior code ...
// assume that 4 is the log id of the record just inserted
// and 1000 is the fk_ref intended to be inserted
LogTable _new = _me.LogTable.where(p=>p.LogID == 4).FirstOrDefault();
// when I inspect _new the fk_ref post save changes the value is 1000
_new.fk_ref = 999;
_me.SaveChanges();
Retrieving the record from the db again fk_ref is still null
Found the Answer
I have no idea how to categorize this but the answer was in a scope not included in the original question. My thanks to all who responded as you pushed me to look under different rocks - sometimes that is all you need.
additional scope for question
This project is part of an enterprise wide management tool delivered via a One-click interface. Each of the 20 or so different business surfaces has their own management project which may or may not be written within the IT development group. (Democritization and all that). The subordinate projects are user control DLLs that are then distributed along with the main shell. The entire solution gets data from several servers and more than a hundred DBs. The connection strings are managed through main shell. What I am currently working on is a project that has a number shared components and controls including an enhanced logger. (log4j and all related services are outlawed here). I was developing a new control for shared TimeKeeping that uses the shared controls project. More graphically it looks like this:
Timekeeping had a reference to the Shared Controls project which had a EF object that connected to the DB that had the Log Table.
Project 1 had an EF object that connected to the DB that had the Log Table
The Shared Controls EF object was up to date. (the fk_ref field was added some time back)
Project 1 has been around a while (longer than the shared controls) and it's EF object was out of date.
Even though Timekeeping did not have a reference to Project 1 when the EF object in the Shared Controls was writing it used the definition Project 1.
Oddly enough, on a read the Shared controls retrieved all the fields
How I "proved" it
Created a mock up of the original application (WPF)
Added a user control project with EF connecting to a DB
used the control to write data to the DB
Closed out of VS
Used SSMS to modify the table
Opened VS added a new project with an EF project that connects to the same table
Added a third project that used a class in the second to write to the table referenced by the first and second.
any attempt to insert from the third project wrote a NULL into the added field
updated Model form Database in the first control project - checked to make sure the new field was there
Attempted to insert from the third project and all fields were inserted.
This seems odd to me but heck with all VS and MS have done for me who can complain. Off to make up for lost time. Believe it or not, I am more than a little happy I figured it out with all your help. Maybe this experience will help someone else.
Related
Here is the super simple class I'm trying to create.
public class Company
{
public int ID { get; set; }
[Column(TypeName = "VARCHAR(254)")]
[Index]
public string Name { get; set; }
[Index]
public int stupidField { get; set; }
}
My goal was to force Name to be unique, so I added the decoration [Index(IsUnique = true)]. But no unique index was created, so I figured I'll first try to solve the simpler problem of creating any index. Because I read here that indices cannot be created for columns of type varchar(max), I limited the length of the Name field. Still no luck. I even tried a few different syntaxes for limiting the length of the field, but still no index.
To see if something other than string length was at play, I created the integer field stupidField, but I can't index that field either. So now I'm completely out of ideas as to what could be wrong. Please help me!
Check out this screenshot from MS SQL Server Management Studio that shows that my fields are being created but not the indices.
Note: I'm certain migrations are not the issue.
Some of the people I've read about on SO were updating their classes, but those changes were not reflected in the database because of problems with their migrations. That is not relevant here. I delete the database and recreate it every time I make a change. (I even make silly changes like renaming my fields, just to make sure that I can still affect the database.)
Turns out I'm actually using Entity Framework Core, not Entity Framework. In Entity Framework Core, indices cannot be created using attributes, although they can be created using fluent API. See Microsoft's documentation.
I have a scenario in which I need some help.
Let us assume that there is a User who listens to some type of Music.
class User
{
public virtual List<UserMusicType> Music { get; set; }
}
public class UserMusicType
{
public int ID { get; set; }
public MusicType name { get; set; }
}
public class MusicType
{
public int ID { get; set; }
public string Name { get; set; }
}
There is a form where I am asking users to check/select all types of Music he listens to. He selects 3 types namely { Pop, Rock, and Electronic }
CASE 1:
Now I want to update the User Entity and insert these 3 new types. From my understanding, I need to first remove whatever MusicTypes for this users were saved in the Database then insert these new types again. Is it a correct approach? Removing all previous and Inserting new ones? Or any other way to do it?
CASE 2:
I am taking MusicType names as string of course. Now while updating the User Entity, I'll have to first fetch the MusicType.ID after that I'll be able to do this:
user.Music.Add(new UserMusicType() { ID = SOME_ID });
Is there a better approach for this case?
I'll be glad to have some replies from experienced people in EF. I want to learn if there is an efficient way of doing it. Or even if my approach/Models are totally wrong or could be improved.
First of all, you don't need the UserMusicType class, you can just declare the `User class as
class User
{
public virtual List<MusicType> Music { get; set; }
}
And entity framework will create a many to many relationship table in the database
As for the first question, it depends. If you use this relationship any where else, like payment or audit trail, then the best way would be to compare the posted values to the saved values, ex:
User selected Music 1, Music 2, Music 3 for the first time and saved, in this case the 3 records will be inserted.
User edited his selection and chose Music 1,Music 3,Music 4, in this case you will get the values submitted which is 1,3,4 and retrieve the values stored in the database which is 1,2,3
Then you will get the new values which are the items that exist in the new values but not in the old, in this case it will be 4
You will get the removed values, which exist in the old but not in the new, in this case it will be Music 2.
The rest can be ignored.
So, your query, will be add Music 4, remove Music 2.
If you don't depend on the relationship, then it is easier to just remove all user music and add the collection again.
As for the second part of your question, I assume you will display some chechboxes for the user, you should make the value for the checkbox control as the MusicType ID, and this is what will be posted to the backend and you can use it to link it to the user.
ex:
user.Music.Add(new MusicType{ID=[selected ID ]}
You should not depend on the music name
First question:
Actually, it is a personal preference. Because, wouldn't want to delete all rows which belongs to that user and then insert them. I would compare the collection which is posted from the form with the rows which is stored in the database. Then, delete those entities from the database which are not exist in the collection anymore. And, insert new ones. Even, you can update those entities which has modified some additional details.
By the way, you can easily achieve this with the newly released EntityGraphOperations for Entity Framework Code First. I am the author of this product. And I have published it in the github, code-project and nuget. With the help of InsertOrUpdateGraph method, it will automatically set your entities as Added or Modified. And with the help of DeleteMissingEntities method, you can delete those entities which exists in the database, but not in the current collection.
// This will set the state of the main entity and all of it's navigational
// properties as `Added` or `Modified`.
context.InsertOrUpdateGraph(user)
.After(entity =>
{
// And this will delete missing UserMusicType objects.
entity.HasCollection(p => p.Music)
.DeleteMissingEntities();
});
You can read my article on Code-project with a step-by-step demonstration and a sample project is ready for downloading.
Second question:
I don't know on which platform you are developing your application. But, generally I am storing such libraries as MusicType in a cache. And use DropDownList element for rendering all types. When user posts the form, I am getting values rather than names of the selected types. So, no additional work is required.
Using Entity Framework and ASP.Net, one of my entity models is:
public class Records {
public bool IsPublic {get; set;}
// Some other properties
}
I have changed the model to:
public class Records {
[obsolete]
public bool IsPublic {get; set;}
public RecordAccess AccessLevel {get; set;}
}
Where
public enum RecordAccess {Public, Private, Group };
After this migration, I need to initialize the new column (AccessLevel) based on IsPublic column and some other tables.
So I wrote the c# code to initialize the AccessLevel and put it in the Seed method.
Note
I want to delete IsPublic column since it's no longer being used.
I have 4 different databases which I'm working on: localdb, test server, staging server and production server. These databases might not be in the same state. So data and the table schemas might be different.
Question
Is Seed method the right place to put the initialization?
(If I delete the IsPublic from the Record class the code won't be compiled)
How can I roll back the migration and data to what it was before?
I would write the initialization in the migration itself using the Sql("[INSERT SQL HERE]") method. This is assuming that this is a one time move.
If you put it in the Seed method, the code will run every time the database is updated with new migrations, which means that you will probably end up writing code that checks to see if this has already been done and ignoring it, making it one time use, which basically makes it a migration.
To rollback your database, in the Package Manager Console window, use the Update-Database -TargetMigration [name of last good migration]. More Info here.
I'm new to MVC so please bear with me.
As a learning project I've decided to make a Fantasy Football website. The NFL has a really cool api that lets you download TONS of stats and other info. As part of the data stream, each player has an id number (actually they have like 3 ids but I'll just use the main one).
Here is the problem: I want to set up my models to use the playerId value (passed in from an AJAX call on the NFL api) as a PK on my Player class and FK on several other classes/tables. However, you normally can't assign the PK Identity value on a db. I did a lot of searching for a workaround and tried several tricks but none worked. I tried playing with the db manually and ended up ruining the project and having to start over. I've thought of making the playerId a surrogate key but that seems needlessly complicated. Theoretically I would periodically call the api for updates and those are all tied to that playerId value so making that playerId the PK is what makes the most sense.
Is there a way to do this?
Turn off autoincrementing on your primary key column in your database. If your using SQL Server then don't set identity column. Set up your model like so:
public class Table_1
{
[Key]
[DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None)]
public int playerId {get; set;}
public string player_name { get; set; }
}
Then set playerId when you create the record.
You are going to have to turn off the Identity specification on the SQL Server side:
I finally got this to work.
First, add the annotation to the PK (playerId in my case.
[DatabaseGenerated(DatabaseGeneratedOption.None)]
Second, when you do a migration you have to proof read it. I ran into quite a few errors because the migration code was just plain wrong. In one case I changed the name of a PK column and the code tried to add the new PK column before dropping the old one. All I had to do to correct this was to rearrange the steps. Make sure to strip off the "identity: true" from the column which is to be the PK.
*Note: My issues with the migration tool may be in part due to my using a version of VS2012 which I haven't updated in a while. Some of the kinks may have been worked out since then.
I'm building an app to display the historical log of changes from a source control repository. The app is implemented in .NET 4 WPF and Entity Framework Code First.
One of the problems I'm getting is that over time, as more log entries are added to the log, the application uses more and more memory and doesn't release references to the log entries. Each log entry holds a list of "changed files" and for each changed file, the before and after version of the file.
The UI displays a list of log entries and the diff between the old and new version of the currently selected LogEntry and ChangedFile. The data model is roughly as follows:
public class LogSubscription
{
public List<LogEntry> Log { get; set; }
}
public class LogEntry
{
public List<ChangedFile> ChangedFiles { get; set; }
}
public class ChangedFile
{
public string OldVersion { get; set; }
public string NewVersion { get; set; }
}
As I'm using EF Code First, the database is queried and the object model is built automatically by simply accessing the List properties. What I'd like to do is somehow de-reference the ChangedFiles list after a certain time and have the database re-queried and the object model rebuilt as necessary (i.e. the user has clicked back onto the log entry).
Is there any way to do this with EF Code First? Or should I be taking a different approach to control the memory used?
The app and full source code is hosted on GitHub here: https://github.com/tomhunter-gh/SourceLog
As said in the comment: this is likely to happen with a static context that is never disposed. I see in the source that there is a ThreadStaticContextBackground which is used by a LogEntry: in an active-record like pattern a LogEntry saves itself by its MarkAsReadAndSave method, for which it needs a context.
You probably did this (I did not really scrutinize the source) to prevent creating and disposing many contexts while saving logs entries. But I think you should rethink the active record approach. Log entries are saved by the MainWindowViewModel. The view model should invoke a service method that saves log entries by a short-lived context and possibly caches the entries to have them available for display etc. And it should receive the RemovedItems collection from DgLogSelectionChanged in order to have them handled by one context instead of one context per item. Does that make any sense?