I've been playing with Dapper and I have a question. Is there a way for me to be able to have an enumerator type property in my POCO class and use Dapper? Seems like every time I add a property which is of type Enumerator I get the following exception:
System.NotSupportedException: The type : *my_enum_type* is not
supported by dapper
Am I missing something here? Is there an attribute that I can attach to these properties to specify which of them are mapping to database table columns?
This is an old bug in dapper, ensure you use the latest version. Dapper used to perform no filtering on the input types properties.
Ok I figured this out and it was my fault for not seeing this in the 1st place. This is what I was doing initially:
Dim result = conn.Query("SELECT * FROM Users WHERE UserID = #UserID", New User With {.UserID = userID})
But what needed to be done is:
Dim result = conn.Query("SELECT * FROM Users WHERE UserID = #UserID", New With {.UserID = userID})
In other words declaring param as anonymous type is essential. If you declare param as a specific type and that type contains a property of type which is not covered by dapper code (such as Enum) then the code crashes with the above mentioned error. My User class had an Enum type property and it was causing the problem.
Related
In one of my DB models I have a property with a custom type (Dictionary<string, string>), which is automatically converted to/from JSON with a custom converter, and is stored as a text field in the database. I would like to be able to use MySQL's LIKE comparer to search for a string within this JSON field, but I am getting an exception. I don't care about the JSON's structure, I'm fine with treating it as a simple text field and searching in it that way.
Here's how I try to do it (Sku is the complex type):
var responseSet = database.Products.Where(p => EF.Functions.Like(p.Sku, "%query%"));
And this is the exception I get:
An unhandled exception has occurred while executing the request.
System.InvalidOperationException: The LINQ expression 'DbSet<ProductObject>()
.Where(p => __Functions_0
.Like(
matchExpression: p.Sku, pattern: __Format_1))' could not be translated.
Additional information:
Translation of method 'Microsoft.EntityFrameworkCore.MySqlDbFunctionsExtensions.Like'
failed. If this method can be mapped to your custom function,
see https://go.microsoft.com/fwlink/?linkid=2132413 for more information.
The link in the exception points to a long git issue with tons of cross-references, but I haven't been able to find anything useful in it.
Is there a way I could prevent this error from happening and search within the complex field?
The fundamental problem with the EF Core value converters is that the LINQ query is build against client type, which then is translated behind the scenes to provider type, and there is no standard way to specify provider type conversion inside the query.
However there is a simple trick with casting (presented in some my answers to other conversion related issues like How can a JSON_VALUE be converted to a DateTime with EF Core 2.2?, Expression tree to SQL with EF Core or Comparing strings as dates using EF core 3), which works for most of the EF Core relational database providers.
In this case, you know that the CLR equivalent of the provider type is string, so you can use the following cast
p => EF.Functions.Like((string)(object)p.Sku, "%query%")
The intermediate cast to object is needed to fool C# compiler to accept the actual cast. EF Core translator is smart enough to remove it (as well as others when not needed like here).
This isn't a supported feature yet, but there is a hacky workaround that might work for you. Define a custom database function to explicitly cast the expression to the underlying db type.
public static string Cast(YourComplexType t) => throw new NotSupportedException();
modelBuilder.HasDbFunction(() => Cast(default))
.HasTranslation(args =>
{
var a = args.First();
return new SqlUnaryExpression(
ExpressionType.Convert,
a,
typeof(string),
new StringTypeMapping(a.TypeMapping.StoreType, DbType.String));
});
EF.Functions.Like(Cast(p.Sku), "%query%")
Unfortunately you can't define generic db functions. The arguments all have to be types the provider can store, which rules out object or an interface. So you may have to define overloaded methods for every possible type.
Problem:
Could I perform the left join and verify the nulled element without
terminating the DB link? If yes, how do I do it?
Trial Code
var transactions1 = (from tran in ctx.transactions
group tran.Amount by new { tran.UserID, tran.LeaveID } into leave
select new { UserID = leave.Key.UserID, LeaveID = leave.Key.LeaveID, Balance = leave.Sum() });
var transactions2 = (from tran in ctx.transactions
where tran.Type == type && tran.FiscalYear == fiscal
group tran.Amount by new { tran.UserID, tran.LeaveID } into leave
select new { UserID = leave.Key.UserID, LeaveID = leave.Key.LeaveID, Rollout = leave.Sum() });
var computed = (from balance in transactions1
join join_rollout in transactions2 on new { balance.UserID, balance.LeaveID } equals new { join_rollout.UserID, join_rollout.LeaveID } into rolls
from rollout in rolls.DefaultIfEmpty()
select new
{
UserID = balance.UserID,
LeaveID = balance.LeaveID,
computed = rollout.Rollout
}).ToList();
Goal:
I am trying to left join two tables using linq. As one could guess, some values of the second table could result in null. If I use a ternary operator to verify nulled values the application throws the following exception
The argument to DbIsNullExpression must refer to a primitive, enumeration or reference type.
Find the stack trace at PastBin
If I remove the ternary verification, the application throws the following exception
The cast to value type 'System.Decimal' failed because the materialized value is null. Either the result type's generic parameter or the query must use a nullable type.
Find the stack trace at PasteBin
Working solution:
I am able to avoid these exceptions by terminating the DB link (using .ToList() as suggested by slawek) before the final join (that is, transactions1.ToList() and transactions2.ToList(). However I wish to avoid using .ToList() because I need to perform another join with a DB table which cannot be done on a List.
My Experiments Since the SO post
I've tried using UseDatabaseNullSemantics which I did not expect to resolve it but atleast aid in a workaround, but the result is Failed
The error message that you quoted:
The cast to value type 'System.Decimal' failed because the
materialized value is null. Either the result type's generic parameter
or the query must use a nullable type.
indicates that you are attempting to assing the value of null to a field of System.Decimal type. This is illegal in .Net. System.Decimal is a value type. Unlike a reference type these cannot be null. A good treatment on reference type vs value type can be found here.
When .NET first came out that basically was it. Later on, it became obvious that this can be very inconvenient, so .net designers tucked on a new feature called nullable types. These allow getting around this limitation above. In c# you refer to a nullable type, by specifying a value type and then adding a question mark to it, like this: System.Decimal? or simply decimal?. A field of decimal? type will be able to hold null.
You need to tell the compiler the type you are using, it can't figure it out correctly on its own. Change this line of your code:
computed = rollout.Rollout
to read like this:
computed = (decimal?)rollout.Rollout
This will indicated that null is a valid value, and your code will work.
In general, it helps taking some time to learning the basics, before diving into more complicated stuff like ORM usage. If you do not know the basics, when something breaks it's very difficult to identify what broke - since you don't have all the pieces of the puzzle, but once you know them, often the answer is obvious.
I have setup EF code first against my DB (specifically, a table called tblExpenseMain).
In my controller I'm trying to pass an instance of my 'ExpenseMain' class over to the view so the details can be presented. Manually creating an instance and passing it works:
ExpenseMain em = new ExpenseMain { Card_Number = "123", UniqueID_ERLineID = "blah", Item_Amount = 500, Item_Expense_Type = "something"};
return View("_Basic");
But trying to query it with LINQ like so gives me an exception:
ExpenseMain model = (from e in db.ExpenseMains
where e.UniqueID_ERLineID == transUniqueID
select e).SingleOrDefault();
It tells me to check the innerexception to find the problem. I surrounded it with a try/catch and the innerexception message was:
e.InnerException.Message = "Invalid object name 'dbo.ExpenseMains'."
Any suggestions on what I'm doing wrong? I was hoping that the linq query would just grab my single instance and go from there. Changing 'Expensemain model' to 'var model' and looking at what the var becomes, confirms it becomes an 'Expensemain' anyway.
Thanks
If your class name is different from the table name, then you have to configure EF to map them. You can use the table attribute and give the table name.
[Table("tblExpenseMain")]
public class ExpenseMain
{
//properties
}
Do you have several data access layers in your sulotion? Could it be that ExpenseMain is an old Entity, not generated from EF. Double check that there are no tblExpenseMain entity aswell or similar.
Hope this helps.
Looks like u have 2 classes here: ExpenseMain and ExpenseMains
Thought I read somewhere that you in fact can query your conceptual model with Entity Sql, using string based queries as such:
class DBSetTest<T> : List<T>
{
DBEntities db = new DBEntities();
public DBSetTest()
{
ObjectQuery<T> test = new ObjectQuery<T>("SELECT Name FROM Sessions WHERE Name = 'sean'", db);
foreach (var v in test)
{
this.Add(v);
}
}
}
Where 'Sessions' is a custom Entity Type I defined with a 'DefiningQuery'. I would otherwise query it with normal linq syntax. Does Entity SQL only query the store or can it query my conceptual model just like LINQ-to-Entities does? If so not sure I have the right syntax, since it probably isn't sql syntax. My goal is to create a sort of custom generic list here, where I can write dynamic queries against my conceptual model.
I get the following error:
'Name' could not be resolved in the current scope or context. Make sure that all referenced variables are in scope, that required schemas are loaded, and that namespaces are referenced correctly. Near simple identifier, line 1, column 43.
I think, It is not valid Entity SQL sintax,
probably you must add some keyword like this :
SELECT VALUE s.Name FROM your_ObjectContext_name.Sessions AS s WHERE s.Name = 'sean'
The error you get says that you must put it. before Name, like this:
"SELECT it.Name FROM Sessions WHERE it.Name = 'sean'"
I'm using a custom named query with NHibernate which I want to return a collection of Person objects. The Person object is not mapped with an NHibernate mapping which means I'm getting the following exception:
System.Collections.Generic.KeyNotFoundException:
The given key was not present in the
dictionary.
It's getting thrown when the Session gets created because it can't find the class name when it calls NHibernate.Cfg.Mappings.GetClass(String className). This is all fairly understandable but I was wondering if there was any way to tell NHibernate to use the class even though I haven't got a mapping for it?
Why don't you use:
query.SetResultTransformer(Transformers.AliasToBean(typeof(Person)));
It will insert data from each column in your query into Person object properties using column alias as a property name.
How can you create a query which would return instances of a type that is not mapped ?
I think Michal has a point here, and maybe you should have a look at projections. (At least, this is what I think you're looking for).
You create a query on some mapped type, and then, you can 'project' that query to a 'DTO'.
In order to do this, you'll have to 'import' your Person class, so that it is known to NHibernate, and you'll have to use a ResultTransformer.
Something like this:
ICriteria crit = session.CreateCriteria (typeof(Person));
// set some filter criteria
crit.SetProjection (Projections.ProjectionList()
.Add (Property("Name"), "Name")
.Add (Property( ... )
);
crit.SetResultTransformer(Transformers.AliasToBean(typeof(PersonView));
return crit.List<PersonView>();
But, this still means you'll have to import the class, so that NHibernate knows about it.
By using the class, NHibernate would basically be guessing about everything involved including which table you meant to use for Person, and the field mappings. NHibernate could probably be hacked to do dynamic binding based on matching the names or something, but the whole idea is to create the mappings from plain old data object to the database fields using the xml files.
If there's not a really good reason not to map the class, simply adding the mapping will give you the best results...
That said, you can't use a named query to directly inject results into an unmapped class. You would need to tell it which columns to put into which fields or in other words, a mapping. ;) However, you can return scalar values from a named query and you could take those object arrays and build your collection manually.
To solve this, I ended up using the TupleToPropertyResultTransformer and providing the list of property values. There are a few limitations to this, the main one being that the SQL query must return the results in the same order as you provide your properties to the TupleToPropertyResultTransformer constructor.
Also the property types are inferred so you need to be careful with decimal columns returning only integer values etc. Apart from that using the TupleToPropertyResultTransformer provided a reasonably easy way to use an SQL query to return a collection of objects without explicitly mapping the objects within NHibernate.