NHLambdaExtensions: Create a Criterion object to add to ICriteria later - criteria

My application creates a dynamically generated query at runtime based on user input by creating Criterion objects e.g:
ICriterion criterion = Restrictions.Eq("Name", "John");
......
detachedCriteriaSomewhereElse.Add(criterion);
How do I do this in NHLambdaExtensions?
what I really need to do is
ICriterion criterion = Restrictions.Eq<Person>(p=> p.Name == "John");
but this isn't valid. Is there any way to do this?

With the NHLambdaExtensions you have the SQLExpression class that lets you do the following:
ICriterion criterion = SqlExpression.CriterionFor<Person>(p => p.Name == "John");

Related

Is it possible to combine multiple IQueryable into a single one without using Union?

Good evening. Is it possible to combine multiple IQueryable into a single one without using Union in EF Core 2? The main problem is that it doesn't allow to use methods like .Union(), .Except() etc.
It's important that IQueryable should be executed as a single query.
This is what I want to get: toCreateSubquery.Union(toDeleteSubquery)
The queries I want to combine are listed below.
var toCreateSubquery =
validLinks
.Where(
rl => !existingLinks.Any(
el => el.Contract == rl.Contract && el.Estate == rl.Estate)) // Except() doesn't work'
.Select(x => new {x.Contract, x.Estate, Type = ActionType.Create});
var toDeleteSubquery =
existingLinks
.Where(el => !validLinks.Any(rl => el.Contract == rl.Contract && el.Estate == rl.Estate))
.Select(x => new {x.Contract, x.Estate, Type = ActionType.Delete});
This is the visualization of the problem I'm solving.
I want to get the union of these sets without intersection and be able to distinguish belonging to one of these sets.
Just in case, I attach the code of getting these sets:
var validLinks = from ac in _context.AreaContracts
from e in _context.Estate
where (from al in e.AreaLinks select al.Area.Id).Contains(ac.Area.Id) ||
e.Geometry.Intersects(
ac.Area.Geometry)
select new { Contract = ac.Contract.Id, Estate = e.Id };
var existingLinks = from sd in _context.SquareDistributions
select new { Contract = sd.Contract.Id, Estate = sd.Estate.Id };
Thank you for your attention.

How to obtain a subset of records within a context using EntityFramework?

