Misleading SQL Exception Text cannot be compared - servicestack

I get that exception when OrmLite make the following call :
return db.Select<T>(x => x.Name == name && x.PuId == puId).FirstOrDefault();
Exception :"System.Data.SqlClient.SqlException (0x80131904): The text,
ntext, and image data types cannot be compared or sorted, except when
usingIS NULL or LIKE operator.
The name is a String and puid is an Int. The type is mapped to a SQL Table which has no columns of type Text, NText or image at all.
When I look at the LastSQLStatement and executes it from SQL Server, it works. When I replace the call with the following, it works fine too
return db.SqlList<T>("SELECT Event_Id, Event_Num, Entry_On, Timestamp, Applied_Product, Source_Event, Event_Status, Confirmed, User_Id, Extended_Info, Comment_Id, PU_Id FROM Events WHERE ((Event_Num = #Event_Num) AND (PU_Id = #PU_Id))",new {Event_Num= "16J2730", PU_Id=91}).FirstOrDefault();
An old version of my service works fine with the same code. Using the latest version of servicestack and ormlite, I am now getting that weird issue...
Is the latest version of OrmLite has issues with old version of SQL Server? We are still on a 2000 version. I used both SQLServer Dialect without luck.
Anyone have an idea?
Here is what Mythz requested
public ProficyEvent TestGetByName(string name, int puId, bool withDetails = false)
{
using (IDbConnection db = OpenDBConnection())
{
try
{
return db.Select<ProficyEvent>(x => x.Name == name && x.PuId == puId).FirstOrDefault();
}
catch (Exception ex)
{
log.ErrorFormat("Error querying database: {0}", ex.ToString());
throw;
}
}
}
[Alias("Events")]
public class ProficyEvent:IProficyPuEntity
{
[AutoIncrement]
[Alias("Event_Id")]
public int Id { get; set; }
[Ignore]
public string Code { get; set; }
[Ignore]
public string Desc { get; set; }
[Alias("Event_Num")]
public string Name { get; set; }
[Alias("Entry_On")]
public DateTime? LastModified { get; set; }
[Ignore]
public string LastModifiedBy { get; set; }
public DateTime? Timestamp { get; set; }
[Alias("Applied_Product")]
public int? AppliedProductId { get; set; }
[Ignore]
public string AppliedProductName { get; set; }
[Ignore]
public int OriginalProductId { get; set; }
[Ignore]
public string OriginalProductName { get; set; }
[Alias("Source_Event")]
public int? SourceEvent { get; set; }
[Alias("Event_Status")]
public int? EventStatus { get; set; }
[Ignore]
public string EventStatusName { get; set; }
public int Confirmed { get; set; }
[Alias("User_Id")]
public int UserId { get; set; }
[Alias("Extended_Info")]
public string ExtendedInfo { get; set; }
[Ignore]
public string Comment { get; set; }
[Alias("Comment_Id")]
public int? CommentId { get; set; }
[Ignore]
public IEnumerable<ProficyTest> TestResults { get; set; }
[Alias("PU_Id")]
public int PuId { get; set; }
[Ignore]
public string UnitName { get; set; }
[Ignore]
public string LineName { get; set; }
}
CREATE TABLE [dbo].[Events](
[Event_Id] [int] IDENTITY(1,1) NOT NULL,
[Event_Num] [Varchar_Event_Number] NOT NULL,
[PU_Id] [int] NOT NULL,
[TimeStamp] [datetime] NOT NULL,
[Applied_Product] [int] NULL,
[Source_Event] [int] NULL,
[Event_Status] [tinyint] NULL,
[Confirmed] [bit] NOT NULL DEFAULT (0),
[User_Id] [int] NULL,
[Comment_Id] [int] NULL,
[Entry_On] [datetime] NULL,
[Testing_Status] [int] NULL DEFAULT (1),
[Event_Subtype_Id] [int] NULL,
[Start_Time] [Datetime_ComX] NULL,
[Extended_Info] [varchar](255) NULL,
[Converted_Timestamp] [datetime] NULL,
[Orientation_X] [float] NULL,
[Orientation_Y] [float] NULL,
[Orientation_Z] [float] NULL,
[Final_Dimension_Z] [real] NULL,
[Final_Dimension_A] [real] NULL,
[Initial_Dimension_A] [real] NULL,
[Final_Dimension_X] [real] NULL,
[Final_Dimension_Y] [real] NULL,
[Initial_Dimension_Y] [real] NULL,
[Initial_Dimension_Z] [real] NULL,
[Initial_Dimension_X] [real] NULL,
[Conformance] [tinyint] NULL,
[Testing_Prct_Complete] [tinyint] NULL)
CREATE TYPE [dbo].[Varchar_Event_Number] FROM [varchar](25) NOT NULL
CREATE TYPE [dbo].[Datetime_ComX] FROM [datetime] NOT NULL

