Store MiniProfiler results in PostgreSQL - postgresql

I have integrated MiniProfiler v4.2.22 in my Asp.Net Core application.
Its is working as expected when using In-Memory database, but when i change the storage option to PostgreSQL or SQL Server, no data is coming in the results.
Code Used -
services.AddMiniProfiler(options =>
{
options.UserIdProvider = (request) =>
{
var id = request.HttpContext.User.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? request.HttpContext.User.FindFirst("sub")?.Value;
return id;
};
options.RouteBasePath = "/profiler";
options.PopupRenderPosition = StackExchange.Profiling.RenderPosition.BottomLeft;
options.PopupShowTimeWithChildren = true;
if (!string.IsNullOrEmpty(miniProfilerOptions?.PostgreSqlStorage?.ConectionString))
{
var storageOpt = miniProfilerOptions.PostgreSqlStorage;
var storage = new PostgreSqlStorage(storageOpt.ConectionString, storageOpt.ProfilersTable, storageOpt.TimingsTable, storageOpt.ClientTimingsTable);
_ = storage.TableCreationScripts;
options.Storage = storage;
}
options.ShouldProfile = (request) =>
{
if (request.Path.StartsWithSegments("/healthcheck"))
{
return false;
}
return true;
};
})
.AddEntityFramework();
appsettings.json -
"Monitoring": {
"MiniProfiler": {
"IsEnabled": true,
"PostgreSqlStorage": {
"ConectionString": "Server=localhost;Port=5432;Database=MiniProfilerTestDB;User ID=myLogin;Password=Password#1234567890;",
"ProfilersTable": "MiniProfilersTable",
"TimingsTable": "MiniProfilerTimingsTable",
"ClientTimingsTable": "MiniProfilerClientTimingsTable"
},
},
}
Model -
public class MiniProfilerOptions
{
public bool IsEnabled { get; set; }
public PostgreSqlStorageOptions PostgreSqlStorage { get; set; }
}
public class PostgreSqlStorageOptions
{
public string ConectionString { get; set; }
public string ProfilersTable { get; set; }
public string TimingsTable { get; set; }
public string ClientTimingsTable { get; set; }
}
SQL Query to create table -
CREATE TABLE "MiniProfilersTable"
(
RowId serial primary key,
Id uuid not null, -- don't cluster on a guid
RootTimingId uuid null,
Name varchar(200) null,
Started timestamp(3) not null,
DurationMilliseconds decimal(15,1) not null,
"User" varchar(100) null,
HasUserViewed boolean not null,
MachineName varchar(100) null,
CustomLinksJson varchar,
ClientTimingsRedirectCount integer null
);
CREATE UNIQUE INDEX IX_MiniProfilersTable_Id ON "MiniProfilersTable" (Id);
CREATE INDEX IX_MiniProfilersTable_User_HasUserViewed_Includes ON "MiniProfilersTable" ("User", HasUserViewed);
CREATE TABLE "MiniProfilerTimingsTable"
(
RowId serial primary key,
Id uuid not null,
MiniProfilerId uuid not null,
ParentTimingId uuid null,
Name varchar(200) not null,
DurationMilliseconds decimal(15,3) not null,
StartMilliseconds decimal(15,3) not null,
IsRoot boolean not null,
Depth smallint not null,
CustomTimingsJson varchar null
);
CREATE UNIQUE INDEX IX_MiniProfilerTimingsTable_Id ON "MiniProfilerTimingsTable" (Id);
CREATE INDEX IX_MiniProfilerTimingsTable_MiniProfilerId ON "MiniProfilerTimingsTable" (MiniProfilerId);
CREATE TABLE "MiniProfilerClientTimingsTable"
(
RowId serial primary key,
Id uuid not null,
MiniProfilerId uuid not null,
Name varchar(200) not null,
Start decimal(9, 3) not null,
Duration decimal(9, 3) not null
);
CREATE UNIQUE INDEX IX_MiniProfilerClientTimingsTable_Id on "MiniProfilerClientTimingsTable" (Id);
CREATE INDEX IX_MiniProfilerClientTimingsTable_MiniProfilerId on "MiniProfilerClientTimingsTable" (MiniProfilerId);
Am i doing something wrong ? or am i missing anything ?
Github Link - https://github.com/MiniProfiler/dotnet/issues/620

Related

Entity Framework 6 to add one-to-one association between a SalesOrder.CustomerID and Customer.Id