A newbie question. I am using EntityFramework 4.0. The backend database has a function that will return a subset of records based on time.
Example of working code is:
var query = from rx in context.GetRxByDate(tencounter,groupid)
select rx;
var result = context.CreateDetachedCopy(query.ToList());
return result;
I need to verify that a record does not exist in the database before inserting a new record. Before performing the "Any" filter, I would like to populate the context.Rxes with a subset of the larger backend database using the above "GetRxByDate()" function.
I do not know how to populate "Rxes" before performing any further filtering since Rxes is defined as
IQueryable<Rx> Rxes
and does not allow "Rxes =.. ". Here is what I have so far:
using (var context = new EnityFramework())
{
if (!context.Rxes.Any(c => c.Cform == rx.Cform ))
{
// Insert new record
Rx r = new Rx();
r.Trx = realtime;
context.Add(r);
context.SaveChanges();
}
}
I am fully prepared to kick myself since I am sure the answer is simple.
All help is appreciated. Thanks.
Edit:
If I do it this way, "Any" seems to return the opposite results of what is expected:
var g = context.GetRxByDate(tencounter, groupid).ToList();
if( g.Any(c => c.Cform == rx.Cform ) {....}

Adding Data to a very nested child object using EntityFramework

I am trying to add a note to my event object. I am getting an error using this code
Note noteToAdd = new Note { State = State.Added, NoteText = note };
Patient patient = context.Patients.Find(patientId);
patient.State = State.Modified;
patient.MobilePatient.State = State.Modified;
patient.MobilePatient.MCalmEvents.Find(e => e.Id == eventid).Note = noteToAdd;
context.ApplyStateChanges();
Is there a better way to do it using Linq To Entity?
The error that I am having is :
{"Invalid column name 'Note_Id'."}
and the SQl that is being generated is a SELECT instead of INSERT.
Thank you
but your map shows a one-to-many relation between Note and Event...
all of your code remain as they are, but instead of this line :
patient.MobilePatient.MCalmEvents.Find(e => e.Id == eventid).Note = noteToAdd;
replace these lines:
noteToAdd.EventID = oEvent.ID; // replace field names, to exactly what they are;
context.Note.Add(noteToAdd);
var oEvent = patient.MobilePatient.MCalmEvents.Find(e => e.Id == eventid);
oEvent.NoteID = noteToAdd.ID; // replace field names, to exactly what they are;
also i think if you don`t write these two:
var oEvent = patient.MobilePatient.MCalmEvents.Find(e => e.Id == eventid);
oEvent.NoteID = noteToAdd.ID; // replace field names, to exactly what they are;
there is not any problem, i`m not sure
According to your map, Event entity has a list of Note as navigation property, and i think you should add to this collection instead, what you write in this line:
patient.MobilePatient.MCalmEvents.Find(e => e.Id == eventid).Note = noteToAdd;
i think should be like this:
patient.MobilePatient.MCalmEvents.Find(e => e.Id == eventid).Add(noteToAdd);
in addition, what kind of error you get ? can you explain your error ?
are sure there is no add method on Event navigation property
why don`t you try to add note from context directly? like:
context.Note.Add(noteToAdd);

EF DbContext. How to avoid caching?

Spent a lot of time, but still cann't understand how to avoid caching in DbContext.
I attached below entity model of some easy case to demonstrate what I mean.
The problem is that dbcontext caching results. For example, I have next code for querying data from my database:
using (TestContext ctx = new TestContext())
{
var res = (from b in ctx.Buildings.Where(x => x.ID == 1)
select new
{
b,
flats = from f in b.Flats
select new
{
f,
people = from p in f.People
where p.Archived == false
select p
}
}).AsEnumerable().Select(x => x.b).Single();
}
In this case, everything is fine: I got what I want (Only persons with Archived == false).
But if I add another query after it, for example, query for buildings that have people that have Archived flag set to true, I have next things, that I really cann't understand:
my previous result, that is res, will be added by data (there
will be added Persons with Archived == true too)
new result will contain absolutely all Person's, no matter what Archived equals
the code of this query is next:
using (TestContext ctx = new TestContext())
{
var res = (from b in ctx.Buildings.Where(x => x.ID == 1)
select new
{
b,
flats = from f in b.Flats
select new
{
f,
people = from p in f.People
where p.Archived == false
select p
}
}).AsEnumerable().Select(x => x.b).Single();
var newResult = (from b in ctx.Buildings.Where(x => x.ID == 1)
select new
{
b,
flats = from f in b.Flats
select new
{
f,
people = from p in f.People
where p.Archived == true
select p
}
}).AsEnumerable().Select(x => x.b).Single();
}
By the way, I set LazyLoadingEnabled to false in constructor of TestContext.
Does anybody know how to workaround this problem? How can I have in my query what I really write in my linq to entity?
P.S. #Ladislav may be you can help?
You can use the AsNoTracking method on your query.
var res = (from b in ctx.Buildings.Where(x => x.ID == 1)
select new
{
b,
flats = from f in b.Flats
select new
{
f,
people = from p in f.People
where p.Archived == false
select p
}
}).AsNoTracking().AsEnumerabe().Select(x => x.b).Single();
I also want to note that your AsEnumerable is probably doing more harm than good. If you remove it, the Select(x => x.b) will be translated to SQL. As is, you are selecting everything, then throwing away everything but x.b in memory.
have you tried something like:
ctx.Persons.Where(x => x.Flat.Building.Id == 1 && x.Archived == false);
===== EDIT =====
In this case I think you approach is, imho, really hazardous. Indeed you works on the data loaded by EF to interpret your query rather than on data resulting of the interpretation of your query. If one day EF changes is loading policy (for example with a predictive pre-loading) your approach will "send you in then wall".
For your goal, you will have to eager load the data you need to build your "filterd" entity. That is select the building, then foreach Flat select the non archived persons.
Another solution is to use too separate contexts in an "UnitOfWork" like design.

Generate dynamic select lambda expressions

I am somewhat new to expression trees and I just don't quite understand some things.
What I need to do is send in a list of values and select the columns for an entity from those values. So I would make a call something like this:
DATASTORE<Contact> dst = new DATASTORE<Contact>();//DATASTORE is implemented below.
List<string> lColumns = new List<string>() { "ID", "NAME" };//List of columns
dst.SelectColumns(lColumns);//Selection Command
I want that to be translated into code like this (Contact is an entity using the EF4):
Contact.Select(i => new Contact { ID = i.ID, NAME = i.NAME });
So let's say I have the following code:
public Class<t> DATASTORE where t : EntityObject
{
public Expression<Func<t, t>> SelectColumns(List<string> columns)
{
ParameterExpression i = Expression.Parameter(typeof(t), "i");
List<MemberBinding> bindings = new List<MemberBinding>();
foreach (PropertyInfo propinfo in typeof(t).GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if (columns.Contains(propinfo.Name))
{
MemberBinding binding = Expression.Bind(propinfo, Expression.Property(i, propinfo.Name));
bindings.Add(binding);
}
}
Expression expMemberInit = Expression.MemberInit(Expression.New(typeof(t)), bindings);
return Expression.Lambda<Func<t, t>>(expMemberInit, i);
}
When I ran the above code I got the following error:
The entity or complex type 'Contact' cannot be constructed in a LINQ to Entities query.
I looked at the body of the query and it emitted the following code:
{i => new Contact() {ID = i.ID, NAME = i.NAME}}
I am pretty sure that I should be able to construct the a new entity because I wrote this line explicitly as a test to see if this could be done:
.Select(i => new Contact{ ID = i.ID, NAME = i.NAME })
This worked, but I need to construct the select dynamically.
I tried decompiling a straight query(first time I have looked at the low level code) and I can't quite translate it. The high level code that I entered is:
Expression<Func<Contact, Contact>> expression = z =>
new Contact { ID = z.ID, NAME = z.NAME };
Changing the framework used in the decompiler I get this code:
ParameterExpression expression2;
Expression<Func<Contact, Contact>> expression =
Expression.Lambda<Func<Contact, Contact>>
(Expression.MemberInit(Expression.New((ConstructorInfo) methodof(Contact..ctor),
new Expression[0]), new MemberBinding[] { Expression.Bind((MethodInfo)
methodof(Contact.set_ID), Expression.Property(expression2 = Expression.Parameter(typeof(Contact), "z"), (MethodInfo)
methodof(Contact.get_ID))), Expression.Bind((MethodInfo)
methodof(Contact.set_NAME), Expression.Property(expression2, (MethodInfo)
methodof(Contact.get_NAME))) }), new ParameterExpression[] { expression2
});
I have looked several places to try and understand this but I haven't quite gotten it yet. Can anyone help?
These are some places that I have looked:
msdn blog -- This is exactly what I want to do but my decopiled code soes not have Expression.Call.
msdn MemberInit
msdn Expression Property
stackoverflow EF only get specific columns -- This is close but this seems like it is doing the same thing as if i just use a select off of a query.
stackoverflow lambda expressions to be used in select query -- The answer here is exactly what I want to do, I just don't understand how to translate the decompiled code to
C#.
When I did it last time I projected result to not mapped class (not entity) and it worked, everything else was the same as in your code. Are you sure that not dynamic query like .Select(i => new Contact{ ID = i.ID, NAME = i.NAME }) works?