To answer this question, this runs without issue on a recent version of SQL Server.
The major change to OrmLite that would have likely affected this behavior was the change to use Parameterized SQL Expressions from in-line SQL params.
You can use in-line SQL Params using OrmLite's legacy APIs or by dropping down to use Custom SQL APIs.

Related

Update partial object which has inner sub objects using NEST query in Elastic Search

Current structure in Elastic Search DB, is it stores 2 sub objects called PatientPersonal and PatientScan in the main object PatientClass. My requirement is, I need to update partial fields in PatientClass object.
I need to update only below fields
PatientClass -> Timestamp
PatientClass -> ScannerName
PatientClass -> PatientPersonal -> PatientId
PatientClass -> PatientPersonal -> PatientWeight
PatientClass -> PatientPersonal -> PatientInsurancePlan
PatientClass -> PatientScan -> PatientTransportArrangements
PatientClass -> PatientScan -> Priority
PatientClass -> PatientScan -> ProcedurePriority
using System;
using Nest;
public class PatientClass
{
[Keyword]
public string Uid { get; set; }
[Object]
public PatientPersonal patientDetails{ get; set; }
[Object]
public PatientScan scanDetails{ get; set; }
[Date]
public DateTime TimeStamp { get; set; }
[Date]
public DateTime InitialProcessingScanDateTime { get; set; }
[Number(NumberType.Long)]
public long? AppointmentTime { get; set; }
[Date]
public DateTime ScanTime { get; set; }
[Keyword]
public string ScannerName { get; set; }
}
}
public class PatientScan
{
[Keyword]
public string ProcedureScanUid { get; set; }
[Date(Format = "strict_hour_minute_second")]
[StringTimeSpan]
public TimeSpan? EntryTime { get; set; }
[Keyword]
public string ProcedurePriority { get; set; }
[Keyword]
public string PatientTransportArrangements { get; set; }
[Keyword]
public string Priority { get; set; }
[Keyword]
public string ProcedureLocation { get; set; }
}
public class PatientPersonal
{
[Keyword]
public string PatientId { get; set; }
[Keyword]
public string PatientIdIssuer { get; set; }
[Keyword]
public string PatientInsurancePlan { get; set; }
[Keyword]
public string ScheduledPatientSex { get; set; }
[Number(NumberType.Long)]
public long? ScheduledPatientWeight { get; set; }
[Keyword]
public double? ScheduledPatientSize { get; set; }
}
Currently I'm using
var response = await ElasticClient.UpdateAsync(patient.Uid, UpdateInitialFieldsSelector(Patient));
private Func<UpdateDescriptor<Patient, object>, IUpdateRequest<Patient, object>> UpdateInitialFieldsSelector(Patient pat)
{
return i => i
.Routing(Patient.Uid.ToString(CultureInfo.InvariantCulture))
.Doc(new
{
ScannerName,
patientPersonal.PatientWeight ,
patientPersonal.PatientId,
patientPersonal.PatientInsurancePlan,
patientScan.PatientTransportArrangements,
patientScan.Priority,
patientScan.ProcedurePriority,
TimeStamp = Convert.ToDateTime(DateTime.UtcNow.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture))
})
.RetryOnConflict(ElasticClient.MaxRetriesOnConflict)
.Refresh(Refresh.False);
}
Issue:
PatientId, PatientWeight, PatientInsurancePlan,
PatientTransportArrangements, Priority, ProcedurePriority
All these fields are being created newly at the root level mapping[under PatientClass] in ES and not getting updated as sublevel object field mapping values. How do I make a partial fields update at the inner object level?
NOTE
Need solution as NEST query and not as script

