Entity Framework sort by Enum value alphabetically - asp.net

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.

Related

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.

Many to one relationship, unwanted JOIN

I have many to one relationship defined within my model:
Project.CreatedBy = ApplicationUser.Id:
class Project{
[Required]
public ApplicationUser CreatedBy {get; set;}
}
With mapping:
modelBuilder.Entity<Project>().HasOptional(i => i.CreatedBy).WithOptionalDependent().WillCascadeOnDelete(false);
Unfortunatelly, when I try to retrieve all users from DbContext by:
var users = context.Users;
Generated SQL looks like:
SELECT
[...]
FROM [dbo].[AspNetUsers] AS [Extent1]
LEFT OUTER JOIN [dbo].[Projects] AS [Extent4] ON ([Extent4].[CreatedBy_Id] IS NOT NULL) AND ([Extent1].[Id] = [Extent4].[CreatedBy_Id])
So when certain user have created 10 projects, the user entity is multiplied 10 times. It makes me impossible to look for that user by his username:
context.Users.Single(u => u.Username == "test")
because it would give me Sequence contains more than one element exception.
Do you have any idea how to avoid that extra join?
I suspect it has something to do with modelBuilder declaration. I've been googling about this, but never found any solution.
Any materials about modelBuilder and defining relationships with it would be really appreciated too.
The mapping...
modelBuilder.Entity<Project>()
.HasOptional(i => i.CreatedBy)
.WithOptionalDependent()
...expresses this as a 1:1 association, not the 1:n association you intend it to be.
So change the mapping into one-to-many:
modelBuilder.Entity<Project>()
.HasOptional(p => p.CreatedBy)
.WithMany()
.HasForeignKey(p => p.CreatedBy_Id);
I'm not sure why EF joins in the Project when you query User because the association is optional), but a proper 1:n mapping will stop this.
Use
.FirstOrDefault()
instead of Single.
This method will return first object or null if object is not found. Single method throws exception if more than one element is selected.
You can also use
.First()
But this method will throw exception if no elements found.
PS I'm not sure if it's a typo, but you have to use double equal sign, like that:
context.Users.Single(u => u.Username == "test")
And keep in mind that usually in these cases LoweredUsername column is created and used for compare:
context.Users.Single(u => u.LoweredUsername == providedUsername.ToLower())
Reasons for that:
Someone can type John instead of john
Indexing on SQL server side works in this case
I suppose in ApplicationUser you have
public virtual ICollection<Project> Projects { get; set; }
Please see turn off lazy loading for all entities in MSDN
Or to turning off lazy loading for specific navigation properties make the Projects property non-virtual

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

ServiceStack ORMLite - Select columns

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

ASP.NET which type of collection should I use?

I am writing a class to save searches on my site. I want to have in the class an "Array" of all the parameters that were specified. I tried a NameValueCollection but the problem I ran into is when I have a multi-select (e.g. states) it only stores one of the entries because the key gets taken. I need a collection type that will let me have something like the following:
Name => Bob
State => Alaska
State => Oregon
State => Washington
Company => Acme
What type of collection should I use?
EDIT: ==============================
I'm not sure the comments so far will help. Let me explain a little further. This search class will be used to save the parameters for any search on my site. Different searches may or may not have the same parameters. When this classes save method is called the search will be dumped into a database. One record will be created in the Searches table and as many records an there are items in the collection will be created in the SearchesParameters table. The SearchesParamaters table has these columns (ID,searches_ID,key,value).
The database could care less if there are two parameters with a key of "State". In order to keep my class generic enough to use on all searches without having to be updated I want to have a collection/array that will let me have key/value pairs and also let me have multiple instances of the same key. Really I just want to be able to call searchObj.addParameter(KEY,VALUE); How the class handles that on the back end is mostly irrelevant so long as i can reliably get the correct keys paired up with the correct values.
Is a collection the way to go with this or should I be considering something like two arrays one storing the keys and one storing the values?
A Dictionary that maps String to an List<string>. Something like Dictionary<string, List<string>>.
If an element isn't there in the Dictionary, create a new List for the Key and add to it. Otherwise, simply add the new Value to the existing List.
Create a class, and store that class in a collection.
class Search
{
public string Name { get; set; }
public List<string> State { get; set; }
public string Company { get; set; }
}
Then you can have multiple states per search. Add instances of this to List and away you to.
what about a generic list (System.Collections.Generic)?
e.g.,
string name;
List<string> states;
string company;
You can read up about generic lists here
You should use a List<KeyValuePair<string, string>>
I would use Dictionary<string, HashSet<string>>.

Resources