Maybe the answer will be "by design". But enum properties are mostly used for filtering. So they need "Db Index". But If you map them to varchar(max) we could not create index for them in Sql Server.
So if answer is "by design" could you add [Flag] style binding alternative.
This is architectural mistake to me and no solution.
So we made
int UserTypeId {get;set,} // It is for db
UserTypeEnum UserTypeEnum {get;set;} // It manipulates UserTypeId and read from UserTypeId
Related
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.
I'm using Entity Framework (DB First) on a new project and wanted to add some customisation to the classes generated. However, my changes are obviously lost every time that the edmx is refreshed. I was just wondering if there is a design pattern for handling this sort of thing?
As an example, suppose I have a class with a integer property; StatusID - and I'd like to extend the entity class so that the status value can also be accessed/set via the related enum and finally a property that gets a text representation of that Enum from the description attribute. This all works, but those customisations are lost when the model is refreshed. I appreciate that the property can be converted to an enum, so the latter property that gets the description of the enum is perhaps a better example for this question.
I think I know the answer but I just wanted to put this out there in case there were some magic tricks that would allow this to work and prevent those customisations from being lost.
public int StatusID { get; set; }
public Enumerations.ValidationStatus StatusEnum
{
get
{
return (Enumerations.ValidationStatus)StatusID;
}
set
{
StatusID = (int)value;
}
}
public string StatusText
{
get
{
return MyMethodThatGetsTheEnumDescription(StatusEnum);
}
}
Two Solutions to work around the problem:
User Data Transfer Object(DTO) nd put the enum there. then use Automapper or manually map between the DB Model and the DTO Model (best practice)
Instead of enum you can use extension functions on the model and define your getter, setters and any extra properties you want as extension functions to the class
(will add some complexity to your models)
I have a WebForms applicaiton using the new ASP.NET Identity. I've added a couple of additional fields in the class to allow for Email and a Boolean called IsOnLine.
I use migrations, to explicititly update the tables, and can see that my new fields are there. However, whenever I try to login now i get the following error:
Additional information: The 'IsOnLine' property on 'ApplicationUser' could not be set to a 'null' value. You must set this property to a non-null value of type 'System.Boolean'.
All the exmamples on the net relate to MVC, which i'm not using yet.
How can i fix this?
A bool cannot be null so that is what is set to in the database. If you want it to be null you will need to define it as a nullable bool using the ? operator.
public class ApplicationUser : IdentityUser
{
public bool? IsOnline { get; set; }
}
To update this information in the database take a look at this article on adding email confirmation to ASP.NET Identity. In this example there is a boolean flag IsConfirmed that is updated during the registration process, which will be similar to your IsOnline flag. Here is a snippet of the relevant code to update the database.
user.IsOnline = true;
DbSet<ApplicationUser> dbSet = context.Set<ApplicationUser>();
dbSet.Attach(user);
context.Entry(user).State = EntityState.Modified;
context.SaveChanges();
I prefer to avoid null values whenever possible, to determine if a property can accept nulls values it is better to think what that information represents in the domain problem. In this case i think it not make sense to have an undetermined value for IsOnline because the user is online or offline, not middle terms apply to this fact.
To resolve this problem with the database, taking advantage that you are using Code Migration , you can use the Seed method from Configuration class and assing the value(s) to your new fields that are not nullable.
Pseudo code:
Seed()
for each user
set user IsOnline to false
I don't think I am asking the question correctly, but hopefully you know what I am asking.
What are pros and cons of using a string value to represent a database field (or any variable) vs using an enumeration or constant? I am not asking about the datatype, but hows its handled on the back-end. I'll use LINQ to SQL for an example.
My thinking is that by using an enumerable or constant it's: easier to read, ensures compatibly should the values ever need to be changed, and the value is hard coded -so to speak- so there are less chances of an error caused by a typo. On the flip side, do I really need a class/structure with member enumerations that essentially act as a look up for the value I want?
Using an Constant
Module Trip
Public Const OPEN As String = "Open"
Public Const PENDING_PAYMENT As String = "Pending Payment"
Public Const CANCELLED As String = "Cancelled"
Public Const CLOSED As String = "Closed"
End Module
Dim product = From p In db.Payments
Where p.PaymentId = PaymentId
For Each item In product
item.Status = PayStatus.PENDING_PAYMENT
Next
Using a string
Dim product = From p In db.Payments
Where p.PaymentId = PaymentId
For Each item In product
item.Status = "Pending Payment"
Next
As one of the comments says, the common way to deal with this is using a lookup table in the database. In its most simple form, you would have a class, let's say PaymentStatus:
Class PaymentStatus
Public Property Id As Integer
Public Property Name As String
End Class
and Payment would have reference property like
Public Property PaymentStatus As PaymentStatus
This way, you can always get the options from the database and you will never make a typo in code. It's also much easier to add options or to change descriptions.
For instance, think of what you need to do if you'd decide that "Cancelled" needs to be differentiated into "Cancelled by user" (the old status) and "Cancelled by system" (a new status introduced by new business logic). You'd need a script to update all records in the database to the new string (and change the code, but you'd be changing code anyway). A lookup table allows you to update only one record (and add a new one in this example).
I'd like to build a ParameterCollection object based on the results of either execute sp_columns MyTableName or SELECT * FROM INFORMATION_SCHEMA.Columns WHERE TABLE_NAME = 'MyTableName'.
The problem I'm having is assigning the appropriate data type to each Parameter ... I'm unsure of how to gather that information from either of the above two queries and convert it into a System.Data.DbType or System.TypeCode.
Any help is greatly appreciated.
Links: MSDN: sp_columns, MSDN: Information Schema.Columns
Edit: I guess what I'm looking for is functionality similar to Type.GetType("TypeName") that would accept a SQL data type. For example, DbType.GetType("int") or DbType.GetType("varchar").
Edit: Links from Answer: MSDN: Enum.Parse Method
It's actually really easy to do :-) SqlDbType is an enum, and there's a nice static function on the Enum class which accomplishes just what your looking for:
private SqlDbType ConvertType(string typeName)
{
return (SqlDbType)Enum.Parse(typeof(SqlDbType), typeName, true);
}
With this, you should have no trouble converting the output from your INFORMATION_SCHEMA query into a collection of SqlParameters.