Custom Mapping where source property does not exist

Is it possible to map from a source object to a destination object where the source object is different?
The config table has a related objects to the lookup tables via the Code property (kind of a loose coupling scenario)
ServiceLookup & ClassificationLookup cannot exist in the source object for legacy reasons.
These are the tables.
CREATE TABLE Config ( Id INT IDENTITY (1, 1) NOT NULL
, Code NVARCHAR (50) NOT NULL
, ServiceLookupCode NVARCHAR (50) NOT NULL
, ClassificationLookupCode NVARCHAR (50) NOT NULL
, SomeConfigValue NVARCHAR (50) NOT NULL
, CONSTRAINT PK_Table1 PRIMARY KEY CLUSTERED (Id ASC)
, CONSTRAINT UQ_Table1_Code UNIQUE NONCLUSTERED (Code ASC) );
CREATE TABLE ServiceLookup ( Id int IDENTITY(1,1) NOT NULL
, Code nvarchar(50) NOT NULL
, Name nvarchar(255) NOT NULL
, CONSTRAINT PK_ServiceLookup PRIMARY KEY CLUSTERED (Id ASC)
, CONSTRAINT UQ_ServiceLookup_Code UNIQUE NONCLUSTERED (Code ASC) );
CREATE TABLE ClassificationLookup ( Id int IDENTITY(1,1) NOT NULL
, Code nvarchar(50) NOT NULL
, Name nvarchar(255) NOT NULL
, CONSTRAINT PK_ClassificationLookup PRIMARY KEY CLUSTERED (Id ASC)
, CONSTRAINT UQ_ClassificationLookup_Code UNIQUE NONCLUSTERED (Code ASC) );
SQL Example to return the data
SELECT c.Id, c.Code, c.SomeConfigValue
, sl.Name AS ServiceName
, cl.Name AS ClassificationName
FROM Config c
INNER JOIN ServiceLookup sl ON c.ServiceLookupCode = sl.Code
INNER JOIN ClassificationLookup cl ON c.ClassificationLookupCode = cl.Code
Entities
The ServiceLookup and ClassificationLookup tables cannot be defined in the Config table (legacy reasons)
public class Config
{
public int Id { get; set; }
public string Code { get; set; }
public string ServiceLookupCode { get; set; }
public string ClassificationLookupCode { get; set; }
public string SomeConfigValue { get; set; }
//public ServiceLookup ServiceLookup { get; set; } <-- This is not allowed due to loose coupling design (legacy reasons)
//public ClassificationLookup ServiceLookup { get; set; } <-- This is not allowed due to loose coupling design (legacy reasons)
}
public class ServiceLookup
{
public int Id { get; set; }
public string Code { get; set; }
public string Name { get; set; }
}
public class ClassificationLookup
{
public int Id { get; set; }
public string Code { get; set; }
public string Name { get; set; }
}
DTO
public class ConfigDto
{
public int Id { get; set; }
public string Code { get; set; }
public string ServiceLookupCode { get; set; }
public string ClassificationLookupCode { get; set; }
public string SomeConfigValue { get; set; }
public LookupModel ServiceLookup { get; set; }
public LookupModel ClassificationLookup { get; set; }
public class LookupModel
{
public int Id { get; set; }
public string Code { get; set; }
public string Name { get; set; }
}
}
Linq code
var result = await (from c in Db.Config
join sl in Db.ServiceLookup on c.ServiceLookupCode equals dl.Code
join cl in Db.ClassificationLookup on c.ClassificationLookupCode equals cl.Code
select new ConfigDto
{
Id = c.Id,
Code = c.Code,
SomeConfigValue = c.SomeConfigValue,
ServiceLookupCode = c.ServiceLookupCode,
ClassificationLookupCode = c.ClassificationLookupCode,
ServiceLookup = new ConfigDto.LookupModel
{
Id = sl.Id,
CodePath = sl.Code,
Name = sl.Name
},
ClassificationLookup = new ConfigDto.LookupModel
{
Id = cl.Id,
Code = cl.Code,
Name = cl.Name
}
}
)

