How do I use my enumeration in a LinqToSQL query? - c#

I have a field in my database table that use to store an enumeration value, e.g.:
create table MyTable (
...
Status tinyint not null,
...
)
and in my C# class I have
public enum TStatus : byte {
Pending = 1
Active = 2,
Inactive = 3,
}
public TStatus MyStatus {
get { return (TStatus)Status; }
set { Status = (byte)value; }
}
now I want to write a Linq query that uses the MyStatus property of MyTable e.g.
var q = MyDataContext.GetTable<MyTable>().Where(t => t.MyStatus == TStatus.Active);
but of course, Linq doesn't know how to interpret MyStatus as SQL.
What do I need to do to MyStatus in order for it to work in LinqToSQL?

Check out this link:
http://dotnet.org.za/hiltong/archive/2008/08/06/using-enums-with-linq-to-sql.aspx
As links die - and at least for me this one did die - here is the important part:
[When adding the column to the entity] by default, the Type will come up as an "int (System.Int32)", but you can change it to the fully-qualified type of the enum (in my case, ConsoleApplication1.CustomerType). BUT, in order to locate it fully, you have to add the global identifier, as follows: global::ConsoleApplication1.CustomerType , so type that as is (but the equivalent for your namespace) into the textbox

Don't have a compiler handy, but I think if you cast your enum to an int, it will work.
So try:
var q = MyDataContext.GetTable().Where(t => t.MyStatus == (int)TStatus.Active);

Related

Is there any way of checking if an enum has an object value defined in it?

Here is the enum that I have defined:
enum LogicalChange
{
List = SyntaxKind.List,
TildeToken = SyntaxKind.TildeToken,
ExclamationToken = SyntaxKind.ExclamationToken,
DollarToken = SyntaxKind.DollarToken,
PercentToken = SyntaxKind.PercentToken,
CaretToken = SyntaxKind.CaretToken,
AmpersandToken = SyntaxKind.AmpersandToken,
AsteriskToken = SyntaxKind.AsteriskToken,
MinusToken = SyntaxKind.MinusToken,
PlusToken = SyntaxKind.PlusToken,
EqualsToken = SyntaxKind.EqualsToken
}
I have a set of commands that should execute only if change.After.Parent.Kind() (which returns a SyntaxKind) is defined in the enum LogicalChange.
What I have tried so far is -
Enum.IsDefined(typeof(LogicalChange), change.After.Parent.Kind())
but this generates an exception. I don't want to do string comparison. Is there any other way of achieving this?
It is not a simple name or string comparison, you need to cast it to the Enum Type you are comparing it to. This should not trigger an exception:
if (Enum.IsDefined(typeof(LogicalChange), (LogicalChange)change.After.Parent.Kind()))
{
}
IsDefined method allows to you to send three type as value:
own enum type
int
string
so you can use these ways:
1. Enum.IsDefined(typeof(LogicalChange), (LogicalChange)change.After.Parent.Kind())
2. Enum.IsDefined(typeof(LogicalChange), (int)change.After.Parent.Kind())
3. Enum.IsDefined(typeof(LogicalChange), change.After.Parent.Kind().ToString())
note: way no 3 is correct for you because you choose same name in both Enums, But it's better to not use it.

Store Enum as Char with Dapper

I have an enum like:
public enum AccountStatus : byte
{
Locked = (byte)'L',
Active = (byte)'A',
Deleted = (byte)'D'
}
and would like to to store the char values in the database.
Is there a way in Dapper with type maps or otherwise to define which enum types should be mapped to their char values (for read/update/insert)?
The anonymous type member declarator is also preventing me from casting the property to a char directly in the query:
As mentioned by #TyCobb in the comments, this isn't an issue with storing an Enum as a char, but rather with how anonymous type properties are declared.
Anonymous types can only infer and generate names for "simple property references" where no transformation is done to the property during assignment, such as
new { account.AccountStatus }
As soon as you transform the data or need to rename the property, you must explicitly declare the property name:
new { Status = (char)account.AccountStatus }
In regards to reads, you can map to a char via dapper, then cast as your enum in your select (or transform the data in whatever way is most appropriate):
var result = connection.Query<char>(
"select char column from table where Status = #Status",
new {Status = (char)account.AccountStatus}
).Select(x => (AccountStatus)x).FirstOrDefault();
In addition, Dapper also handles AccountStatus : byte cases natively, so you may be able to directly return the value as your enum:
var result = connection.Query<AccountStatus>(
"select char column from table where Status = #Status",
new {Status = (char)account.AccountStatus}
).FirstOrDefault();

The cast to value type 'Boolean' failed because the materialized value is null

