QueryOver in NHibernate - c#

I'm lost when it comes to to QueryOver in NHibernate, I'm trying to query over a database and retrive 4 values of importans, the rest are unnecessary and take up processing power.
I'm trying this:
var ext = _session.QueryOver<ExternServiceSettings>()
.Where(x => x.ExternService == ExternServiceEnum.Outlook).List();
which works fine but takes too long and returns everything in the database. then I tried:
var ext = _session.QueryOver<ExternServiceSettings>()
.Where(x => x.ExternService == ExternServiceEnum.Outlook)
.List<ExternServiceSettings>()
.Select(y => y.UserName);
However this only return the username and won't let me fetch more than one value...
All help is appreciated!

We should use .SelectList()
Check the example from doc:
var selection =
session.QueryOver<Cat>()
.SelectList(list => list
.Select(c => c.Name)
.SelectAvg(c => c.Age))
.List<object[]>();
see more here:
16.7. Projections

Related

LINQ Query find columns where string contains any letter

I'm trying to find all customer codes where the customer has a status of "A" and whose code does not contain any letter using LINQ query.
var activeCustomers = Customers.Where(x => x.Status == "A" && x.Code.Any(n => !char.IsLetter(n))).Select(x => x.Code);
When I run this query in LinqPad I get the following error:
You'll need to do this as a two part query. First, you could get all the users who's status is "A":
var activeCustomers = Customers.Where(x => x.Status == "A").ToList();
After you've got those in-memory, you can create an additional filter for char.IsDigit:
var codes = activeCustomers.Where(x => x.Code.Any(n => !char.IsLetter(n)))
.Select(x => x.Code)
.ToArray();
As commenters have stated, IsLetter() cannot be translated to SQL. However, you could do the following, which will first retrieve all items with Status "A" from the database, then will apply your criteria after retrieval:
var activeCustomers = Customers.Where(x => x.Status == "A").AsEnumerable().Where(x => x.Code.Any(n => !char.IsLetter(n))).Select(x => x.Code);
You'll have to determine if it's acceptable (from a performance perspective) to retrieve all customers with "A" and then process.
The AsEnumerable() transitions your LINQ query to working not with IQueryable (which works with SQL) but with IEnumerable, which is used for plain LINQ to objects.
Since it is LINQ 2 SQL, there is no natural way to translate char.IsLetter to something SQL can understand. You can hydrate a query that retrieves your potential candidates and then apply an addition in-memory filter. This also solves the issue where LINQ 2 SQL has a preference for a string and you are dealing with chars
var activeCustomers = Customers.Where(x => x.Status == "A").ToList();
var filteredCustomers = activeCustomers.Where(x =>
x.Code.Any(n => !char.IsLetter(n))).Select(x => x.Code).ToList();
There are two performance hits here. First, you're retrieving all potential records, which isn't too desirable. Second, in your above code you were only interested in an enumerable collection of codes, which means our query is including far more data than we originally wanted.
You could tighten up the query by only returning back to columns necessary to apply your filtering:
var activeCustomers = Customers.Where(x => x.Status == "A")
Select(x => new Customer{ Status = x.Status, Code = x.Code }).ToList();
You still return more sets than you need, but your query includes fewer columns.

Cannot implicitly convert type 'System.Collections.Generic.List<System.Collections.Generic.IEnumerable<xxx>>' to 'System.Collections.Generic.List<xxx>