Selecting OrmLite new object from joined table for insertion

I have 3 entities:
[CompositeIndex(nameof(Url), nameof(TargetDomainRecordId), nameof(UserAuthCustomId), Unique = true)]
public class WatchedUrlRecord
{
[AutoIncrement]
public long Id { get; set; }
public string Url { get; set; }
public string Provider { get; set; }
public string DomainKey { get; set; }
public WatchedUrlScanStatus WatchedUrlScanStatus { get; set; }
public bool NoFollow { get; set; }
public HttpStatusCode HttpStatusCode { get; set; }
public DateTime? LastScanTime { get; set; }
public WatchedUrlScanResult LastScanData { get; set; }
public string Anchors { get; set; }
public int? OutboundLinks { get; set; }
[ForeignKey(typeof(TargetDomainRecord), OnDelete = "CASCADE")]
public long TargetDomainRecordId { get; set; }
[ForeignKey(typeof(UserAuthCustom), OnDelete = "CASCADE")]
public long UserAuthCustomId { get; set; }
}
[CompositeIndex(nameof(Url), nameof(TargetDomainRecordId), nameof(UserAuthCustomId), Unique = true)]
public class WatchedUrlQueue
{
[PrimaryKey]
public long WatchedUrlRecordId { get; set; }
[Index]
public string Url { get; set; }
[Index]
public string DomainKey { get; set; }
[Index]
public long TargetDomainRecordId { get; set; }
public string TargetDomainKey { get; set; }
[Index]
public DateTime CreateDate { get; set; } = DateTime.UtcNow;
public int Tries { get; set; }
[Index]
public DateTime? DeferUntil { get; set; }
[Index]
public long UserAuthCustomId { get; set; }
[Index]
public bool FirstScan { get; set; }
}
[CompositeIndex(nameof(Url), nameof(UserAuthCustomId), Unique = true)]
public class TargetDomainRecord
{
[AutoIncrement]
public long Id { get; set; }
public string Url { get; set; }
public string DomainKey { get; set; }
public DateTime CreateDate { get; set; } = DateTime.Now;
public DateTime? DeleteDate { get; set; }
public bool IsDeleted { get; set; }
public bool Active { get; set; } = true;
public DomainType DomainType { get; set; }
[ForeignKey(typeof(UserAuthCustom), OnDelete = "CASCADE")]
public long UserAuthCustomId { get; set; }
}
I am trying to insert queue objects based on IDs of WatchedUrlRecords so I came up with this query:
var q = db.From<WatchedUrlRecord>()
.Where(x => Sql.In(x.Id, ids))
.Join<TargetDomainRecord>((w, t) => w.TargetDomainRecordId == t.Id)
.Select<WatchedUrlRecord, TargetDomainRecord>((w, t) => new WatchedUrlQueue()
{
UserAuthCustomId = w.UserAuthCustomId,
DomainKey = w.DomainKey,
CreateDate = DateTime.UtcNow,
DeferUntil = null,
FirstScan = firstScan,
TargetDomainKey = t.DomainKey,
Tries = 0,
TargetDomainRecordId = w.TargetDomainRecordId,
Url = w.Url,
WatchedUrlRecordId = w.Id
});
var inserted = db.InsertIntoSelect<WatchedUrlQueue>(q, dbCmd => dbCmd.OnConflictIgnore());
This doesn't work and gives error:
variable 'w' of type 'Project.ServiceModel.WatchedUrl.Entities.WatchedUrlRecord' referenced from scope '', but it is not defined
If I try anonymous object like new {} instead of new WatchedUrlQueue then InsertIntoSelect() throws error:
'watched_url_record"."user_auth_custom_id' is not a property of 'WatchedUrlQueue'
I have looked in documentation and can see SelectMulti() method but I don't think that is suitable as it will involve me creating a tuple list to combine into the new object. The passed list can be quite large so I just want to send the correct SQL statement to PostgreSQL which would be along lines of:
insert into watched_url_queue (watched_url_record_id, url, domain_key, target_domain_record_id, target_domain_key, create_date, tries, defer_until, user_auth_custom_id)
select wur.id watched_url_record_id,
wur.url url,
wur.domain_key,
wur.target_domain_record_id,
tdr.domain_key,
'{DateTime.UtcNow:MM/dd/yyyy H:mm:ss zzz}' create_date,
0 tries,
null defer_until,
wur.user_auth_custom_id
from watched_url_record wur
join target_domain_record tdr on wur.target_domain_record_id = tdr.id
where wur.id in (323,3213123,312312,356456)
on conflict do nothing ;
I currently have a lot of similar type queries in my app and it is causing extra work maintaining them, would be really nice to be able to have them use fluent api without reducing performance. Is this possible?
Custom select expression can't be a typed projection (i.e. x => new MyType { ... }), i.e. you'd need to use an anonymous type expression (i.e. new { ... }) which captures your query's Custom SELECT Projection Expression.
You'll also need to put your JOIN expressions directly after FROM (as done in SQL) which tells OrmLite it needs to fully qualify subsequent column expressions like Id which would otherwise be ambiguous.
I've resolved an issue with field resolution of custom select expressions in this commit where your query should now work as expected:
var q = db.From<WatchedUrlRecord>()
.Join<TargetDomainRecord>((w, t) => w.TargetDomainRecordId == t.Id)
.Where(x => Sql.In(x.Id, ids))
.Select<WatchedUrlRecord, TargetDomainRecord>((w, t) => new {
UserAuthCustomId = w.UserAuthCustomId,
DomainKey = w.DomainKey,
CreateDate = DateTime.UtcNow,
DeferUntil = (DateTime?) null,
FirstScan = firstScan,
TargetDomainKey = t.DomainKey,
Tries = 0,
TargetDomainRecordId = w.TargetDomainRecordId,
Url = w.Url,
WatchedUrlRecordId = w.Id
});
var inserted = db.InsertIntoSelect<WatchedUrlQueue>(q, dbCmd=>dbCmd.OnConflictIgnore());
This change is available from v5.10.5 that's now available on MyGet.