To everyone,
I'm trying to instantiate a SalesOrder Customer object so I can add the Customer's DisplayName to the SalesOrder.
I have this code in the SalesOrder class:
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
#nullable disable
namespace DataLayer
{
public partial class SalesOrder
{
public SalesOrder()
{
this.SalesOrderLineItems = new HashSet<SalesOrderLineItem>();
//this.Customer = new Customer().;
}
public Guid Id { get; set; }
public DateTime Date { get; set; }
public Guid CustomerID { get; set; }
public virtual ICollection<SalesOrderLineItem> SalesOrderLineItems { get; set; }
public virtual Customer Customer { get; set; }
}
}
The SalesOrder does retrieve the SalesOrderLineItems. However, everything I try to fill the instance of the Customer fails. I have the CustomerID FK on the SalesOrder object. I'm trying to use that PK/FK reference to the Customer row to bring in the instance of the customer which will allow me to reach the cust.DisplayName property. Just as I have with the SalesOrder.SalesOrderLineItems I want the Customer object to be properly attached to the SalesOrder object.
Here is the table structure which is normal PK/FK configuration between SalesOrder.CustomerID FK and Customer.Id PK.
Here is the code in the API controller that loads the SalesOrderLineItems to the SalesOrder object. This is all working perfectly...I think.
public JsonResult GetSalesOrderByID(Guid salesorderid)
{
{
var context = db.Set<SalesOrder>();
SalesOrder sorder = context.First(s => s.Id == salesorderid);
context.Entry(sorder).Collection(l => l.SalesOrderLineItems).Load();
return new JsonResult(sorder);
}
}
USE [Backend]
GO
/****** Object: Table [dbo].[**SalesOrder**] Script Date: 12/16/2022 9:20:54 AM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[SalesOrder](
[ID] [uniqueidentifier] ROWGUIDCOL NOT NULL,
[Date] [datetime] NOT NULL,
[CustomerID] [uniqueidentifier] NOT NULL,
CONSTRAINT [PK_SalesOrder] 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].[SalesOrder] ADD CONSTRAINT [DF_SalesOrder_ID] DEFAULT (newid()) FOR [ID]
GO
ALTER TABLE [dbo].[SalesOrder] WITH CHECK ADD CONSTRAINT [FK_SalesOrder_Customer] FOREIGN KEY([CustomerID])
REFERENCES [dbo].[Customer] ([ID])
GO
ALTER TABLE [dbo].[SalesOrder] CHECK CONSTRAINT [FK_SalesOrder_Customer]
GO
USE [Backend]
GO
/****** Object: Table [dbo].[Customer] Script Date: 12/16/2022 9:29:17 AM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Customer](
[ID] [uniqueidentifier] ROWGUIDCOL NOT NULL,
[DisplayName] [nvarchar](max) NOT NULL,
[FirstName] [nvarchar](50) NOT NULL,
[LastName] [nvarchar](50) NOT NULL,
CONSTRAINT [PK_Customer] 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
ALTER TABLE [dbo].[Customer] ADD CONSTRAINT [DF_Customer_ID] DEFAULT (newid()) FOR [ID]
GO
Here is the JSON response as of now:
{
"id": "fea1996f-7569-4556-971c-ecb125a53b53",
"date": "2022-10-30T00:00:00",
"customerID": "23a85521-d834-47b9-be0a-2d34fde6c2dc",
"salesOrderLineItems": [
{
"id": "7ca18a2d-1c3c-4ab6-a583-aafe3342f3d2",
"salesOrderID": "fea1996f-7569-4556-971c-ecb125a53b53",
"productID": "a8ee410e-aba3-45dc-aec3-5be389d98f96",
"productDisplayName": "Acme Soap",
"quantity": 30,
"price": 200
},
{
"id": "286997cc-b552-44fd-b7dc-fb60cfe88b61",
"salesOrderID": "fea1996f-7569-4556-971c-ecb125a53b53",
"productID": "0c56a53a-25c2-4ebf-a859-591edefb2657",
"productDisplayName": "Central Widget",
"quantity": 200,
"price": 1.05
}
],
"customer": null
}
The ideal response would be:
{
"id": "fea1996f-7569-4556-971c-ecb125a53b53",
"date": "2022-10-30T00:00:00",
"customerID": "23a85521-d834-47b9-be0a-2d34fde6c2dc",
"CustomerDisplayName": "Jack Doe",
"salesOrderLineItems": [
{
"id": "7ca18a2d-1c3c-4ab6-a583-aafe3342f3d2",
"salesOrderID": "fea1996f-7569-4556-971c-ecb125a53b53",
"productID": "a8ee410e-aba3-45dc-aec3-5be389d98f96",
"productDisplayName": "Acme Soap",
"quantity": 30,
"price": 200
},
{
"id": "286997cc-b552-44fd-b7dc-fb60cfe88b61",
"salesOrderID": "fea1996f-7569-4556-971c-ecb125a53b53",
"productID": "0c56a53a-25c2-4ebf-a859-591edefb2657",
"productDisplayName": "Central Widget",
"quantity": 200,
"price": 1.05
}
]
}
I'm trying to populate the full customer object to start with but that might not be necessary.
Ideally I'd just like the Customer.Display name to be added as a property of the SalesOrder. Then I wouldn't need the entire Customer object which is not necessary.
So maybe SalesOrder looks like this with just the CustomerDisplayName added. But I still need to reach the related Customer to derive the CustomerDisplayName.
namespace DataLayer
{
public partial class SalesOrder
{
public SalesOrder()
{
this.SalesOrderLineItems = new HashSet<SalesOrderLineItem>();
//this.Customer = new Customer().;
}
public Guid Id { get; set; }
public DateTime Date { get; set; }
public Guid CustomerID { get; set; }
public virtual ICollection<SalesOrderLineItem> SalesOrderLineItems { get; set; }
public virtual string CustomerDisplayName { get; set; }
}
}
Thanks for all your help in advance,
E
I tried to decorate the SalesOrder with the
[ForeignKey] attribute and define the CustomerID, but that didn't work.
I tried to load the Customer collection as I did with the SalesOrderLineItems, but of course the Customer doesn't have the SalesOrder relationship as the SalesOrderLineItem does. The Customer stands alone by itself and is referenced/associated in SalesOrder as a FK.

Postgresql primary key not auto incrementing even when notation serial specified using .net core

Postgresql primary key not auto incrementing even when notation serial specified
this is my table create script
CREATE TABLE public."Medication" (
"Id" serial NOT NULL,
"ResidentId" int4 NOT NULL,
"PharmacyId" int4 NULL,
"PhysicianId" int4 NULL,
"ReleasedQty" float8 NOT NULL DEFAULT '0'::double precision,
"PRNEffectiveTime" int4 NULL,
"IsSTO" bool NOT NULL DEFAULT false,
"IsPendingOrder" bool NOT NULL DEFAULT false,
"IsPsychotropic" bool NOT NULL DEFAULT false,
"IsINRRequired" bool NOT NULL DEFAULT false,
"eSignature" varchar NULL,
"eSignatureDate" timestamp NULL,
"PhysicianId_X" int4 NULL,
"IsWitnessSigReq" bool NULL,
"IsInjection" bool NULL,
"AdministerByRole" varchar NULL,
"AdministerByUser" varchar NULL,
"AlfId" int4 NOT NULL,
CONSTRAINT "Medication_pkey" PRIMARY KEY ("Id", "AlfId")
)
PARTITION BY RANGE ("AlfId");
the value of Id always remains zero
My query was inserting explicit value 0
and this was happening in .net core with Medication Object containing Id zero.
when in my class I specified Id as Identity column then it fixed
Table("Medication")]
public partial class Medication
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
I was not adding [DatabaseGenerated(DatabaseGeneratedOption.Identity)] and this is why it was happening

create non clustered index on primary key entity framework 6.0

I am aware of this, which states that it is not possible to create a primary key with non clustered index via code first. Is this still the case?
Ideally, I would like to specify via EntityTypeConfiguration, that my primary key (Guid) has a non-clustered index and there is another column (int) with a clustered index.
AFAIK this is not possible with EntityTypeConfiguration. However you can do this with Code-First migrations. Working example:
public class Product
{
public Guid Id
{ get; set; }
public int Price
{ get; set; }
}
class AppDbContext : DbContext
{
public DbSet<Product> Products
{ get; set; }
}
public partial class InitialCreate : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.Products",
c => new
{
Id = c.Guid(nullable: false),
Price = c.Int(nullable: false),
})
.PrimaryKey(t => t.Id, clustered: false)
.Index(t => t.Price, clustered: true);
}
public override void Down()
{
DropIndex("dbo.Products", new[] { "Price" });
DropTable("dbo.Products");
}
}
Result:
CREATE TABLE [dbo].[Products] (
[Id] UNIQUEIDENTIFIER NOT NULL,
[Price] INT NOT NULL,
CONSTRAINT [PK_dbo.Products] PRIMARY KEY NONCLUSTERED ([Id] ASC)
);
GO
CREATE CLUSTERED INDEX [IX_Price]
ON [dbo].[Products]([Price] ASC);
You can also do this with your OnModelCreating method like so:
modelBuilder.Entity(entityTypeName)
.HasKey(nameof(ClassName.Id))
.ForSqlServerIsClustered(false);

How to read DateTimeOffset from Postgres with Dapper.net?

Having this Postgres table
CREATE TABLE "TimeRange"
(
"Id" bigint NOT NULL,
"Start" timestamp with time zone NOT NULL,
"End" timestamp with time zone NOT NULL,
CONSTRAINT "TimeRange_pkey" PRIMARY KEY ("Id")
)
Filling it with
INSERT INTO "TimeRange"("Id", "Start", "End")
VALUES (1, '2014-03-03 05:55:00+01', '2014-03-03 05:55:00+01');
Having this C# POCO
public class TimeRange
{
public long Id { get; set; }
public DateTimeOffset Start { get; set; }
public DateTimeOffset End { get; set; }
}
Selecting data..
myOpenedNpgsqlConnection.Query<TimeRange>("SELECT * FROM \"TimeRange\"");
..results in DataException: "Error parsing column 1 (Start=03.03.2014 05:55:00 - DateTime)"
I'm aware that the timezone information is missing in the error message. Running the same sql statement in PgAdmin returns results that include timezone information.
Is there any (clean) way to read Postgres "timestamp with timezone" via dapper into DateTimeOffset?
EDIT:
I've found that modifing the POCO to following seems to work. But seriously, is there no nicer way?
public class TimeRange
{
public long Id { get; set; }
private DateTime Start { get; set; }
private DateTime End { get; set; }
public DateTimeOffset StartOffset { get { return this.Start; } set { this.Start = value.UtcDateTime; } }
public DateTimeOffset EndOffset { get { return this.Start; } set { this.Start = value.UtcDateTime; } }
}
It seems that PostgreSQL stores internally the UTC timestamp only.
Maybe it helps, if you cast the timezone information in SQL query, PostgreSQL uses the TZ from client session.
CREATE TABLE TimeRange
(
Id bigint NOT NULL,
start_at timestamp with time zone NOT NULL,
end_at timestamp with time zone NOT NULL,
CONSTRAINT TimeRange_pkey PRIMARY KEY (Id)
);
INSERT INTO TimeRange (Id, start_at, end_at)
VALUES (1, '2014-03-03 05:55:00+01', '2014-03-03 05:55:00+01');
SELECT
id,
start_at::TIMESTAMP WITH TIME ZONE as start,
end_at::TIMESTAMP WITH TIME ZONE as end
FROM TimeRange;

Entity Framework Code Only Relationship Not Being Read

(This looks like a long question, but it's not really, honest!)
I am trying to get a simple proof of concept working with Entity Framework 4 and the CTP 3 version of Code Only. It feels like I'm missing something really obvious and simple.
I have this following test which is failing:
[TestFixture]
public class ParentChildTests
{
[Test]
public void ChildRead_DatabaseContainsRelatedObjects_ParentIsNotNull()
{
var ctx = GetMyObjectContext();
var child = ctx.Children.Where(c => c.Id == 1).Single();
var parent = child.ParentTable;
Assert.That(parent, Is.Not.Null);
}
// GetMyObjectContext etc...
}
The read of child works fine and I get back a ChildTable whose ParentTableId value is '1' as I would expect, but the ParentTable property is NULL. I do not expect this because my POCOs have all virtual properties (see below) and EF4 has lazy loading enabled by default.
What am I missing?
Database
create table parent_table
(
parent_table_id int identity(1,1) primary key,
parent_table_name varchar(50) not null,
display_name varchar(50)
)
create table child_table
(
child_table_id int identity(1,1) primary key,
child_table_name varchar(50) not null,
parent_table_id int not null
)
alter table child_table add constraint FK_child_table__parent_table
foreign key (parent_table_id) references parent_table(parent_table_id)
POCO Entities
public class ParentTable
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual string DisplayName { get; set; }
}
public class ChildTable
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual int ParentTableId { get; set; }
public virtual ParentTable ParentTable { get; set; }
}
Entity Configurations
public class ParentTableConfiguration : EntityConfiguration<ParentTable>
{
public ParentTableConfiguration()
{
MapSingleType(pt => new
{
parent_table_id = pt.Id,
parent_table_name = pt.Name,
display_name = pt.DisplayName,
})
.ToTable("dbo.parent_table");
Property( pt => pt.Id ).IsIdentity();
Property( pt => pt.Name ).IsRequired();
}
}
public class ChildTableConfiguration : EntityConfiguration<ChildTable>
{
public ChildTableConfiguration()
{
MapSingleType(ct => new
{
child_table_id = ct.Id,
child_table_name = ct.Name,
parent_table_id = ct.ParentTableId,
})
.ToTable("dbo.child_table");
Property( ct => ct.Id ).IsIdentity();
Property( ct => ct.Name ).IsRequired();
Relationship(ct => ct.ParentTable)
.HasConstraint((ct, pt) => ct.ParentTableId == pt.Id);
}
}
(Thanks for reading this far!)
As far as understand you just do not load this navigation property.
This will result in eager loading.
var child = ctx.Children.Include("ParentTable").Where(c => c.Id == 1).Single();
Or you could enable lazy loading by setting ctx.ContextOptions.LazyLoadingEnabled = true;