I am getting the following error. I googled it for more than a day but I cant find the exact solution, Please help me Thank you
ERROR: Cannot implicitly convert type
System.Collections.Generic.List<System.Collections.Generic.IEnumerable<ADCO.eJMC.EntityDataModel.ShareholderUser>>
to
System.Collections.Generic.List<ADCO.eJMC.EntityDataModel.ShareholderUser>
I used the following code
List<ShareholderUser> list = new List<ShareholderUser>();
list = dataContext.EJMCShareholderApprovals
.Include(s => s.Shareholder.ShareholderUsers)
.Where(e => e.EJMCRequestId == requestId)
.Select(s => s.Shareholder.ShareholderUsers
.Where(x => x.AccessMode == true))
.ToList();
The problem is that at the moment, you're selecting a sequence of sequences - one sequence of ShareholderUser items for each Shareholder. If you just want a list of ShareholderUser items, you need to flatten the results. That's most easily done using SelectMany, which can actually replace your Select call in this case.
List<ShareholderUser> list = dataContext.EJMCShareholderApprovals
.Where(e => e.EJMCRequestId == requestId)
.SelectMany(s => s.Shareholder.ShareholderUsers)
.Where(x => x.AccessMode == true)
.ToList();
Note how breaking the query over multiple lines makes it much simpler to read, too. Also, there's no point in initializing the list variable to a new List<ShareholderUser> if you're then immediately going to give it a different value. I've also removed the Include call, as that was unnecessary - you're explicitly selecting Shareholder.ShareholderUsers in the query, so you don't need to include it.
This should do it?
var list = dataContext.EJMCShareholderApprovals
.Include(s => s.Shareholder.ShareholderUsers)
.Where(e => e.EJMCRequestId == requestId)
.Select(s => s.Shareholder.ShareholderUsers
.Where(x => x.AccessMode == true)).ToList();
though you are doing select on to ShareHolderUsers? Are you trying to get a list of ShareHolderUsers or a list of lists of ShareHolderUsers?
.Select(s => s.Shareholder.ShareholderUsers

Linq select with filtering not working

I'm trying to select one field last record in filtered database (this is different than last inserted record). I tried with following code in controller but instead of field value, i'm getting "true" or "false", depending on if there's results after filtering or not.
List<Pozicije> poz = new List<Pozicije>();
poz = db.Pozicijes.Where(p => p.grupa == grupa)
.OrderBy(p => p.sifra_pozicije).ToList();
string pos = poz.Select(p => p.sifra_pozicije.Contains(s)).LastOrDefault().ToString();
can someone point me how to get value i need instead?
Try this instead. I've combined both parts of your query into one.
var pos =
Convert.ToString(db.Pozicijes.Where(p => p.grupa == grupa
&& p.sifra_pozicije.Contains(s))
.OrderByDescending(p => p.sifra_pozicije)
.Select(p => p.sifra_pozicije)
.FirstOrDefault());
If it doesn't work, you may need to tell us what types s and sifra_pozicije are.
LastOrDefault is not supported with LINQ to Entities/LINQ TO SQL. You need to do OrderByDescending and then get First record. Like:
string pos = db.Pozicijes.Where(p => p.grupa == grupa && p.sifra_pozicije.Contains(s)))
.OrderByDescending(p=> p.sifra_pozicije)
.Select(r=> r.sifra_pozicije)
.First();

linq-to-sql SelectMany() troubles

I'm trying to find a more elegant way of pulling information from my db to my web application. Currently I pull all data in my table and use only two columns' data. It was suggested that I look into using SelectMany() to accomplish this by being able to select only the columns I need.
I'm not entirely sure how to translate the msdn example to a linq statement using a linq-to-sql db.
My current statement is this:
return db.document_library_sitefiles
.Where(item => item.SiteID == siteId)
.Select(item => item.document_library)
.GroupBy(item => item.Filename)
.Select(group => group.OrderByDescending(p=>p.Version).First())
.Where(item => !item.Filename.Contains("*")).ToList();
My current attempt, which I know is wrong, looks like this:
return db.document_library_sitefiles
.Where(item => item.SiteID == siteId)
.SelectMany(item => item.document_library, (filename, filesize)
=> new { filename, filesize })
.Select(item => new { filename = item.document_library.filename,
filesize = item.document_library.filesize })
.ToList();
Am I remotely close to getting my intended results?
Basically I want to get the data in my filename and filesize columns without pulling the rest of the data which includes file content (not my design or idea) so I'm not flooding my server with needless information just to show a simple data table of the files currently in this db.
I think you're going in the right direction. It looks like you're just changing the second query in an undesirable way. Give this a try;
return db.document_library_sitefiles
.Where(item => item.SiteID == siteId)
.Select(item => item.document_library)
.GroupBy(item => item.Filename)
.Select(group => group.OrderByDescending(p=>p.Version).First())
.Where(item => !item.Filename.Contains("*"))
.Select( item => new { filename = item.document_library.filename,
filesize = item.document_library.filesize } ).ToList();
Basically you want to keep all of the logic exactly the same as in the first query then just tack on one more select where you initialize the anonymous object to return.
In your attempt at the query you altered some of the underlying logic. You want all of the early operations to remain exactly the same (otherwise the results you return will be from a different set), you only want to transform objects in the resulting set which is why you add a select after the final where.
Cant you just append a select to you first statement?
....Where(item => !item.Filename.Contains("*"))
.Select(item => new {
item.Filename,
item.Filesize
}).ToList();

