How to convert this Lambda Expression in NHibernate Criteria - c#

I have this class
public class MyClass
{
public int Id { get; set;}
public string Name { get; set; }
public IList<MyClass> Classes { get; set; }
}
I am using Fluent NHibernate, and I have this AutoMappingOverride:
public void Override(AutoMapping<MyClass> mapping)
{
mapping.HasManyToMany(m => m.Classes)
.ParentKeyColumn("ClassId")
.ChildKeyColumn("SecondClassId")
.Table("ClassesRelation")
.Cascade.SaveUpdate();
}
And I have this method on my Repository:
public IList<MyClass> ClassesThatContains(MyClass #class)
{
//allClasses.Where(tempClass => tempClass.Classes.Contains(#class))
string query = string.Format(#"SELECT Class.* FROM Class WHERE Class.Id IN (SELECT ClassesRelation.ClassId FROM ClassesRelation WHERE ClassesRelation.SecondClassId = {0})", #class.Id);
var criteria = NhSession.CreateSQLQuery(query).AddEntity(typeof(MyClass));
return criteria.List<MyClass>();
}
In the method ClassesThatContains I have the comment that is equivalent to SQL Statement, but NHibernate dont know Contains Linq method....So I used SQL Statement, but how can I convert this SQL Statement to Criteria like?
Obs: ClassRelation is not a Class.

Nhibernate has a linq provider in version 3 built in and as an addon in version 2.
You could just change the method like so::
from c in session.Query<MyClass>()
from cr in session.Query<ClassRelation>()
where c.FirstId == cr.Id
where cr.SecondId = #class.Id
select c.Id;
Or something more closer to your query you actually want.

Related

EF Core cant translate simple where linq expression

we recently upgraded to use ef core 2.1.8, and now some of our queries are failing.
Something as simple as a where clause causes ef to evaluate it locally.
I have a language table, with a name column, i wanna select all where name is not null, but neither of these two works:
var searchQuery = from q in context.Language
where (q.Name != null) select q;
var searchQuery = context.Language.Where(x => x.Name != null);
Both are being evaluated locally. Name is a simple string / nvarchar column.
Edit:
Language is a partial class that looks like this:
public partial class Language
{
public Language()
{
Segmentation = new HashSet<Segmentation>();
SystemTranslation = new HashSet<SystemTranslation>();
}
public int Id { get; set; }
[Required]
[MaxLength(255)]
public string Culture { get; set; }
public ICollection<Segmentation> Segmentation { get; set; }
public ICollection<SystemTranslation> SystemTranslation { get; set; }
}
public partial class Language : IIdentifiable<int>, ISearchable
{
public string Name => CultureInfo.GetCultureInfo(Culture).DisplayName;
}
With the following configuration:
void IEntityTypeConfiguration<EF.Entity.Core.Language>.Configure(EntityTypeBuilder<EF.Entity.Core.Language> builder)
{
builder.HasIndex(e => e.Culture)
.HasName("IX_Culture")
.IsUnique();
}
It turns out i was trying to query on a calculated property, which of course means that ef was unable to use it server side, as no column by that name existed.

Execute Linq to SQL queries on Navigation properties in Model

Is it possible to make Statistics.Sum(s => s.Conversions) linq query as Linq to SQL and not Linq to Object like in this code below. Every time when I access TotalConversions property, the whole Statistics table downloaded from database and then SUM linq executed locally. I want to do that in database server as SQL.
public class User : Entity
{
public int Id { get; set; }
public string Email { get; set; }
public virtual ICollection<Statistic> Statistics { get; set; }
[NotMapped]
public int TotalConversions
{
get
{
return Statistics.Sum(s => s.Conversions);
}
}
}
Yes, but you need a reference to the DbContext. This is one of the costs of having the Entities be persistence-ignorant.
Then the property would look something like:
return db.Users.Single(s => s.Id = this.Id).Statistics.Sum(s => s.Conversions);

Dynamically set the table name in LINQ query

I am working on data warehouse application and we have 4 tables where schema is identical. Only difference between those tables is just Table Name.
Table Example:
ps_Contractor
ps_Employee
ps_Union
ps_NonUnion
Schema
id
hourly
benefit
total
Now i need to generate 4 reports based on these tables. Instead of writing 4 separate LINQ queries i would like to write single query where i can pass the table name dynamically.
The question How do i pass the table name dynamically in following LINQ query ?
var data = ( from q in _dbcontext.ps_Contractor
join _l in _dbcontext.log on q.id equals l.tablelogid
where q.hourly = 8
select new{
hourly=q.hourly,
benefit=q.benefit,
total=q.total,
log = l.message
}.ToList();
I have looked at all similar questions suggested by stack overflow. I do not want to use ExecuteStoreQuery.
what options do i have ?
If all the tables have the same columns, then I'd extract an interface out of those tables and create partial entity classes just to implement that interface, finally use that interface to query.
For example:
//entities
public partial class ps_Contractor: ICommonInterface{}
public partial class Table2 : ICommonInterface{}
in the search method I'd pass IEnumerable<ICommonInterface> or IQueryable<ICommonInterface> and apply that query on that. All you'd need to do is to pass different tables to that search method.
Or you can even have kind of generic class of type ICommonInterface and use that to do the query.
public void Example(IQueryable<ICommonInterface>dataSource)
{
var data = ( from q in dataSource
join _l in _dbcontext.log on q.id equals l.tablelogid
where q.hourly = 8
select new{
hourly=q.hourly,
benefit=q.benefit,
total=q.total,
log = l.message
}.ToList();
}
Example(_dbcontext.ps_Contractor.AsQueryable())
This is just a sample that I tested now:
public class Repository
{
private List<string> GetData(IQueryable<IContractor> data)
{
return (from d in data select d.Name).ToList();
}
public List<string> GetFullTime()
{
using (var context = new TestDbEntities())
{
return GetData(context.FTContractors.AsQueryable());
}
}
public List<string> GetPartTime()
{
using (var context = new TestDbEntities())
{
return GetData(context.PTContractors.AsQueryable());
}
}
}
Entities:
public interface IContractor
{
int Id { get; set; }
string Name { get; set; }
}
public partial class FTContractor : IContractor
{
public int Id { get; set; }
public string Name { get; set; }
}
public partial class PTContractor : IContractor
{
public int Id { get; set; }
public string Name { get; set; }
}
Test:
[TestMethod]
public void Temp()
{
var tester = new Repository();
var ft = tester.GetFullTime();
var pt = tester.GetPartTime();
Assert.AreEqual(3, ft.Count);
Assert.AreEqual(4, pt.Count);
}
In the database there are two tables containing just Id and Name columns
EF Core no longer has a non generic .set method but This extension class makes it easy to query your table based on a string using dynamic Linq
public static class DbContextExtensions
{
public static IQueryable<Object> Set(this DbContext _context, Type t)
{
return (IQueryable<Object>)_context.GetType().GetMethod("Set").MakeGenericMethod(t).Invoke(_context, null);
}
public static IQueryable<Object> Set(this DbContext _context, String table)
{
Type TableType = _context.GetType().Assembly.GetExportedTypes().FirstOrDefault(t => t.Name == table);
IQueryable<Object> ObjectContext = _context.Set(TableTypeDictionary[table]);
return ObjectContext;
}
}
}
usage:
IQueryable<Object> query = db.Set("TableName");
// Filter against "query" variable below...
List<Object> result = query.ToList();
// or use further dynamic Linq
IQueryable<Object> query = db.Set("TableName").Where("t => t.TableFilter == \"MyFilter\"");
Here's a way to do a dynamic function that accepts a DbSet<T> (type of database class that you want to pass as a parameter) and a specific expression to build a query on that table:
private IQueryable<T> BuildQueriedCollection<T>(Expression<Func<T, bool>> exp, DbSet<T> dbTable) where T : class
{
var appliedQueryCollection = dbTable.AsExpandable().Where(exp);
return appliedQueryCollection;
}
and you could call the function like so:
Expression<Func<MyClass, bool>> myExp = myList => myList... // some condition...;
var dbset = dbContext.MyTable;
var query = BuildQueriedCollection(myExp, dbset);

join diffrent field type in linq

join diffrent field type in linq
public partial class Product
{
public int ID { get; set; }
public string CategoryID
{
get { return Myclass.increse(CategoryID); }
set { CategoryID = value; }
}
public string Name { get; set; }
}
public partial class ProductCategory
{
public int ID { get; set; }
public string Name { get; set; }
}
var query = (from c in dContext.ProductCategories
join p in dContext.Products
on Myclass.EncodeMD5(c.ID.ToString()) equals p.CategoryID
select new { id = p.ID, cat = p.CategoryID, name = p.Name, cat1 = c.Name }
).ToList();
The field should be converted to string
Then function runs EncodeMD5
error:
LINQ to Entities does not recognize the method 'System.String
EncodeMD5(System.String)' method, and this method cannot be translated
into a store expression.
You cannot call arbitrary .NET methods in LINQ-to-(some database backend) - the entire point of EF (etc) is that it wants to create SQL from your expression - something involving a where clause. It can work with simple properties and operators, and a few methods it knows about and can map into SQL, but it can't perform something it has never heard of (increse, EncodeMD5, etc) how would it know what SQL to write?
With something like MD5, your best bet would be to store the MD5 hash in the underlying table along with the ID. Likewise with the CategoryID's "increse" (whatever that is). So your query would end up working off these pre-calculated values:
on c.IDHash equals p.CategoryIDHash

Fill property of DTO with SubQuery in NHibernate Query

I have a DTO object like this:
public class TreeViewDTO
{
public string Value { get; set; }
public string Text { get; set; }
public bool HasChildren { get; set; }
}
and my entity mapped with Nhibernate is:
public class Entity
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual Entity Parent { get; set; }
/* other properties */
}
I would like to know, how can I get a List of my DTOs and fill the HasChildren property using a count method or a subquery to know if there are childrens?
I have tried this, but does not work:
return Session.QueryOver<Entity>
.Select(entity => new TreeViewViewModel() {
Value = entity.Id.ToString(),
Text = entity.Name,
HasChildren = (Session.QueryOver<Entity>().Where(x => x.ParentId == entity.Id).RowCount() > 0)})
.ToList();
I got an exception with this: NotSupportedException and the messages says: x => (x.Parent.Id == [100001].Id) and it is not supported.
How could I create a query to fill this property?
PS: I would like to have a query to select only the Id, Name and Count... because my entity can have 30 fields or more...
Thank you.
Using the NHibernate Linq provider then you can do this:-
public class dto
{
public long Value { get; set; }
public int Count { get; set; }
public bool HasCount { get { return Count > 0; } }
}
Note: my DTO has a read-only property that looks at the actual count, the query is then:-
var a = Db.Session.Query<Support>().Select(
s => new dto {
Value = s.Id,
Count = s.CommentList.Count
}
).ToList();
This generates the following sQL
select support0_.Id as col_0_0_,
(select cast(count(*) as SIGNED)
from supportcomment commentlis1_
where support0_.Id = commentlis1_.SupportId) as col_1_0_
from support support0_
I have never seen a working example of this using QueryOver. I have had had a stab at it but couldn't get it working..
Didn't you consider the option of using something else rather than NHibernate for this job?
In my opinion, lightweight library like Dapper can be a brilliant solution for this use case. You'll end up with a resonably simple sql query instead of jiggling with Nhibernate.
Edit:
dapper code will be as simple as this:
public IDbConnection ConnectionCreate()
{
IDbConnection dbConnection = new SQLiteConnection("Data Source=:memory:;pooling = true;");
dbConnection.Open();
return dbConnection;
}
public void Select()
{
using (IDbConnection dbConnection = ConnectionCreate())
{
var query = #"SELECT e1.id as Value, e1.name as Text, CASE WHEN EXISTS
(SELECT TOP 1 1 FROM Entity e2 WHERE e2.parent = e1.id)
THEN 1 ELSE 0 END as HasChildren
FROM Entity e1";
var productDto = dbConnection.Query<TreeViewDTO>(query);
}
}

Categories