Servicestack Ormlite generates invalid SQL query for custom select

I am using version 4.5.14 of Servicestack ormlite
here "InMaintenance" Property is ignored as it is not the "Network" table column in the database. I want to set the value of the InMaintenance property based on whether the "Enddate" column in the NetworkMain table has value or not.
Following is the code
but the above code generates the following SQL query for SelectExpression
as we can see there is no space between the not null condition in the above expression.
And FromExpression is as follows
I know that I can use the SQL query in the select but how to resolve this issue?
Thanks!
Amol
4.5.14 is several years old, but this generates valid SQL in the latest version of OrmLite. Here's a live demo on Gistlyn you can run:
OrmLiteUtils.PrintSql();
public class Network
{
[PrimaryKey]
public string Id { get; set; }
public string Name { get; set; }
[Ignore]
public bool InMaintenance { get; set; }
}
public class NetworkMain
{
[PrimaryKey]
public string Id { get; set; }
[ForeignKey(typeof(Network))]
public string NetworkId { get; set; }
public DateTime? EndDate { get; set; }
}
public class NetworkDTO
{
public string Id { get; set; }
public string Name { get; set; }
public bool InMaintenance { get; set; }
}
var q = db.From<Network>()
.LeftJoin<NetworkMain>()
.Select<Network, NetworkMain>((a, m) => new
{ a,
InMaintenance = m.NetworkId == a.Id && m.EndDate.HasValue ? "1" : "0"
}).OrderBy(x=>x.Name);
var results = db.Select<NetworkDTO>(q).ToList();
Which generates:
SELECT "Network"."Id", "Network"."Name", (CASE WHEN (("NetworkMain"."NetworkId"="Network"."Id")AND("NetworkMain"."EndDate" is not null)) THEN #0 ELSE #1 END) AS InMaintenance
FROM "Network" LEFT JOIN "NetworkMain" ON
("Network"."Id" = "NetworkMain"."NetworkId")
ORDER BY "Network"."Name"

How to perform a more complex query with AutoQuery

Given the following definitions from a ServiceStack endpoint:
public class LoanQueue
{
public int LoanId { get; set; }
public DateTime Submitted { get; set; }
public DateTime Funded { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int Fico { get; set; }
public int Fraud { get; set; }
public int CDS { get; set; }
public int IDA { get; set; }
public string Income { get; set; }
public string Liabilities { get; set; }
public string Agent { get; set; }
public string Status { get; set; }
public string State { get; set; }
public string Product { get; set; }
public string Comment { get; set; }
}
public enum DateType
{
None,
Submitted,
Funded
}
[Route("/loan/queue/search", "GET")]
public class LoanQueueQueryGet : QueryBase<LoanQueue>
{
public DateType DateType { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public string AgentUserName { get; set; }
public Languange Languange { get; set; }
public bool WorkingLoan { get; set; }
public bool MicrobusinessLoan { get; set; }
public LoanStatus LoanStatus { get; set; }
}
public object Get(LoanQueueQueryGet request)
{
if (request == null) throw new ArgumentNullException("request");
var profiler = Profiler.Current;
using (profiler.Step("LoanServices.LoanQueue"))
{
SqlExpression<LoanQueue> q = AutoQuery.CreateQuery(request, Request.GetRequestParams());
QueryResponse<LoanQueue> loanQueueResponse = AutoQuery.Execute(request, q);
return loanQueueResponse;
}
}
My question is this, "Is it even possible to run conditional logic based on the request object in the service impl"? e.g.
If DateType == DateType.Submitted
then query the Submitted property on the LoanQueue with a BETWEEN clause (StartDate/EndDate) or
If DateType == DateType.Funded
then query the Funded property on the LoanQueue with a BETWEEN clause (StartDate/EndDate).
My guess is that I'm trying to bend AutoQuery too far and would be better served just coding it up the old fashion way. I really like the baked-in features of the AutoQuery plugin and I'm sure there will be times when it will suit my needs.
Thank you,
Stephen
AutoQuery will ignore any unmatched fields so you're able to use them to extend your populated AutoQuery with additional custom logic, e.g:
public object Get(LoanQueueQueryGet request)
{
var q = AutoQuery.CreateQuery(request, Request.GetRequestParams());
if (request.DateType == DateType.Submitted)
{
q.And(x => x.Submitted >= request.StartDate && x.Submitted < request.EndDate);
}
else
{
q.And(x => x.Funded >= request.StartDate && x.Funded < request.EndDate);
}
var loanQueueResponse = AutoQuery.Execute(request, q);
return loanQueueResponse;
}

Resources