How to query (using LINQ) FormattedValues in Dynamics CRM 2011

I am using LINQ to retrieve Account type entities from Microsoft Dynamics CRM Online. I am unable to filter the list for a particular formatted value. I have the correct value, but am receiving zero records.
I'm creating my connection like this:
var connection = new CrmConnection("CRMOnline");
connection.ProxyTypesEnabled = true;
CrmOrganizationServiceContext _context = new CrmOrganizationServiceContext(connection);
I've tried:
List<Account> items = _context.CreateQuery<Account>()
.Where( c => ((OptionSetValue)c["new_accreditationstatus"]).Equals(7))
.ToList();
and
List<Account> items = _context.CreateQuery<Account>()
.Where( c => c.GetFormattedAttributeValue("new_accreditationstatus") == "7"
.ToList();
and
List<Account> items = _context.CreateQuery<Account>()
.Where( c => c["new_accreditationstatus"] == "7"
.ToList();
The last on throws a System.Format exception.
Filters on normal properties, i.e. .Where(c => c.AccountNumber.StartsWith("2010")) work perfectly fine.
You can only get access to the _____Set entities when generating the early-bound CRM file (look into crmsvcutil.exe/Xrm.cs online) and creating an early-bound derivative of CrmOrganizationServiceContext (commonly called XrmServiceContext). You can see the available constructors in the early-bound file.
So if you know the (int) value of the OptionSetValue in advance (7, in this case), you can just use this value as one of the arguments in the Where clause, as you've stated elsewhere:
.Where( c => c.new_AccreditationStatus.Value == 7)
EDIT (try this):
var list = _context.AccountSet.Where(c =>
c.FormattedValues["new_accreditationstatus"] == "7").ToList();
Another great question, but unfortunately, I think this will represent another failure/"limitation" of the Linq provider, which doesn't mention anything about FormattedValues as one of the permitted uses of the Where clause, though it is permitted as an item in the Select clause.
The actual values for OptionSetValues are stored in the StringMap entity, and incidentally enough, you can access the StringMap entity via Linq. An example is as follows.
// This query gets one permissible value for this entity and field.
var actualValue = _context.CreateQuery("stringmap")
.Where(x => x.GetAttributeValue<string>("attributename") == "new_accreditationstatus")
.Where(x => x.GetAttributeValue<int>("value") == "7")
.Where(x => x.GetAttributeValue<int>("objecttypecode") == Account.EntityTypeCode)
.Select(x => x.GetAttributeValue<string>("value"))
.Single();
However, trying to build on this with a subquery and a version of your original query, as in the below, results in an exception, also below.
var actualValues = _context.CreateQuery("stringmap")
.Where(x => x.GetAttributeValue<string>("attributename") == "new_accreditationstatus")
.Where(x => x.GetAttributeValue<int>("objecttypecode") == Xrm.Account.EntityTypeCode);
// This (modified) query uses the StringMap values from the previous query in
// a subquery, linking to the int (attributevalue) value that
// new_accreditationstatus represents.
List<Account> items = _context.CreateQuery<Account>()
.Where(c => actualValues
.Where(x => x.GetAttributeValue<int>("attributevalue") == c.new_accreditationstatus.Value)
.Select(x => x.GetAttributeValue<string>("attributevalue"))
.Single() == "7")
.ToList();
...throws an exception.
Privilege Type Read not defined on entity 'StringMap'.
Which is of course frustrating, because somehow, Linq allows you to query the string map in the first query.
So you'll have to first query the StringMap entity for the AttributeValue that corresponds to "7", then use that value in a new query that references that value as follows:
var actualValue = _context.CreateQuery("stringmap")
.Where(x => x.GetAttributeValue<string>("attributename") == "new_accreditationstatus")
.Where(x => x.GetAttributeValue<int>("value") == "7")
.Where(x => x.GetAttributeValue<int>("objecttypecode") == Account.EntityTypeCode)
.Select(x => x.GetAttributeValue<string>("attributevalue"))
.Single();
List<Account> items = _context.CreateQuery<Account>()
.Where(c => c.new_accreditationstatus = new OptionSetValue(actualValue)
.ToList();
If I can ever find a way to do all of this in one query, I will definitely edit and repost.
Have you seen the crmsvcutil extension that will generate enumerations for optionsets?

Categories