ServiceStack ORMLite - Select columns - ormlite-servicestack

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

Related

Linq to entity enum not comparing

Here is my Enum:
public enum AdvertStatus
{
Active,
Archived
}
And my entity type:
public record Advertisement
{
...
public AdvertStatus Status { get; set; }
...
}
In database it's stored as int, Database is Postgree
When I try to compare it like so:
data = data.Where(x => x.Status == searchValues.Status);
Entity Framework throws an exception sayng:
.Status == (int)__searchValues_Status_3)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'.
I tried solutions from this question: LINQ TO ENTITY cannot compare to enumeration types but it did't work.
EDIT 1:
data is database table context IQueryable<AdvertisementDTO>
searchValues.Status is type of AdvertStatus from search filter
The issue may be higher up in your Linq query, such as you are attempting to project with a Select or ProjectTo before filtering. For simple types like int/string this should work, but depending on how your DTO is declared you might be introducing problems for mpgsql.
For instance if your query is something like:
var query = _context.Advertisements
.Select(x => new AdvertisementDTO
{
// populate DTO
}).Where(x => x.Status == searchValues.Status)
// ....
then npgsql may be having issues attempting to resolve the types between what is in the DTO and the enumeration in your searchValues. From what the exception detail looks like, npgsql is trying to be "safe" with the enum and casting to intbut feeding that to PostgreSQL that results in invalid SQL. I did some quick checks and the DTO would need to be using the same Enum type (C# complains if the DTO cast the value to int, cannot use == between AdvertStatus and int fortunately) The project may have something like a value converter or other hook trying to translate enumerations which is getting brought into the mix and gunking up the works.
Try performing the Where conditions prior to projection:
var query = _context.Advertisements
.Where(x => x.Status == searchValues.Status)
.Select(x => new AdvertisementDTO
{
// populate DTO
})
// ....
If the data value is stored as an Int then this should work out of the box. npgsql does support mapping to string (which would require a ValueConverter) as well as database declared enumerations. (https://www.postgresql.org/docs/current/datatype-enum.html) However, Int columns should work fine /w enums.
If that doesn't work, I'd try with a new DbContext instance pointed at the DB and a simple entity with that Enum to load a row from that table to eliminate whether npgsql is translating the enum correctly, just to eliminate any possible converters or other code that the main DbContext/models/DTOs may be contributing.
It was all my mistake in higher repo Select projection.
Thanks you all for help. Cheers.

Extending SELECT projection

I want to extend the documents that I receive from a SELECT clause.
Lets assume a I have a collection that stores documents in the following shape
{"foo": "yeah I am a foo", "bar": "And I am a bar"}
so that the query
SELECT * FROM f
would return the above document(s)
Now I want to add an additional property that is NOT part of the documents stored as part of the projection of the SELECT statement.
Basically I'd like to do something like using Javascript's spread operator (which is not possible in Cosmos DB)
SELECT {...*, "newprop": "oh! I am new here!"} FROM f
and which should then return document(s) like this
{"foo": "yeah I am a foo", "bar": "And I am a bar", "newprop": "oh! I am new here!"}
The one thing I DONT WANT TO DO is to repeat all the toplevel properties of my documents. So a solution in the form of
SELECT {"foo": f.foo, "bar":f.bar, "newprop": "oh! I am new here!"} FROM f
is not desired.
I also tried to get that done via a function. Which I was not able to do as I cant find out how to get the toplevel object / document handle within the SELECT clause.
I tried the following
SELECT udf.ExtendDocument(*) FROM f
SELECT udf.ExtendDocument($1) FROM f
SELECT udf.ExtendDocument(f) FROM f
SELECT udf.ExtendDocument(value) FROM f
most of which produced a syntax error
It's not possible to use SELECT *, then append columns to the projection.
One option you could explore is to add a static property and value to the class that you deserialize your data into.
For instance, you could create a class like this simple one for a person with a hardcoded property and default value. Then deserialize your query results into it with the static value added as another property with a default value.
class Person
{
[JsonProperty(PropertyName = "id")]
public string Id { get; set; }
[JsonProperty(PropertyName = "pk")]
public string Pk { get; set; }
[JsonProperty(PropertyName = "firstName")]
public string FirstName { get; set; }
[JsonProperty(PropertyName = "lastName")]
public string LastName { get; set; }
public string MyStaticColumn get; set; } = "Default Value";
}
Then the code to run the query...
public static async Task QueryPerson(Container container)
{
QueryDefinition query = new QueryDefinition("select * from c");
FeedIterator<Person> resultSet = container.GetItemQueryIterator<Person>(
query, requestOptions: new QueryRequestOptions()
{
MaxConcurrency = -1
});
List<Person> results = new List<Person>();
while (resultSet.HasMoreResults)
{
FeedResponse<Person> response = await resultSet.ReadNextAsync();
foreach(var p in response)
{
results.Add(p);
}
}
}
So I found a solution.
A) Build a user defined function that does the "Extension"
function extendProjection(x) {
var result = {
//usually one want to extend the returned doc
//with some new calculated properties and not
//with a static value
newprop: calculateNewPropFromObject(x)
}
return Object.assign(result, x)
}
B) Use the user defined function in your SELECT
SELECT VALUE udf.extendProjection(c) FROM c
//it is important to use the keyword "VALUE" eitherwise
//the resulting doc will look {$1: { //the extendedprojection }}
Having described that I would recommend against this approach
Your RUs will easily tripple. The reason seems to be the usage of the JS itself and not so much what the JS engine does.
its not possible to "reuse" different registered UDFs within your JS code.
So one has to copy code snippets
"Extended Properties" are not useable in your WHERE clause
Runtime error messages returned from Cosmos DB are horrible to decipher.
The lack of any decent development environment is basically a no go.
Like #mark-brown already answered, it does not seem to be possible.
I would just like to add that likely you shouldn't do that anyway and offer a workaround arguably better than the UDF (which is costly, hard-to-maintain, does not support multiple concurrent logic versions, etc).
If you want to add extra calculations to query output based on the same entire document, then it would make more sense to do it in business layer (after querying), not data layer (CosmsosDB queries). It would also be faster (less calculations, less data to move) and cheaper (less RU).
If you want to add static data (ex: a fix string or other constants), then the same argument applies - passing it back-and-forth to cosmosDB just makes things slower and costlier. That's not the responsibility of storage.
The workaround
If the goal is to query an entire CHILD object and add only a few selected properties from other areas of documents then its best not to try to flatten the object. Just keep your storage model objects and extras side-by-side, ex:
select c.childWithAllTheFutureChildren,
c.other.location.single.value as newProp
from c
If you really-really want to add some calculation/statics to query output then you could also still use the same pattern for entire document:
SELECT c as TheRealStoredThing,
'oh! I am new here!' as theNewProp
FROM c
Yes, it does require you to have a separate model on client side for this query, but that's a good clean practice anyway. And it's much simpler than using/maintaining UDFs.

