EF6 Code First throwing NullReferenceException when no PRIMARY KEY exists - ef-code-first

This is a bit of a strange one. I do not have administrative access on the database and am working with a table that is properly constrained, but has no primary key defined.
The schema is as follows:
CREATE TABLE Foo
(
KeyColumn VARCHAR(100) NOT NULL UNIQUE,
BitValue BIT NOT NULL
)
Within EF, there's a model:
[Table("Foo")]
public partial class FooModel
{
[Key] //Also tried by setting this in the ModelBinder as well with no luck
public string KeyColumn {get;set;}
public bool BitValue {get;set;}
}
//Within DbContext subclass:
public DbSet<FooModel> {get;set;}
//When in code:
context.FooModel throws NullReferenceException.
I believe this is because EF cannot map to the underlying table in spite of the identical schema. KeyColumn is a valid key, but is not defined as explicitly as the PRIMARY KEY.
Is there a way to force EF CodeFirst to map to this underlying table?

I was able to solve this through using the leaky abstraction provided by EF6.
var m = context.FooModel.SqlQuery(#"
SELECT KeyColumn
, BitValue
FROM Foo
WHERE KeyColumn = #KeyColumn
", new SqlParameter("#KeyColumn", keyColumnValue)).FirstOrDefault();
Other operations, such as adding and saving models seemed to work after this.

Related

Identity MySQL EF Pomelo Core 2.2 - Two user tables to one UserId FK relationship not displaying user results

I have three MySQL tables that all appear to have been generated correctly with .Net Core 2.2. code first with Pomelo/EF framework:
AspnetUsers with PK=UserId (default Identity Users table with Id
changed to UserId)
UserSetting with PK=UserId
I originally had these as one User table on my last project and wanted to leave Identity alone as much as possible and so I created a second table to hold other user settings.
The problem exists on my third table called Incident with this FK relationship currently:
public class Incident
{
[Key]
public int IncidentId { get; set; }
...
public string UserId { get; set; }
...
[ForeignKey("UserId")]
public virtual IdentityUser User { get; set; }
[ForeignKey("UserId")]
public virtual UserSetting UserSetting { get; set; }
}
So, ideally I'll be able to pull values from both the AspnetUsers table and the UserSetting table to form one complete user. However, when trying to do this:
IQueryable<Incident> incident = _context.Incident.OrderByDescending(d => d.IncidentDate);
And then get at either:
incident.User (e.g. incident.User.Username)
incident.UserSetting (e.g. incident.UserSetting.IPAddress)
I get a NullReferenceException. I think EF is confused or maybe I am... but when I remove the annotations and let EF try to sort it out, I get this error message when running add-migration:
Both relationships between 'Incident.User' and 'IdentityUser' and
between 'Incident.UserSetting' and 'UserSetting' could use {'UserId'}
as the foreign key. To resolve this configure the foreign key
properties explicitly on at least one of the relationships
I tried several things and none the wiser. I've definitely checked that the UserId value matches in all three tables (so it's not null because it's actually null). When I look in the debugger output I don't see any joins being done on the select statement.
I thought about creating a FK relationship between UserSetting and the AspnetUsers table (e.g. incident.User.Usersetting.IPAddress), but again, trying to get away from the tweaking of Identity which appears to be mostly invisible without scaffolding it.
How do I link both my user tables to my Incident table using one UserId property in that Incident table? Is this maybe a bad idea (i.e. should I just go back to using a single user table)? Thanks.
Rookie mistake... I was converting a four year old project from .NET Framework to .NET Core. .NET Framework doesn't require the .Include() statements. So, I simply changed:
IQueryable<Incident> incident = _context.Incident.OrderByDescending(d => d.IncidentDate);
To:
var incident = _context.Incident
.Include(u => u.User)
.Include(u => u.UserSetting)
.OrderByDescending(d => d.IncidentDate);

How to map my stored procedure returned values into my asp.net mvc

I have created the following stored procedure, which accept two parameters and search two different databases :-
Use ITSys
Go
CREATE PROCEDURE [dbo].[AdvanceSearch]
-- Add the parameters for the stored procedure here
#Name varchar(50) = null,
#Tag varchar(50) = null
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
select t.Tag , r.RESOURCENAME , rs.DESCRIPTION , tt.Name
from [ITSys].[dbo].[Technology] t
inner join
[IT360servicedesk].[dbo].[Resources] r
on
t.IT360ID = r.RESOURCEID
inner join
[IT360servicedesk].[dbo].[Resourcestate] rs
on rs.RESOURCESTATEID = r.RESOURCESTATEID
inner join
[ITSys].[dbo].[TechnologyTypes] tt
on tt.AssetTypeID = t.TypeID
where t.Tag = #Tag and r.RESOURCENAME = #Name
END
Then i created the following view model class, to map the stored produce values:-
public class AdvanceSearch
{
public string Tag { set; get; }
public string Name { set; get; }
public string type { set; get; }
}
and currently inside my repository class , i have the following :-
public IQueryable<AdvanceSearch> AdvanceSearch(NetworkAdvanceSearch na, NameAdvanceSearch nas)
{
var t = ITSys.AdvanceSearch(nas.name, nas.tag);
//map should be done here...
but i have the following questions:-
how i can map the values returned from my stored procedure into my view model class?
as this is the first time i work with Stored procedures inside entity framework , so is the way i am calling the stored procedure and mapping its values the right way to do things ? or there is a better way ?
Thanks
suggestion:
do not use view models to directly get the data from the data store.
use domain classes or data transfer objects to get it. so in your case,
a. you need to hand-define the classes, if you use ADO.NET to run the SPROC and get the results.
b. you could leverage LINQ2SQL or EntityFramework, which help you with auto-generated classes to map SPROC results. (with EF, you might have to do some manual editing)
once you have the data classes populated by your repository, have the controller transform them into view models and render the view.
e.g. code
public class MyController : Controller
{
public virtual ActionResult Search() // take search parameters etc.
{
// this could be DTO or Domain classes or EF objects etc.
IEnumerable<IDataTransferObject> results = _repository.AdvanceSearch();
// you can do the transformation using extension methods, or linq to objects
// or helper methods, or tools like auto mapper etc.
IEnumerable<AdvanceSearch> model = results.ToAdvanceSearchViewModel();
// return the view with the model data it needs.
// this View Model is different from repository objects to keep the concerns
// separate.
return View(model);
}
}

Entity Framework 5: Using DatabaseGeneratedOption.Computed option

I have an EF5 code first project that uses the [DatabaseGenerated(DatabaseGeneratedOption.Computed)] attribute.
This option is overriding my settings.
Consider this SQL table:
CREATE TABLE Vehicle (
VehicleId int identity(1,1) not null,
Name varchar(100) not null default ('Not Set')
)
I am using the SQL default construct to set the [Name] is case it is not set.
In code behind, I have a class defined similar to:
public class Vehicle {
...
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public string ShoulderYN { get; set; }
}
When I update the entity in code, the value set in the default overrides my new setting.
In code, I have (pseudo):
vehicle.Name = 'Update Name of Vehicle';
_dbContext.Update(vehicle);
_dbContext.SaveChanges();
The expected result is Vehicle.Name = 'Update Name of Vehicle'.
The actual result is Vehicle.Name = 'Not Set'.
Is there a way in EF5 to say:
if Vehicle.Name is null/empty, use the value defined in the database? Otherwise, if I set the value in code, I want to use this value.
Apparently, no there isn't. It's not that smart :)
As you may already read, Computed option just tells the EF not to update your column, because you will compute a value on the DB-side yourself. EF will then just return newly computed value from your database (which in your case is "Not Set").
Your basic three options are - as per EF Source code documentation:
None - The database does not generate values.
Identity - The database generates a value when a row is inserted.
Computed - The database generates a value when a row is inserted or updated.
https://github.com/aspnet/EntityFramework6/blob/527ae18fe23f7649712e9461de0c90ed67c3dca9/src/EntityFramework/DataAnnotations/Schema/DatabaseGeneratedOption.cs
Since you expect a little more custom logic to be done, I'm afraid you would have to do it yourself. I would suggest you stop relying on database default constraint and do everything in code first approach. This way you would have a code like that:
public class Vehicle
{
public Vehicle()
{
this.Name = "Not set";
}
// Without 'Generated' attribute
public string Name { get; set; }
}
This way, when your Entity is created, it automatically starts with expected default value. And can be later changed by simply modifying the Name property.
Hope it helps!
Actually there is a simple solution for this:
You need to leave default constraint with value in table creation script as it is now:
CREATE TABLE Vehicle (
VehicleId int identity(1,1) not null,
Name varchar(100) not null default ('Not Set')
)
Just remove DatabaseGenerated attribute from property in class definition:
public class Vehicle {
...
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public string ShoulderYN { get; set; }
}
And that's it: now database will use default value only if you do not specify some value in code. Hope this helps.
I checked this for hours to get good answer but no:
EF cannot update models by automatic generated-ID.
You have 3 options:
Adding another VehicleId to Vehicle model.
Change automatic generated-ID to be manual generated by you.
Setting unique identifier to be something else then the generated-ID in your
model.
In your Vehicle Class it can be the Name property.
I suggest you option 3:
Setting up unique-id to be Vehicle.Name (and you can add more properties).
Then: if vehicle by unique-id doesn't exists, add new vehicle to db-context:
//if there is no such a Vehicle in system, add it:
if (vehicle.Name !=null && vehicle.Name != String.Empty && _dbContext.Where(v => v.Name == vehicle.Name).FirstOrDefault() == null)
_dbContext.Add(vehicle);
_dbContext.SaveChanges();

ServiceStack ORMLite - Select columns

I recently started working with ServiceStack and its ORMLite framework. I have searched on Google and browsed the source code but couldn't find anything relevent.
Is there any way to select specific columns when executing a query with ORMLite ?
Something like that : Db.First<Model>(q => q.Id == someId, "Column1, Column2")
Unless I missed this feature, I am surprised nobody asked about this before, since this is one the rule of thumbs to optimize your DB transactions.
If you want to specify columns other that the table you need to use SQL as seen in this earlier example
So in your case you could do something like:
Db.First<Model>("SELECT Column1, Column2 FROM AnyTableOrView");
You can also create a partial model that looks at your table by decorating it with the [Alias] attribute, like:
[Alias("AnyTableOrView")]
public class Model {
public int Id { get; set; }
public string Column1 { get; set; }
public string Column2 { get; set; }
}
Then you can do something like:
Db.First<Model>(q => q.Id == someId);
And it will only SELECT + populate fields from the partial model.
I did try this :
Created a Database VIEW (table name and columns are already set)
Created a class named "Event" and matching each fields for that table with a property
(i used [Alias] for table name and for all columns to have nice names)
Wrote access to DB to select 1 record based on it's ID
var dbFactory = new OrmLiteConnectionFactory(
"Data Source=MyDB;User Id=user;Password=pwd", // Connection String
OracleDialect.Provider);
using (var db = dbFactory.OpenDbConnection())
{
var event = db.GetByIdOrDefault<Event>( request.Id );
}
At that point the var 'event' is populated but only the Id field is filled !
all the others fields of the class are not filled (while there are really data in database).
It's the simplest i can do and it does not work. Any ideas ?
(PS : i am using OrmLite for Oracle)
Thanks
I have found the problem.
It was due to an incorrect type matching between field in my class (defined as a string) and the corresponding Oracle Field (that is a DATE).
I replaced the string with datetime and worked like a charm.
So it's working perfectly with a VIEW and that's GREATLY simplify the code.
I had a similar problem, however my solution was different.
I had a int property in my POCO. My query (from Oracle) was returning a null for this property. It caused a exception to be raised and prevented further processing of that row.
The result was a partial populated POCO.
The solution was to change to type to be nullable.
public int? mypropperty

Primary key has to be called "Id" using EFCode?

I'm writting an application using ASP MVC 3 and based on the music store tutorial. I installed the EFCode framework and I created a database by right clicking App_Data folder.
The columns of the Table "Client" are
IdClient
Name
IdClient was defined as primary key by right clicking on it, and Identity was set to Yes and autoincrement, etc. Everything seems fine...When I run the application it tells me that this table has no key defined!!
If i rename IdClient to Id there is no problem, my question why doesn't it detect IdClient as primary key?? In the tutorial database the primary keys are not defined with a name "Id"...
Thanks!
I assume you mean Entity Framework Code First.
You can add the [Key] attribute to the column.
public class Product
{
[Key]
public int TestID { get; set; }
}
Entity Framework Code First uses convention over configuration and by default will look for a column named "ID" to make it the primary key. Otherwise you have to it declaratively.
You have to do one of two things
1) Use DataAnnotations, and in your model class give your idClient property the [Key] attribute. That will tell EF that idClient is your primary key.
2) In your data context's class, you can override the OnModelCreating(ModelBuilder modelBuilder) method. In that method you have to call something like modelBuilder.Entity<MyEntity>().HasKey(x => x.idClient); so EF knows that this is an identity and key column.
By default, EF4 Code First only automatically sets a column as a primary key if it is named Id or <entity name>Id.

Resources