public ActionResult Votation(int id=0)
{
var events = db.Events_Info_tbl.Where(x => x.is_active == true).FirstOrDefault();
//query the first category
List<CampaignManager_tbl> candidates = new List<CampaignManager_tbl>();
candidates = (from cat in db.Events_Category_tbl
join can in db.Candidates_Info_tbl
on cat.events_category_id equals can.events_category_id
where cat.events_info_id == events.events_info_id
select new CampaignManager_tbl {
events_category_name = cat.events_category_name,
candidates_fullname = can.candidates_fullname,
candidates_info_id = can.candidates_info_id,
vote_no = cat.vote_no.Value,
isSelected = can.isSelected.Value,
events_category_id = cat.events_category_id
}).ToList();
return View(candidates);
}
This code was working before but now I've got this error: The cast to value type 'Boolean' failed because the materialized value is null. Either the result type's generic parameter or the query must use a nullable type.
What's wrong with this? I didn't change any of my codes before. It just this time I've got an error.
I ran into this error because of a different problem. In my query I am selecting specific columns from an account member and its associated account:
DBContext.Set<AccountMember>()
.Include(am => am.Account)
.Select(am => new
{
am.ID,
am.IsPrimary,
Account = new { am.Account.ID, am.Account.DoNotEmail }
});
Both of my boolean properties, IsPrimary and DoNotEmail are non-nullable so the resulting property type of their properties in the anonymous type is bool. The exception occurs when an AccountMember does not have an Account. Since am.Account is null, the expression am.Account.DoNotEmail returns null. But the anonymous type's DoNotEmail property inferred it was of type bool which cannot be set to null. The solution is to give it a hint that this should be a nullable bool by casting the property to a bool?:
DBContext.Set<AccountMember>()
.Include(am => am.Account)
.Select(am => new
{
am.ID,
am.IsPrimary,
Account = new { am.Account.ID, DoNotEmail = (bool?)am.Account.DoNotEmail }
});
Hapily EF is smart enough to ignore this cast and not blow up trying to convert it to SQL.
I'm going out on a limb here, but I'd guess it has to do with this line:
isSelected = can.isSelected.Value,
If when the two tables are joined, one of them does not return a value for that particular field, it will be null. It appears the type here is a nullable of some type - probably bool?, based on the naming - and that would match your error.
Try to replace it with the following:
isSelected = can.isSelected.HasValue ? can.isSelected.Value : false,
Of course, you can replace the default false here with true, if that makes more sense in your case.
Edit: Note that you should probably do the same for the other column you use in a similar way (I'm assuming this is returning an int?):
vote_no = cat.vote_no.HasValue ? cat.vote_no.Value : 0
Is db.Candidates_Info_tbl.IsSelected a bit field?
If so, the query could likely result in
isSelected = can.isSelected.Value,
with isSelected being null, whereas you probably declared isSelected as bool in CampaignManager_tbl class.
Change the isSelected to bool in the CampaignManager_tbl class and try again.
if your field type is int while some value is null, then you can use case-when and convert to bit like below
SELECT CASE WHEN FieldName IS NOT NULL THEN CONVERT(BIT,FieldName) ELSE CONVERT(BIT,0) END AS NewFieldName

How to Assign a Query Result to an Integer Array

public List<Workflow> GetMyWorkflows(int[] MyRoles)
{
int[] myWorkflowIDs = new int[] { };
RapidWorkflowDataContext context = new RapidWorkflowDataContext();
var query = from w in context.WorkflowRoles
where MyRoles.Contains((int)w.RoleID)
select w.WorkflowID;
var distinctWorkflows = query.Distinct();
myWorkflowIDs = distinctWorkflows.toArray();
return myWorkflowIDs;
}
In this method I want to retrieve an array of workflows that a user can
access.
I get the following error : Cannot implicitly convert type 'int?[]' to 'int[]'
I want to retrieve an array of workflows
But your method must return a List<Workflow> or a List<int>.
So you should skip the array idea. The other issue is between int and int?. You can solve that in the select clause with select w.WorkflowID.Value or select w.WorkflowID ?? 0. Or simply select w for a List<Workflow>.
Also it is a good idea to dispose a context when it becomes unreachable.
public List<int> GetMyWorkflows(int[] MyRoles)
{
using (RapidWorkflowDataContext context = new RapidWorkflowDataContext())
{
var query = from w in context.WorkflowRoles
where MyRoles.Contains((int)w.RoleID)
select w.WorkflowID ?? 0;
// select w; to return a List<WorkFlow>
var distinctWorkflows = query.Distinct();
return distinctWorkflows.ToList(); // ToList because we are closing the Context
}
}
I'm going to guess that WorkflowID is of type int?. If you are certain that it cannot be null, change your central query to:
var query = from w in context.WorkflowRoles
where MyRoles.Contains((int)w.RoleID)
select w.WorkflowID.Value;
This will ensure that query is now of type IEnumerable<int> instead of IEnumerable<int?>, with the int following on throuhh the Distinct() and ToArray() functions.
This seems like a pretty good error to me
Cannot convert type 'int?[]' to 'int[]'
You must have an array of type int? and be trying to implicitly convert it to int.
Therefore you have two options - stop trying to implicitly convert, and allow the result to be int?[], like this:
int?[] myWorkflowIDs = new int?[] { };
or force the convert to take place, like this:
RapidWorkflowDataContext context = new RapidWorkflowDataContext();
var query = from w in context.WorkflowRoles
where MyRoles.Contains((int)w.RoleID)
select (int)w.WorkflowID;
// or w.WorkflowID ?? 0; as necessary
So int? can also be written Nullable<int> which is basically an int that can take null values. For example:
int? nullableNumber = 5; // Set to a value
nullableNumber = null? // Set to null (which is possible because int? is nullable).
As you can imagine, Nullable<int> is useful for databases because sometimes you might have a column that has null values, and so this type gives a useful means of mapping to this sort of value. The problem, though is that in your code you have to deal with two different types, int vs. int?. You can cast between the two values by using:
// If the nullable-integer is not-null then use it's value, else default to `0`.
int nonNullable = nullableNumber ?? 0;
which will replace nulls with 0 if the value is null. Or you can just store your myWorkflowIDs in a nullable value (Nullable<int>[] or int?[]), which semantically better reflects what the column value in the database actually is.

Pros and Cons of using "as", "is", "DBNull", ToString() in C#

When reading data from ExecuteReader. It returns me data in IDataReader. When I want to fill this data into my entities I will be assigning Non Nullable values in most of the cases to either Nullable types or Non Nullable types, e.g.:
int id;
string name;
int? age;
byte[] image;
example e = new example();
e.id = (int)dr["id"]; //unbox
e.name = dr["name"].ToString() //will result in empty string if dr["name"] is DBNull
e.name = dr["name"] is DBNull ? null : dr["name"].ToString();
e.age = dr["age"] as int?; // way 1
e.age = dr["age"] is DBNull ? null : (int?)dr["age"]; //way 2
e.image = dr["image"] as byte?;
EDIT
if id is primary key for the table and is NonNullable. But it is being used in another table where this key could be NULL then what should be the approach. Should that entity become NULL or exception should be thrown.
If the object reference is null, the is operator always returns false because there is no object available to check its type.
if (o is Employee) {
Employee e = (Employee) o;
// Use e within the ‘if’ statement.
}
The as operator works just like casting except the as operator will never throw an exception. Instead, if the object can’t be cast, the result is null.
Employee e = o as Employee;
if (e != null) {
// Use e within the ‘if’ statement.
}
Check more : C# is and as operators
If you're sure in result type and it can't be null:
(int)dr["id"]
If result can be null and you know the type:
dr["age"] as int?;
dr["age"] as int? ?? -1; // default value
If result can't be null and you don't know the type:
Convert.ToInt32(dr["age"]);
If result can be null and you don't know the type:
object age = dr["age"]; // can be short, int, etc
!Convert.IsDBNull(age) ? Convert.ToInt32(age) : -1;
My assignments would look as such:
e.id = (int)dr["id"]; //unbox
e.name = dr["name"] as string; // String if string, null if DbNull
e.age = dr["age"] as int?;
e.image = dr["image"] as byte?;
The as operator works best for reference types (such as strings or nullables), because it will return null should the object I'm casting be something else, like a DbNull. This is exactly the same as doing the check manually, but more terse and easy to understand.
Look at the answers to this question for some good examples of generic helper functions for your problem.
In (comments) you asked for more info on my dapper comment; the point I was trying to make is that this is essentially a solved problem, and there are a range of tools to help here, from the complex and feature-rich ORMs (NHibernate, Entity Framework, LLBLGenPro), through middle-grounds (LINQ-to-SQL etc), through to stupidly simple (but very effective) micro-ORMs (dapper-dot-net, Peta.Poco, Simple.Data).
dapper-dot-net is the (freely available, OSS) micro-ORM that underpins stackoverflow/stackexchange. It is based on the ridiculously simple approach of mapping directly from returned column names to member names (fields or properties), such that the usage is simply:
var connection = ... // an open connection
int id = ... // a record to fetch
var singleItem = connection.Query<example>(
"select * from example where id = #id", new {id}).Single();
or
int customerId = ...
var orders = connection.Query<Order>(
"select * from Orders where Status = 'Open' and CustomerId = #customerId",
new {customerId}).ToList();
where example is the type in your code, with members name, id, etc. It takes care of all the heavy lifting:
mapping inputs to parameters (see #id and #customerId, and how their values are provided)
materializing returned records into objects in a very optimised way
a few other tricks like horizontal multi-mapping, multi-grid mapping, automatic IN handling, etc
this means that you don't have to worry about details like null, DBNull, Nullable<T> etc, since dapper already handles all that for you, and is (thanks to some IL voodoo) just as fast at runtime as writing the materialization code yourself.

Categories