Entity Framework sort by Enum value alphabetically

I have an entity called Comment, which has an enum property of type CommentType:
public class Comment
{
public virtual Guid Id { get; private set; }
public virtual CommentType CommentType { get; set; }
// other prop removed for simplicity
}
public enum CommentType
{
Comment,
Correction,
Improvement,
BugFix,
NewFeauture,
Other
}
I need to select the comments from database by the alphabetically value of the CommentType enum, something like
_db.Comments.OrderBy(p => p.CommentType)
However, the Enum values are treated as integers, and the sort will not work alphabetically correctly.
Is there any way to add some attributes / metadata to the Enum values to make them sort correctly alphabetically?
One solution will be to assign the integer value to enum values, but i already have many database records that will need to be updated. And this solution is not good for new added enum values.
public enum CommentType
{
Comment = 2,
Correction = 3,
Improvement = 4,
BugFix = 1,
NewFeauture = 5,
Other = 6
}
Currently your enum name is not known at database level so in my opinion you have two options.
Use value on server side:
_db.Comments.ToList().OrderBy(p => p.CommentType.ToString())
Add value on database side: You should create table that will contain comment type names. And then you can create foreign key between comments and commentTypeNames and use simple select:
_db.Comments.OrderBy(c => c.CommentTypeNames.Name)
or just make join:
_db.Comments
.Join(
_db.CommentTypeNames,
c => c.CommentType,
ctn => ctn.CommentType,
(c, ctn) => new { Comment = c, CommentName = ctn })
.OrderBy(g => g.CommentName.Name)
.Select(g => g.Comment);
There are also computed columns. I never used those though so I dont have experience. Maybe you could add column that will be resolved to string by CASE statements on database. However I am not sure how will this affect performance/maintainability. For more info you could check http://geekswithblogs.net/DavidPaquette/archive/2012/09/23/calculated-columns-in-entity-framework-code-first-migrations.aspx
For performance reasons I would advise to take option 2, it's better to do such things in database.
I assume that you are using code first so maybe this post will be helpful for you: EF5 Code First Enums and Lookup Tables
This idea of a table is nice of course, especially when the enum has many values and is likely to get new ones. However, when enum values are added both the code and the database need to be maintained.
In general when the enum is volatile I would not use an enum but only the table.
But when it is not likely to change much, you could also consider to stick with the enum simply write out the order instruction:
_db.Comments.OrderBy(p =>
p.CommentType == CommentType.Comment ? "Comment" :
p.CommentType == CommentType.Correction ? "Correction" :
p.CommentType == CommentType.Improvement? "Improvement" :
.... :
"ZZZ")
As an alternative you may convert CommentType property in your Comment class into foreign key 'CommentTypeId' pointing at CommentType(Id, Name) table, that has your Id values and corresponding text Name.
This will let you order by text easily.

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();

ASP.NET Dynamic Data DisplayColumn Attribute Causing Sorting Issue

Using ASP.NET Dynamic Data with a LINQ to SQL DataContext from the Northwind Database...
When I add a DisplayColumn Attribute to one of my LINQ to SQL entity classes and reference a property from my custom code in the partial class, I lose the ability to sort by that column in the generated GridViews. I continue to lose the ability to sort even if I reference a non-custom property as the sortColumn.
Why is this happening?
Example Code:
[DisplayColumn("LastNameFirstName", "LastName", false)]
public partial class Employee
{
public string LastNameFirstName
{
get { return LastName + ", " + FirstName; }
}
}
Aaron
EDIT: The sortColumn specifies the column that will be used to sort this entity when it is used as a foreign key (in a DropDownList), not when it is being sorted in the GridView.
That is correct because the property is not in the DB and linq to SQL will try to construct a T-SQL quert to the DB to get your entities. but will fail because these is no column with that name.
This may be by Design...
The "sortColumn" specifies the column that will be used to sort this entity when it is used as a foreign key (in a DropDownList), not when it is being sorted in the GridView.
You could try overriding the ToString() method that might work but it would only filter on the entity referenced by the FK relationship.
Try adding [ScaffoldColumn(true)] - it might trick dynamic data to enable sorting
[DisplayColumn("LastNameFirstName", "LastName", false)]
public partial class Employee
{
[ScaffoldColumn(true)]
public string LastNameFirstName
{
get { return LastName + ", " + FirstName; }
}
}

Resources