Elastic scale query with EF: Nullable object must have a value - entity-framework

On Azure, I have a setup of several sharded databases and an elastic query database with external tables mirroring the tables on the shards. The two main tables I use are:
Channels:
[Name] nvarchar NOT NULL,
[Id] [uniqueidentifier] NOT NULL,
[ExternalReference] nvarchar NULL
Users:
[Email] nvarchar NOT NULL,
[FirstName] nvarchar NOT NULL,
[LastName] nvarchar NOT NULL,
[ChannelId] [uniqueidentifier] NOT NULL,
[Status] [int] NOT NULL,
[AvatarId] [uniqueidentifier] NULL,
[Id] [uniqueidentifier] NOT NULL
When I query this via EF and linq:
var user = db.Users.Include("Channel").FirstOrDefault(u => u.Email ==
"tony#soprano.com");
I get an error:
An error occurred while executing GlobalQuery operation: Nullable object must have a value.
This is what the User class looks like:
public class User
{
public Guid Id { get; set; } = SequentialGuid.NewGuid();
[Required]
public string Email { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
[Index]
public Status Status { get; set; }
public Guid? AvatarId { get; set; }
[Index]
public Guid ChannelId { get; set; }
[ForeignKey("ChannelId")]
public virtual Channel Channel { get; set; }
}
Querying directly via T-SQL:
SELECT * FROM Users INNER JOIN Channels ON Users.ChannelId = Channels.Id
gives me the same error.
Further investigation shows that casting the Ids to uniqueidentifiers (which they already are) solves the problem:
SELECT * FROM Users INNER JOIN Channels ON CAST(Users.ChannelId as uniqueidentifier) = CAST(Channels.Id as uniqueidentifier)
The ChannelId and Id Columns are already non nullable uniqueidentifiers. The data inside the shards is also valid and not null, so what exactly is the problem here?
Another question: how can I force that ‘cast to uniqueidentifier’ in linq?

Quick question, you state that "Querying directly via T-SQL:
SELECT * FROM Users INNER JOIN Channels ON Users.ChannelId = Channels.Id
gives me the same error." , are you querying from the Elastic Query Head database, or on a shard? I setup the schema and could not repro when querying from the shard, so I am wondering if I am not repro'ing the issue correctly?

Related

Entity Framework Core 6 is auto incrementing non-key columns

Entity Framework Core 6 is auto-incrementing columns in my table that it shouldn't be.
I have a table in SQL Server defined like this:
CREATE TABLE [dbo].[Company]
(
[Company_ID] [int] IDENTITY(1,1) NOT NULL,
[CompanyName] [varchar](100) NOT NULL,
[CreatedOnUtc] [datetime] NOT NULL,
[AccountManager_ID] [int] NULL,
[Project_ID] [int] NOT NULL,
[Customer_ID] [int] NOT NULL
CONSTRAINT [PK_Company]
PRIMARY KEY CLUSTERED ([Company_ID] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Company]
ADD CONSTRAINT [DF_Company_CreatedOnUtc] DEFAULT (GETUTCDATE()) FOR [CreatedOnUtc]
GO
My entity is defined like this - I am explicitly defining the key column and that it's an identity.
[Table("Company")]
public partial class CompanyEntity
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Required]
[DisplayName("Company ID")]
public int Company_ID { get; set; }
[Required]
[MaxLength(100)]
[DisplayName("Company Name")]
public string CompanyName { get; set; } = "";
[ForeignKey("AccountManager")]
[DisplayName("Account Manager ID")]
public int? AccountManager_ID { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
[Required]
[DisplayName("Created On Utc")]
public DateTime CreatedOnUtc { get; set; }
[ForeignKey("Project")]
//[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Required]
[DisplayName("Project ID")]
public int Project_ID { get; set; }
[ForeignKey("Customer")]
[Required]
[DisplayName("Customer ID")]
//[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Customer_ID { get; set; }
public AccountManagerEntity? AccountManager { get; set; }
public CustomerEntity Customer { get; set; } = new CustomerEntity();
public ProjectEntity Project { get; set; } = new ProjectEntity();
}
When I create and save a new CompanyEntity record
var company = new CompanyEntity()
{
CompanyName = "Test",
AccountManager_ID = 1
Project_ID = 1,
Customer_ID = 1,
};
Context.Company.Add(company);
await Context.SaveChangesAsync();
What I'd expect to see happen is a new record is added to the Company table that looks like:
Company_ID: xxx (auto-incremented in DB)
CompanyName: Test
AccountManager_ID: 1
Project_ID: 1
Customer_ID: 1
CreatedOnUtc: xx/xx/xxxx (auto generated)
Instead, what I see happening is that both the Project_ID and Customer_ID columns are being automatically incremented. So I'm ending up with records like this:
Company_ID: 1
CompanyName: Test
AccountManager_ID: 1
Project_ID: 1
Customer_ID: 1
CreatedOnUtc: 3/27/2022 18:13:00
Then if I delete this record and run my code again, I end up with another record (which I'd expect), but Project_ID and Customer_ID have incremented instead of using my values:
Company_ID: 2
CompanyName: Test
AccountManager_ID: 1
Project_ID: 2
Customer_ID: 2
CreatedOnUtc: 3/27/2022 18:13:00
I can see in the SQL Server profiler that EF is passing the incremented values. I'm sure this is because of some convention, but I have explicitly set attributes on these columns. AccountManager_ID seems to work OK - the only difference with that one is it's not a required field. I also tried adding
[DatabaseGenerated(DatabaseGeneratedOption.None)]
to Project_ID / Customer_ID, but that did not make any difference.
How do I tell EF, "don't do that"?
EDIT: OK, I figured out why this was happening. Because I'm creating an instance of both the CustomerEntity and ProjectEntity as default values, it's assuming I am also creating new records in these other tables. Removing the = new *Entity(); portion fixed the issue. I had only added these to make the compiler warning about the non-nullable property having a value when it exits the constructor go away. Oops.
public CustomerEntity Customer { get; set; } = new CustomerEntity();
public ProjectEntity Project { get; set; } = new ProjectEntity();
This code automatically creates new instances of Customer and Project and assignes new keys
public CustomerEntity Customer { get; set; } = new CustomerEntity();
public ProjectEntity Project { get; set; } = new ProjectEntity();
leave just like this
public CustomerEntity Customer { get; set; }
public ProjectEntity Project { get; set; }

Entity Framework 5 CodeFirst - "Column names in each table must be unique."

I'm creating the following table on PostgreSQL:
create table dbo.user_ratings (
user_id int not null,
user_rated_id int not null,
value decimal not null,
status_id int not null,
created_at timestamp with time zone not null,
updated_at timestamp with time zone null,
user_comment text null,
primary key (user_id, user_rated_id),
foreign key (user_id) references dbo.users(id),
foreign key (status_id) references dbo.status(id),
foreign key (user_rated_id) references dbo.users(id));
When mapping the table using CodeFirst, I'm doing the following:
[Table("user_ratings", Schema="dbo")]
public class UserRating {
[Key, Column("user_id", Order = 0)]
public int UserId { get; set; }
public virtual User User { get; set; }
[Key, Column("user_rated_id", Order = 1)]
public int UserRatedId { get; set; }
public virtual User UserRated { get; set; }
[Column("status_id")]
public int StatusId { get; set; }
public virtual Status Status { get; set; }
public decimal Value { get; set; }
[Column("user_comment")]
public string UserComment { get; set; }
[Column("created_at")]
public DateTime CreatedAt { get; set; }
[Column("updated_at")]
public DateTime UpdatedAt { get; set; }
}
Well, so far, so good. When I try to query something, I'm getting the error below:
Column names in each table must be unique. Column name 'User_Id' in table 'user_ratings' is specified more than once.
What is wrong with this? When I remove the mapping related to the column user_id, it's passing, but it's not right. Can someone help me?
Thank you all!!!
Seems that this error is related only to SQLServer databases. I still had some SQLServer configurations on my Web.config/App.config, that's why this error was appearing. Without SQLServer configurations, the error is gone.

EntityFramework SqlQuery does not work with custom mapping (DataAnnotation Column)

I'm using EF 5.0 (CodeFirst) with VS 2012 and am having trouble making a query using SqlQuery. The problem happens in the mapping between the property name of the entity and the name of the column in the database.
My entity (model):
[Table("Teste")]
public class TesteEntity
{
[Column("Teste_Id")]
public int Id { get; set; }
[Required]
public bool IsAdministrator { get; set; }
[Required]
public string Name { get; set; }
}
When I run the query, I get an error.
Rotine:
List<TesteEntity> list = dataContext.Database.SqlQuery<TesteEntity>("SELECT * FROM Teste").ToList();
Error:
The data reader is incompatible with the specified '....TesteEntity'. A member of the type, 'Id', does not have a corresponding column in the data reader with the same name.
Table structure in database:
CREATE TABLE [dbo].[Teste]( [Id] [int] IDENTITY(1,1) NOT NULL,
[IsAdministrator] [bit] NOT NULL, [Name] [nvarchar](max) COLLATE
Latin1_General_CI_AS NOT NULL, CONSTRAINT [PK_dbo.Teste] PRIMARY KEY
CLUSTERED ( [Id] ASC )WITH (PAD_INDEX = OFF, IGNORE_DUP_KEY = OFF)
ON [PRIMARY] ) ON [PRIMARY]
Obviously when I take DataAnnotation [Column ("Teste_Id")] and recreate the table, everything works, but wanted to know if you have to do this query using DataAnnotation [Column ("Teste_Id")].
Thanks
Please, write this code as follow:
[Table("Teste")]
public class TesteEntity
{
[Column("TesteId")]
[Key]
public int Id { get; set; }
[Required]
public bool IsAdministrator { get; set; }
[Required]
public string Name { get; set; }
}
I suggest you to write your code How EF CF work fine. For your code it is as follow:
public class Teste
{
public int TesteId{ get; set; }
[Required]
public bool IsAdministrator { get; set; }
[Required]
public string Name { get; set; }
}

How to create one to one relationship T-SQL for EntityFramework code first

I have two tables
CREATE TABLE Company (
Id uniqueidentifier primary key not null,
CompanyName nvarchar(100) not null)
GO
CREATE TABLE Feature (
Id uniqueidentifier primary key not null,
CompanyId uniqueidentifier not null,
Feature1 bit not null,
Feature2 bit not null)
GO
How to make one to one relation by CompanyId field on database and EF Code First side with configuration files
The classes:
public class Company
{
private Guid id = Guid.NewGuid();
public virtual Guid Id
{
get { return id; }
set { id = value; }
}
public virtual string CompanyName { get; set; }
}
public class Feature
{
private Guid id = Guid.NewGuid();
public virtual Guid Id
{
get { return id; }
set { id = value; }
}
public virtual Company Company { get; set; }
public virtual bool Feature1 { get; set; }
public virtual bool Feature2 { get; set; }
}
You cannot make a real one-to-one relation in EF with these tables because it requires unique index on CompanyId column and EF doesn't support unique indices yet. Currently the only way to get a real one-to-one relation is to use primary key of dependent table (in your case Feature.Id) as foreign key to principal table.
You can cheat. Just use it as one-to-many (Company can have many features) and if you have unique index in the database you will receive exception if another feature tries to associate with already used company.

Autogenerate primary key (Guid) Entity Framework CTP5

I have a following POCO class
public class Account
{
[Key,DatabaseGenerated(DatabaseGenerationOption.Identity)]
public string AccountId { set; get; }
public string FirstName { set; get; }
public string LastName { set; get; }
public string Email { set; get; }
}
I get the following exception when the database gets created
Identity column 'AccountId' must be of data type int, bigint, smallint, tinyint, or decimal or numeric with a scale of 0, and constrained to be nonnullable.
Shouldn't you have:
[Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid AccountId { set; get; }
?
Jeff's answer is right. Just a little tip for you. Using EF6 I wrote next configuration in order to set all fields with name "Id" and type "Guid" as identity.
modelBuilder.Properties<Guid>()
.Where(info => info.Name.ToLower()== "id")
.Configure(configuration => configuration.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity));
So I don't have to write [DatabaseGenerated(DatabaseGenerationOption.Identity)] everytime.
Sound like you have to update your AccountId property from string to one of the mentioned datatypes in the exception:
int, bigint, smallint, tinyint, or decimal or numeric with a scale of 0, and constrained to be nonnullable
Any reason why it's now a string?