I have a tiny database with one tiny Table.
CREATE TABLE [dbo].[Connections](
[connectionID] [bigint] IDENTITY(1,1) NOT NULL,
[ipAddress] [varchar](50) NULL,
[ConnectionGUID] [varchar](100) NULL,
[created] [datetime] NULL,
[registrationID] [varchar](100) NULL,
CONSTRAINT [PK_Connections] PRIMARY KEY CLUSTERED
(
[connectionID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
When I generate the model from the database, it generates:
public virtual DbSet<Connections> Connections { get; set; }
of which:
public partial class Connections
{
public long connectionID { get; set; }
public string ipAddress { get; set; }
public string ConnectionGUID { get; set; }
public Nullable<System.DateTime> created { get; set; }
public string registrationID { get; set; }
}
I don't know why it generated the DbSet instead of the single class
When I am trying to fill the class and SaveChanges, how do I access the DbSet?
using (savitassaEntities3 entities3 = new savitassaEntities3())
{
entities3.Connections conn = new Connections();
}
It's also colliding with the SignalR class Connections as this is aSignalR Hub.
From a database perspective, think of the DbSet as the Table and the class (Connections) as a row in that table. The DbSet is included in a DbContext class which you can think of as your Database.
As for the name collision with Connections, make sure to fully namespace qualify it. entities3.Connections conn = new MyProject.MyNamespace.Connections();
Thus to select records, you would do something along the lines of
var cn = new savitassaEntities3();
var query = cn.Connections.Where(c => c.foo == bar);
To insert a connection, use
cn.Connections.Add(value);
cn.SaveChanges();
If I'm missing what you're trying to do and what you have, feel free to add some more code and I can take a look.
Related
I create a table with primary key.
I tried to insert new data with entityframework6, but it would get 23502 error.
But I add the default value to the column before I insert it.
I don't understand why it would get this error.
Table DDL:
CREATE TABLE ERRORLOG(
id numeric NOT NULL,
message varchar(50) NULL,
CONSTRAINT pterrorlog_pk PRIMARY KEY (id)
);
Model:
public partial class ERRORLOG
{
[Key]
[Column(Order = 0)]
public long ID { get; set; } = DateTimeOffset.Now.ToUnixTimeMilliseconds();
public string MESSAGE { get; set; }
}
Funcation:
using (DbContext Db as new DbContext)
using (TransactionScope transactionScope = new TransactionScope())
{
ERRORLOG iLog = new ERRORLOG();
iLog.MESSAGE = Message;
Db.ERRORLOG.Add(iLog);
Db.SaveChanges(); //Get 23502 error
}
Here is the insert script, it looks like didn't insert the id, why is that?
INSERT INTO "pterrorlog"("message") VALUES (#p_0) RETURNING "id"
Edit:
After I add this script on the Model, it works fine now.
public partial class ERRORLOG
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public long ID { get; set; } = DateTimeOffset.Now.ToUnixTimeMilliseconds();
public string MESSAGE { get; set; }
}
Looks like Entity Framework auto insert a value to the column.
After I add the script to prevent this issue, it works fine now.
[DatabaseGenerated(DatabaseGeneratedOption.None)]
Model would like:
public partial class ERRORLOG
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public long ID { get; set; } = DateTimeOffset.Now.ToUnixTimeMilliseconds();
public string MESSAGE { get; set; }
}
You can use PGAdmin to profile the SQL that EF is actually attempting to execute on SaveChanges. C# is case sensitive while Postgres defaults to lower case. If I recall NPGSQL will format all EF SQL Queries with double-quotes so if your Entities were declared with properties like ID, it would be generating statements like INSERT INTO "ERRORLOG" ( "ID", "MESSAGE" ) VALUES ( ... ) so a column named "id" wouldn't be getting set.
If you want your entities to use a different case than the DB, and leave Postgres using lower case then I'd recommend using [Column] attributes to rename the columns:
public partial class ERRORLOG
{
[Key, Column(Name = "id")]
public long ID { get; set; } = DateTimeOffset.Now.ToUnixTimeMilliseconds();
[Column(Name = "message")]
public string MESSAGE { get; set; }
}
The other detail is that Order on the Column attribute is only needed when dealing with composite keys, such as many-to-many joining tables where the PK is made up of two or more columns. It isn't needed for normal single-value PKs.
If that isn't the cause, checking the insert statement in PGAdmin should give you a clue what NPGSQL / EF is attempting to execute.
I have a Gig Model as follows:
public class Gig
{
public int Id { get; set; }
[Required]
public ApplicationUser Artist { get; set; }
[Required]
public string ArtistId { get; set; }
public DateTime DateTime { get; set; }
[Required]
[StringLength(255)]
public string Venue { get; set; }
[Required]
public Genre Genre { get; set; }
[Required]
public byte GenreId { get; set; }
}
In EF6, I was able to Eager Load Artist and Genre using the following code
var gigs = _context.Attendances
.Where(a => a.AttendeeId == userId)
.Select(a => a.Gig)
.Include(a => a.Artist)
.Include(a => a.Genre)
.ToList();
But with EF Core, the Artist info or the Genre info is not getting loaded. SQL Profiler shows that there is no INNER JOIN being called on the projection tables.
SELECT [a.Gig].[Id], [a.Gig].[ArtistId], [a.Gig].[DateTime], [a.Gig].[GenreId], [a.Gig].[Venue]
FROM [Attendances] AS [a]
INNER JOIN [Gigs] AS [a.Gig] ON [a].[GigId] = [a.Gig].[Id]
WHERE [a].[AttendeeId] = #__userId_0',N'#__userId_0 nvarchar(450)',#__userId_0=N'469d8515-9a04-46af-9276-09c6fead9e10'
Can someone help me re-write the query for EF Core please to include the projection tables?
UPDATE:
added link to db schema scripts here. posting just the gigs table here:
CREATE TABLE [dbo].[Gigs](
[Id] [int] IDENTITY(1,1) NOT NULL,
[ArtistId] [nvarchar](450) NOT NULL,
[DateTime] [datetime2](7) NOT NULL,
[GenreId] [tinyint] NOT NULL,
[Venue] [nvarchar](255) NOT NULL,
CONSTRAINT [PK_Gigs] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Attendances] WITH CHECK ADD CONSTRAINT [FK_Attendances_AspNetUsers_AttendeeId] FOREIGN KEY([AttendeeId])
REFERENCES [dbo].[AspNetUsers] ([Id])
GO
ALTER TABLE [dbo].[Attendances] CHECK CONSTRAINT [FK_Attendances_AspNetUsers_AttendeeId]
GO
ALTER TABLE [dbo].[Attendances] WITH CHECK ADD CONSTRAINT [FK_Attendances_Gigs_GigId] FOREIGN KEY([GigId])
REFERENCES [dbo].[Gigs] ([Id])
GO
ALTER TABLE [dbo].[Attendances] CHECK CONSTRAINT [FK_Attendances_Gigs_GigId]
GO
ALTER TABLE [dbo].[Gigs] WITH CHECK ADD CONSTRAINT [FK_Gigs_AspNetUsers_ArtistId] FOREIGN KEY([ArtistId])
REFERENCES [dbo].[AspNetUsers] ([Id])
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[Gigs] CHECK CONSTRAINT [FK_Gigs_AspNetUsers_ArtistId]
GO
ALTER TABLE [dbo].[Gigs] WITH CHECK ADD CONSTRAINT [FK_Gigs_Genres_GenreId] FOREIGN KEY([GenreId])
REFERENCES [dbo].[Genres] ([Id])
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[Gigs] CHECK CONSTRAINT [FK_Gigs_Genres_GenreId]
GO
If you turn on EF Core Logging, you'll see inside the log something like this:
The Include operation for navigation: 'a.Gig.Artist' was ignored because the target navigation is not reachable in the final query results.
and similar for a.Gig.Genre.
Looks like EF Core at this time cannot handle includes for such queries (that don't start from the resulting entity). The only workaround I can propose is to rewrite the query like this:
var gigs = _context.Gigs
.Where(g => g.Attendances.Any(a => a.AttendeeId == userId))
.Include(g => g.Artist)
.Include(g => g.Genre)
.ToList();
or this (translates to better SQL, although the SQL execution plan could be the same):
var gigs = (from g in _context.Gigs
from a in g.Attendances
where a.AttendeeId == userId
select g)
.Include(g => g.Artist)
.Include(g => g.Genre)
.ToList();
I have a (strange) situation.
I am using Entity Framework Code First but I have to attach to an existing Database.
I do not have to map every single table of the database in my object model. So I would like to migrate single Tables, whenever I need it.
I try to explain better. My database have about 100 tables, but I need to map in my model just 3 or 4. I have created my classes in c# and now I would like to map this classes with the tables I need.
Is it possible to do it? Do I have to do a migration?
UPDATE
Here my class:
public class PricePlan
{
public Guid Id { get; set; }
public String Name { get; set; }
public Double ActivationPrice { get; set; }
}
Here the context:
public class PublicAreaContext : DbContext
{
public DbSet<PricePlan> PricePlans { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<PricePlan>()
.HasKey(pp => new { pp.Id });
base.OnModelCreating(modelBuilder);
}
}
Here the table:
ALTER TABLE [dbo].[PricePlan](
[Id] [uniqueidentifier] NOT NULL,
[Name] [varchar](50) NULL,
[ActivationPrice] [decimal](5, 2) NULL,
... //Other columns
CONSTRAINT [PK_Price_Plans] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
possible: yes
migration: no. If you need migration you may have problem as in this case you haven't the __migrationHistory table (as the db is "existing" by opposite to "created by EF").
But the answer is definitively yes.
Create your classes, create a DbContext comprising DbSet, "et voilĂ ".
I've just noticed a rather strange behaviour with how Entity Framework creates a Code-First DB when you have a model with a parent that has a list of children, but also an optional navigation property to one particular child: I end up with an additional nullable foreign key column on the child that I'm not expecting. Can anyone please explain whether this column is actually necessary? And for that matter, can anyone suggest a better way of indicating that a particular child is Selected/Active.
To elaborate:
Given this model:
public class Parent
{
public int Id { get; set; }
public virtual List<Child> Children { get; set; }
// Optional navigation property to one of the child objects.
public int? ActiveChildId { get; set; }
public virtual Child ActiveChild { get; set; }
}
public class Child
{
public int Id { get; set; }
public int ParentId { get; set; }
public virtual Parent Parent { get; set; }
}
I end up with the following DB:
CREATE TABLE [dbo].[Parents](
[Id] [int] IDENTITY(1,1) NOT NULL,
[ActiveChildId] [int] NULL,
CONSTRAINT [PK_dbo.Parents] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Parents] WITH CHECK ADD CONSTRAINT [FK_dbo.Parents_dbo.Children_ActiveChildId] FOREIGN KEY([ActiveChildId])
REFERENCES [dbo].[Children] ([Id])
GO
ALTER TABLE [dbo].[Parents] CHECK CONSTRAINT [FK_dbo.Parents_dbo.Children_ActiveChildId]
GO
CREATE TABLE [dbo].[Children](
[Id] [int] IDENTITY(1,1) NOT NULL,
[ParentId] [int] NOT NULL,
[Parent_Id] [int] NULL,
CONSTRAINT [PK_dbo.Children] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Children] WITH CHECK ADD CONSTRAINT [FK_dbo.Children_dbo.Parents_Parent_Id] FOREIGN KEY([Parent_Id])
REFERENCES [dbo].[Parents] ([Id])
GO
ALTER TABLE [dbo].[Children] CHECK CONSTRAINT [FK_dbo.Children_dbo.Parents_Parent_Id]
GO
ALTER TABLE [dbo].[Children] WITH CHECK ADD CONSTRAINT [FK_dbo.Children_dbo.Parents_ParentId] FOREIGN KEY([ParentId])
REFERENCES [dbo].[Parents] ([Id])
GO
ALTER TABLE [dbo].[Children] CHECK CONSTRAINT [FK_dbo.Children_dbo.Parents_ParentId]
GO
i.e. there is both a ParentId (NOT NULL) column on the child AND a Parent_Id (NULL) column on the child.
It seems to me that since we've already got a 1:N foreign key relationship Parent to Child, then by adding a one-way 1:[0 or 1] Parent to Child relationship it shouldn't create another foreign key column on the child.
Add the InverseProperty attribute:
public class Child
{
public int Id { get; set; }
public int ParentId { get; set; }
[InverseProperty( "Children" )]
public virtual Parent Parent { get; set; }
}
Or map the relationship via Fluent API and specify the FK as ParentId:
modelBuilder.Entity<Parent>()
.HasMany( p => p.Children )
.WithRequired( c => c.Parent )
.HasForeignKey( c => c.ParentId );
Edit: full source code now available at https://bitbucket.org/moerie/multilanguagestring
I've implemented a class called MultilingualString which can serialize a dictionary to XML and be saved to the database.
(See source here: https://gist.github.com/4491990 )
This is actually an implementation of IDictionary but I don't actually add that to my class as it seems to cause hiccups for the Entity Framework.
usage of this class is like follows:
Suppose a class Product is defined like this:
public class Product
{
public int Id { get; set; }
public virtual MultilingualString Description { get; set; }
public virtual MultilingualString Name { get; set; }
}
Then you can do the following:
// make some products
var product1 = new Product
{
Id = 1,
Name = new MultilingualString(),
Description = new MultilingualString()
};
product1.Name["fr"] = "Produit 1";
product1.Description["fr"] = "DĂ©scription 1";
product1.Name["nl"] = "Produkt 1";
product1.Description["nl"] = "Omschrijving 1";
Anyway, since I only want the Id and the Xml value I made an EntityTypeConfiguration:
public class MultilingualStringConfiguration: EntityTypeConfiguration<MultilingualString>
{
public MultilingualStringConfiguration()
{
Ignore(m => m.Count);
Ignore(m => m.Keys);
Ignore(m => m.Values);
Ignore(m => m.IsReadOnly);
}
}
When I run this code, the MultilingualStrings table is created as followed:
CREATE TABLE [dbo].[MultilingualStrings](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Value] [nvarchar](max) NULL,
[Item] [nvarchar](max) NULL,
CONSTRAINT [PK_dbo.MultilingualStrings] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
As you can see, the Entity Framework adds a property called 'Item', and I suspect this is because of the following property in MultilingualString:
/// <summary>
/// Gets or sets the element with the specified key.
/// </summary>
/// <returns>
/// The element with the specified key.
/// </returns>
/// <param name="key">The key of the element to get or set.</param><exception cref="T:System.ArgumentNullException"><paramref name="key"/> is null.</exception><exception cref="T:System.Collections.Generic.KeyNotFoundException">The property is retrieved and <paramref name="key"/> is not found.</exception><exception cref="T:System.NotSupportedException">The property is set and the <see cref="T:System.Collections.Generic.IDictionary`2"/> is read-only.</exception>
public string this[string key]
{
get
{
if (ContainsKey(key))
return Translations[key];
return string.Empty;
}
set
{
Translations[key] = value;
UpdateValue();
}
}
This is the property that allows my MultilingualString property to be used as a Dictionary, but sadly I can't seem to reach this property from the EntityTypeConfiguration.
How can I ignore this property? Thanks in advance.
P.S. For those of you interested in the MultilingualString class, note that you will need the LinqKit library (and specifically the AsExpandable() and Compile() functions to perform Linq queries with this class.
Do you have access to edit MultilingualString? Try adding the annotation [NotMapped] on top of the property that's giving you trouble.
Full source code:
https://bitbucket.org/moerie/multilanguagestring
Since this MultilingualString is in fact a property, I tried to resolve my problem by just mapping my MultilingualString to a String property. Then I could just tell the Entity Framework to not map my MultilingualString property entirely.
This is my product class now:
public class Product
{
private MultilingualString _description;
private MultilingualString _name;
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
public virtual string DescriptionXml
{
get { return Description.Value; }
set { Description.Value = value; }
}
public virtual string NameXml
{
get { return Name.Value; }
set { Name.Value = value; }
}
[NotMapped]
public virtual MultilingualString Description
{
get { return _description ?? (_description = new MultilingualString()); }
set { _description = value; }
}
[NotMapped]
public virtual MultilingualString Name
{
get { return _name ?? (_name = new MultilingualString()); }
set { _name = value; }
}
}
This also seriously simplifies my database.
Yes, I couldn't find a fluent API solution for my original problem, but this workaround turned out to be excellent.