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.
Related
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.
What would be the best practice for storing some entity in a SQL Server database, when I have an Id property which is autoincremented (identity)?
This is for a .NET Core application, using Entity Framework Core. I suppose that I could just create some new entity without the identity id, and move the values of my old entity to my new entity the store it in the .Add method of my current context, or execute a command for enable the 'SET IDENTITY_INSERT ON', but both of those approaches looks messy, I'm guessing that there is a cleaner way to achieve this.
//user has autoincremented property
public IEnumerable<User> SaveUser(User user)
{
context.add(user);
context.SaveChanges(); // Exception Cannot insert explicit value for identity column in table
}
I expect to get to do it in a way that I could reuse it in the whole application, because if my entities keep increasing in size, I would have to write this messy code all around.
To start with, I would like to ask/point at your model class. Not sure how you are having your EF on .net core, but if you are to have a model (be it code first or model first),
lets just say, your Entity model looks similar to below:
User
{
UserId (as int),
UserName (as string),
BirthDate (as date)
}
You can achieve the identity insert by below approach:
public class User
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int UserId {get;set;}
public string UserName {get;set;}
public DateTime BirthDate {get;set;}
}
Please explore and learn about Code first approach, Modelling your data, repository patterns (may be the ideal in my perspective but depends on case or could be a good learning) and see to the attributes, annotations decorations for EF models.
To explain on what actually drives the auto identity by the above annotations,
Identity generation (auto identity prop)
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
is the driving factor, also depends on the data type. some data types may need additional param configurations. Explore when you try out on your solution. For example, you can use Guid data type and see how it turns out in your Db.
primary key or key attr
[Key]
is as simple to denote as the primary key field in the model/entity structure. This may not be helping or running the auto identity but, i am explaining about this on why i added this attribute in my sample code above.
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 am trying to use entity framework code first method to connect to PostgreSQL database, and when I use entity data model wizard in visual studio to generate C# class from database, it can generate classes for each table in database successfully, but the views in database cannot be generated.
(source: linearbench.com)
(source: linearbench.com)
Can someone told me where I did wrong? I use Entity framework 6.1.3, with Npgsql 2.2.5. PosgreSQL database is version 9.3.6 installed on a Ubuntu server.
Thanks
I know this question is a little bit old now, but ill chime in here for anyone else who may be looking for solutions here. My answer may not be exactly what the question was looking for, however, it has sufficed as a work around solution for me.
The problem with views is that entity framework has a hard time determining the primary key column for them. In Sql Server, you can use ISNULL() function to trick EF into thinking that the column is a key column, but the equvilant coalesce() function in postgres isn't good enough for EF. I also tried generating auto-incrementing row id column, joining to other tables with primary keys, etc; no luck with any of these.
However, something that has just about emulated the functionality that I needed as far as being able to query my views into my view objects is to just extend your context class with functions that call Database.SqlQuery and return it as a Queryable
For example:
Suppose a view in your database, "foo", with columns id, bar, baz. You can write your own POCO to hold the view data like so
public class foo
{
public int id { get; set; }
public string bar { get; set; }
public string baz { get; set; }
}
and then extend your context class with a partial class definition like this
public partial class FooContext : DbContext
{
public IQueryable<foo> foo =>
this.Database.SqlQuery<foo>( "select * from foo" ).AsQueryable();
}
And then you can query it from your context just the same as any other table
context.foo.where( x => id > 100 ).toList(); //etc,etc
You wont be able to do inserts or use any of those extra capabilities that usually come with the standard DbSet, but Views are typically used as read-only queries anyways (unless youre using some special insert triggers)...
But this gives you a base call that will query the entire view, and it doesn't hit the database because its left as a queryable, so you're free to call any other LINQ extensions on it such as Where to filter it to the results you want.
I migrated from sql server to postgres sql using npgsql lib, and this fix allowed my views to work without having to make any changes to my programs codebase, just as if nothing had changed at all, and despite the fact that the edmx would not generate my view objects due to lack of a (discernible) primary key.
Hope this helps!
I have two LINQ to SQL classes, CandyBar and DeliciousCandyBar that map to tables of the same name in SQL Server.
There is a 0..1 relationship between CandyBar and DeliciousCandyBar. i.e, A CandyBar can have 0 or 1 DeliciousCandyBars. Conversely a DeliciousCandyBar has exactly one CandyBar.
In LINQ to SQL class, they look (basically) like
class CandyBar {
public int Id { get;set;} // this is primary key w/ autoincrement identity
public string Name {get;set;}
public EntityRef<DeliciousCandyBar> DeliciousCandyBar {get;set;}
}
class DeliciousCandyBar {
public int DeliciousnessFactor {get;set;}
public int CandyBarId {get;set;} // FK to candyBar Id
public EntityRef<CandyBar> CandyBar {get;set;} // the association property of the FK
}
To feed the database (via l2sql), my crawler goes out and finds candy bars and delicious candy bar.
But, with the first delicious candy bar my crawler inserts into the CandyStoreDataContext, the DataContext throws an exception when SubmitChanges is called.
The crawler runs the following code for one delicious candy bar. Please note this is an example. The exact process is more complex and I use a custom DSL crawler that spits out this object structure. Essentially, the following is performed.
var dc = CandyStoreDataContext();
var bar = new CandyBar() {
Name = "Flake",
DeliciousCandyBar = new DeliciousCandyBar() {
DeliciousnessFactory = 12
}
};
dc.CandyBars.InsertOnSubmit(bar);
dc.SubmitChanges();
On SubmitChanges(), a SqlException is thrown with the message "The INSERT statement conflicted with the FOREIGN KEY constraint FK_CandyBar_DeliciousCandyBar. The conflict occured in database CandyStoreData, table 'dbo.DeliciousCandyBar', column 'CandyBarId'".
The problem became clear when I dumped the CandyStoreDataContext.Log to Console.Out, the generated insert statements were round the wrong way. LINQ to SQL was trying to insert the DeliciousCandyBar first (which tried to set an invalid value in CandyBarId column), rather than inserting CandyBar first.
My question is, how do I get Linq to SQL to swap with the order of the insert statements?
I had assumed (incorrectly) that LINQ to SQL would know the direction of relationship dependency and do it the other way around.
UPDATE:
This post suggests I have the Association the wrong way around. But that doesn't make sense to me. It makes sense when modelled in the database. How could this be done the other way around.
On CandyBar, the association attribute on DelciousCandyBar property is
[Association(Name="DeliciousCandyBar_CandyBar", Storage="_DeliciousCandyBar", ThisKey="Id", OtherKey="CandyBarId", IsForeignKey=true)]
On DeliciousCandyBar, the association attribute on CandyBar property is
[Association(Name="DeliciousCandyBar_CandyBar", Storage="_CandyBar", ThisKey="CandyBarId", OtherKey="Id", IsUnique=true, IsForeignKey=false)]
Okay, now I'm confused, why is the second attribute marked as the foreign key.
I'm going to try recreating that relationship between CandyBar and DeliciousCandyBar in SQL Management studio
UPDATE 2
Okay, I tried creating the relationship both ways. And SSMS makes it really clear where the primary key is located (CandyBar.Id). I had it right the first time. Otherwise, the cascading would go backwards.
I would consider the possibility of it being a linq2sql bug with self referencing tables. Its a wild guess, but I recall linq2sql documentation saying somewhere it didn't support it that well. Perhaps there its the linq2sql designer that gets confused.
From the generated properties you posted, you can tell it has it backwards i.e. CandyBar has is pointing to delicious, instead of the other way around. Look at the generated code for other relations that work correctly.
Once you confirm it isn't working, and that by re-adding them in the designer doesn't sets them correctly, open the properties of a working association in the designer and make sure to have the association between candybar and deliciouscandy configured in the same way.
Please try this alternative:
Insert the instance of CandyBar without any child DeliciousCandyBar objects, then call the SubmitChanges() method.
Assuming you have an ID or GUID linking the two objects - lets say, CandyBarID. Then set the value of CandyBarID on the DeliciousCandyBar instance to that of the instance of CandyBar.
You could then insert the DeliciousCandyBar object, and because CandyBarID has been set, the relationship should be correct on selecting CandyBar objects.