EF 6 SaveChanges() fails with Cannot insert the value NULL into column - c#

I have a table that is set to a varchar that cannot be null. Within my code, I am pulling the data from the database, updated the object and then completing a SaveChanges() on the connection.
This gives me the following error:
Cannot insert the value NULL into column 'FileReference', table
'dbo.Files'; column does not allow nulls. INSERT fails. The
statement has been terminated.
FileReference is populated, as I have checked, but within the exception I get:
Exception.Entries.source[0].Entity.FileReference is showing as null.
Confused on this one.
Code as per request
using (var conn = new MagmaContext())
{
var result = from p in conn.Files
where p.fileId == 1
select p;
var rec = result.FirstOrDefault();
// code changed to use correct var
rec.UpdateFileData(model);
conn.SaveChanges();
}
I have an extension class called ObjectTransform.
public static class ObjectTransform
{
public static File UpdateFileData(this File file, valueObject model)
{
file.FileReference = model.File.FileReference;
file.LastUpdate = model.File.LastUpdate;
}
}
And the viewmodel looks like this
public class valueObject
{
public int Id {get;set;}
public string Description {get;set;}
public File File {get;set;}
}
public class File
{
public int Id {get;set;}
public string FileReference {get;set;}
}
Fixed Code as per comment

Thanks to all for all responses, but I came up with the solution. The reason was way simple in the end. All it was, the File object not initialised correctly. Hmm sorry, but worth a meantion anyways for other budding coders.

Related

SQLiteConnection Query<Int> returns 0 but Query<Foo> returns correctly

First of all, I'm using Xamarin/C# with sqlite-net-pcl.
I create a table using SQLite.SQLiteConnection.CreateTable. T is one of my classes I can pass in, Here's an example:
public class Foo : IDatabaseItem
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string Subject { get; set; }
public string Description { get; set; }
}
}
After inserting a record, I want to return the last key inserted. SQLiteConnection.Insert unfortunately returns the amount of rows inserted instead of the row inserted.
public int GetLastInsertedId()
{
return conn.Query<int>($"SELECT Max(Id) FROM {typeof(T).Name}").First();
}
The problem is the function above, it always returns 0. I've tried removing the .First to analyse the entire object it returns and it'll return an IEnumerable which correctly has the amount of rows in the DB within, but all of the results are 0.
Interestingly if I modify my function to return the original class it was created from and remove the "Max(Id", it works.
public Foo GetLastInsertedId()
{
return conn.Query<Foo>($"SELECT * FROM {typeof(T).Name}").First();
}
I believe I'm not referencing the column name correctly, I'd love to view the table to see what I'm working with but unfortunately the version of android I'm using doesn't appear to let you view an SQLite database (from Visual Studio).
I get that I can use Linq queries to quickly get at the data I want, but it feels like returning an entire object from the database is a bit bulky when all I want is the Id, especially as the database grows.
Any help would be appreciated, I'm sure I'm doing something daft...just can't suss out what.

Read object from c# database into classes directly

What I am trying to do is read a database, row by row, and use the data from each row to initialize an object of the type that data represents. In this case I am reading rows of the Device table and trying to create Device objects with that data. I saw this SO link:
and I tried this snippet:
using(var dc = new DataContext(connectionString))
{
List<Person> people = dc.ExecuteQuery(#"
SELECT Id, Name Address
FROM [People]
WHERE [Name] = {0}", name).ToList(); // some LINQ too
}
But it is telling me
The type arguments for this usage cannot be inferred from the usage
Is this in principal correct or should I be using the BondIO serializer/deserializer? as mentioned here
Also the order of the members in the object may not be the same as the order of the columns in the database, is this relevant?
Later that same day....
I now have a DBContext with all my database objects defined like this:
public class MyContext : DBContext
{
public dbSet<Device>{ get; set;}
etc...
}
And I now try to get object using this snippet:
using (var db = new MyContext(ConnectionString))
{
var res = db.Device.Find(ID);
}
However this gives an exception message
Could not load type 'System.Data.Entity.ModelConfiguration.Conventions.AttributeToColumnAnnotationConvention`2
I have checked the database and it should return 1 value based on the PrimaryKey ID that I am passing. Anybody have any hints what I'm still doing wrong.
You cannot, because ExecuteQuery is for executing statements, not for querying database. You should use SqlQuery instead
What you can do is, to create a new class with the properties you want to set in your query, means a simplified version of your query. In your case
public class Device
{
public int Id {get;set}
public string Name {get;set}
public string Address {get;set}
}
then use it as
var people = dc.ExecuteQuery<Device>(#"
SELECT Id, Name Address
FROM [People]
WHERE [Name] = {0}", name).ToList();

Entity Framework and duplicates

I use Entity Framework as ORM in my project. Let's suppose I use Code-First pattern and I have two models. Such as
internal class First
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
public string Name { get; set; }
}
internal class Second
{
public int Id { get; set; }
public First ForeignKeyEntity { get; set; }
// other members
}
And here is code populating database:
List<Second> res = GetData();
using (var ctx = new StatisticContext())
{
foreach (var item in res)
{
ctx.Seconds.Add(item);
}
ctx.SaveChanges();
}
As you can see each instance of class Second has instance of class First in its member ForeignKeyEntity. Obviously some instances of First can be duplicated in res. And when I run this code I get DbUpdateException in ctx.SaveChanges() with inner exception that has the following message:
Violation of PRIMARY KEY constraint 'PK_dbo.First'.
Cannot insert duplicate key in object 'dbo.First'.
The duplicate key value is (29459). The statement has been terminated.
I can not to insert duplicated rows but I don't want to insert duplicates, I would like to insert row only if it doesn't exist. How to solve this problem? How to insert foreign key object only if doesn'tt exist?
The normal way of doing things would be to do a read first with item to see if it exists. If it does then you need to use ctx.Seconds.Update(item);
If your items are already on the context, then you can check the state. it will be either State.Modified or State.Added.
Whats in GetData()

Need to assign object id to field when creating object

I've got an object that has a property which, on creation, should always be populated with the object's id(primary key).
How do I do that?
I've tried assigning the id to the field just after the db savechanges(), before returning to the view, but that creates two records on my db.
Also tried loading a new instance of the same, assign the id to the field and save it, but also creates two records.
Thanks for any help.
//model
public partial class supplier
{
public int id { get; set; }
public int ref { get; set; }
}
//controller
public ActionResult Create(supplier sup)
{
if (ModelState.IsValid)
{
db.suppliers.add(sup)
db.SaveChanges();
sup.ref = sup.id;
db.SaveChanges();
}
...
}
Hi all, thanks for all your help... I've found a solution... don't really know if it's the correct one, but it does the job.
For whoever is interested here it is:
supplier needToUpdateSupplier = (from s in db.suppliers where s.id.Equals(supplier.id) select s).ToList().FirstOrDefault();
if (needToUpdateSupplier != null)
{
needToUpdateSupplier.ref = supplier.id;
db.suppliers.Attach(needToUpdateSupplier);
db.Entry(needToUpdateSupplier).Property(e => e.ref).IsModified = true;
db.SaveChanges();
}
Create the object
Save it (SaveChanges). This will give you the id.
Assign the id to your property, using the existing object (you don't need to load it again)
Save again
Notes: The property must be nullable, because up on creation it will be empty.
// A Test object
class MyObject
{
public int ID {set;get;}
public int? MyProperty {set;get;}
}
void Foo()
{
var test = new MyObject();
db.MyObjects.Add(test);
db.SaveChanges();
// at this moment test has the Id set. You can assign it.
test.MyProperty = test.Id;
db.SaveChanges();
}
With EF it should set the ID for you:
db.Posts.Add(post);
db.SaveChanges();
// post.ID should be now set

Linq Select * from Table ExecuteQuery

First let me start by saying that I don't have a complete understanding of Linq. I am trying to dynamically query a database, The first query uses LINQ-SQL which works fine, but the second dynamic call is what fails in run time
public void getTables()
{
foreach (var c in dc.GetTable<TableListing>())
{
List<TableData> res = tableBrowse(c.TableName);
}
}
public List<TableData> tableBrowse(string tablename)
{
string sql = "Select * from " + tablename;
var results = dc.ExecuteQuery<TableData>(sql);
return results.ToList();
}
public class TableData
{
public int Time { get; set; }
public string Value { get; set; }
}
I query the "master table" and this retrieves a list of tables to query. They all have the same structure, as defined in the class TableData. I get a runtime error about Specified cast is not valid. I'm not really looking for code as much as I am looking for what I am doing wrong and how to fix it. Thanks.
You might try decorating your class properties with ColumnAttributes, specifying the column name and type so that LINQ to SQL knows how to do the version of the column data to the properties. You may also need to set other attribute properties to make it work correctly. I would also specify the column names in the SQL instead of using *. Put the column names in the same order as your properties appear in the class as I believe that it processes the result values in the same order as the properties are defined. Not sure it this will work or not, but essentially you're recreating what the designer would do for you.
public List<TableData> tableBrowse(string tablename)
{
string sql = "Select [time], [value] from " + tablename;
var results = dc.ExecuteQuery<TableData>(sql);
return results.ToList();
}
public class TableData
{
[Column( Name="Time", DbType="DateTime NOT NULL", ... )]
public int Time { get; set; }
[Column( Name="Value", DbType="VARCHAR(255) NOT NULL", ... )]
public string Value { get; set; }
}
You aren't explicitly converting the return value from dc.ExecuteQuery<TableData>(sql) to the TableData type that you've defined. I expect that the ExecuteQuery is complaining because it doesn't know what the TableData type is.
The ExecuteQuery helper needs to return a DBML (LINQ-to-SQL generated) type as defined in your database.
But I would suggest that you don't go down this route. If you want to get records from a table, say Customers, just use content.Customers - the point of LINQ-to-SQL is that it already contains all these accessors to save you time.
Actually I found out what the problem was, I was missing a table definition. There was a third data type in one of the tables. Once I defined that table class and checked for the data type it worked fine. Sadly the compiler just didn't give that much information on just what was wrong.

Categories

Resources