Results IOrderedEnumerable is null outside context - c#

I'm learning entity framework and hitting a wall. Here is my code:
public IOrderedEnumerable<ArchiveProcess> getHistory()
{
using (ArchiveVMADDatabase.ArchiveDatabaseModel dataContext = new ArchiveDatabaseModel())
{
var query = (from history in dataContext.ArchiveProcess.AsNoTracking()
orderby history.ArchiveBegin descending
select history).Take(10).ToList();
return query as IOrderedEnumerable<ArchiveProcess>;
}
}
When I step through this code, query is a List<ArchiveProcess> containing my ten desired results. However, as soon as I exit the method and the context is disposed of, query becomes null. How can I avoid this? I tried doing this instead:
select new ArchiveProcess
{
ArchiveBegin = history.ArchiveBegin,
ArchiveEnd = history.ArchiveEnd,
DeploysHistoryCount = history.DeploysHistoryCount,
MachinesHistory = history.MachinesHistory,
ScriptHistory = history.ScriptHistory
}
But then I received a NotSupportedException. Why does entity framework delete my precious entities as soon as the context is disposed of and how do I tell it to stop?

I think there are several ways to avoid this but in general you should know precisely how long you want your context to live. In general it's better to have the using statement wrapped through the entire method.
In order to avoid the garbage collection you can do something like this: set the object in memory and then add value to that object.
List<ArchiveProcess> query;
using (ArchiveVMADDatabase.ArchiveDatabaseModel dataContext = new ArchiveDatabaseModel())
{
query = (from history in dataContext.ArchiveProcess.AsNoTracking()
orderby history.ArchiveBegin descending
select history).Take(10).ToList();
return query; /// you do not really need to all enumerable as IOrderedEnumerable<ArchiveProcess>;
}

query as IOrderedEnumerable<ArchiveProcess>;
query is a List<ArchiveProcess>, as returns null when you try use it to cast something to an interface it doesn't implement. List<ArchiveProcess> is not an IOrderedEnumerable<ArchiveProcess> so query as IOrderedEnumerable<ArchiveProcess> is null.
The only thing that IOrderedEnumerable<T> does that IEnumerable<T> doesn't do, is implement CreateOrderedEnumerable<TKey>, which can be called directly or through ThenBy and ThenByDescending, so you can add a secondary sort on the enumerable that only affects items considered equivalent by the earlier sort.
If you don't use CreateOrderedEnumerable() either directly or through ThenBy() or ThenByDescending() then change to not attempt to use it:
public IEnumerable<ArchiveProcess> getHistory()
{
using (ArchiveVMADDatabase.ArchiveDatabaseModel dataContext = new ArchiveDatabaseModel())
{
return (from history in dataContext.ArchiveProcess.AsNoTracking()
orderby history.ArchiveBegin descending
select history).Take(10).ToList();
}
}
Otherwise reapply the ordering, so that ThenBy etc. can be used with it:
public IOrderedEnumerable<ArchiveProcess> getHistory()
{
using (ArchiveVMADDatabase.ArchiveDatabaseModel dataContext = new ArchiveDatabaseModel())
{
return (from history in dataContext.ArchiveProcess.AsNoTracking()
orderby history.ArchiveBegin descending
select history).Take(10).ToList().OrderBy(h => h.ArchiveBegin);
}
}
However this adds a bit more overhead, so don't do it if you don't need it.
Remember, IOrderedEnumerable<T> is not just an ordered enumerable (all enumerables are in some order, however arbitrary), it's an ordered enumerable that has knowledge about the way it which it is ordered so as to provide for secondary sorting. If you don't need that then you don't need IOrderedEnumerable<T>.

using (ArchiveVMADDatabase.ArchiveDatabaseModel dataContext = new ArchiveDatabaseModel())
{
var query = dataContext.ArchiveProcess.AsNoTracking().Take(10).OrderBy(o=> o.ArchiveBegin);
return query;
}

Related

Blank Entry for DataContext

We have a data bound control that is populated with a list of banks.
I can figure out how to Read, Insert and Delete real accounts.
On the form, however, our client needs a way to select a "None" option (some customers use cash).
I cannot insert a "None" entry into the banks data table because of how the data tables are linked to other data tables.
I attempted to insert a "None" record before returning the DataContext so that this option would always appear at the top of the list, but there doesn't appear to be any way to get the data into the collection.
public System.Data.Linq.Table<bank> Banks
{
get
{
return this.GetTable<bank>();
}
}
I was able to insert a fake record into an IQueryable list:
internal IQueryable<bank> GetBankInfo(bool insertBlank = false)
{
var list = (from item in banks
orderby item.bank_name
select item).ToList();
if (insertBlank)
{
var item = new bank()
{
bank_name = String.Empty,
contact_name = String.Empty,
contact_phone_num = String.Empty,
routing_num = "None",
source = String.Empty,
fact_routing_num = String.Empty,
note_id = (int?)null,
};
list.Insert(0, item);
}
return (IQueryable<bank>)list;
}
But, I can't use this because it throws a TargetInvocationException:
Unable to cast object of type 'System.Collections.Generic.List1[Billing.DataDB.bank]' to type 'System.Linq.IQueryable1[Billing.DataDB.bank]'.
How do I insert a blank record for the DataContext display?
I do not want the blank record in the database.
You can fix this two ways:
First, change the return type of the method to IEnumerable<T>...
internal IEnumerable<bank> GetBankInfo(bool insertBlank = false)
...and leave out the cast in the return statement. It'll be redundant, as List<T> implements IEnumerable<T>
return list;
Based on what you mentioned in your question, this should work.
Second way:
If you're doing something else with that list/queryable/whatever, such that you really need it to really be IQueryable<T>, that's even easier. Leave the function return type as it was:
internal IQueryable<bank> GetBankInfo(bool insertBlank = false)
And return this:
return list.AsQueryable();
Here's the problem:
I was able to insert a fake record into an IQueryable list:
Actually, you weren't. You enumerated the results of an IQueryable into a new List<T> instance: That's what happened in your ToList() call. It's not a cast, it creates a new collection object. Then you inserted a fake record into the List<T>.
The thing you were casting to IQueryable<bank> wasn't the IQueryable<bank> that you started with; it was a completely different object which contained all the items that were enumerated by the IQueryable<bank>.
The purpose of IQueryable is so the actual query can be executed remotely, as with Entity Framework for example. However, once you materialize the query in the ToList() call, the remote ship has sailed the barn out of the station. Since IQueryable<T> inherits from IEnumerable<T>, I'd tend to use the latter for a general "reference to sequence of things" type. Any method that takes IEnumerable<T> for a parameter will happily consume an IQueryable<T> instead.

Whether to use AsEnumerable() in LINQ or not?

I understand AsEnumerable() is used to switch from "LINQ to SQL" to "LINQ to Object", so we can use some extra (mostly user defined) methods in our LINQ queries. But from my experience I've seen, using AsEnumerable() makes the query much slower. In that case I can enumerate the list later to apply my own methods, but still the result is pretty slow.
Can anyone suggest any better approach?
Here is some code sample of what I'm trying to do?
With AsEnumerable():
var Data = (from r in _context.PRD_ChemProdReq.AsEnumerable()
//where r.RecordStatus == "NCF"
orderby r.RequisitionNo descending
select new PRDChemProdReq
{
RequisitionID = r.RequisitionID,
RequisitionNo = r.RequisitionNo,
RequisitionCategory = DalCommon.ReturnRequisitionCategory(r.RequisitionCategory),
RequisitionType = DalCommon.ReturnOrderType(r.RequisitionType),
ReqRaisedOn = (Convert.ToDateTime(r.ReqRaisedOn)).ToString("dd'/'MM'/'yyyy"),
RecordStatus= DalCommon.ReturnRecordStatus(r.RecordStatus),
RequisitionFromName = DalCommon.GetStoreName(r.RequisitionFrom),
RequisitionToName = DalCommon.GetStoreName(r.RequisitionTo)
}).ToList();
without AsEnumerable():
var Data = (from r in _context.PRD_ChemProdReq
//where r.RecordStatus == "NCF"
orderby r.RequisitionNo descending
select new PRDChemProdReq
{
RequisitionID = r.RequisitionID,
RequisitionNo = r.RequisitionNo,
RequisitionCategory = r.RequisitionCategory,
RequisitionType = (r.RequisitionType),
ReqRaisedOnTemp = (r.ReqRaisedOn),
RecordStatus= (r.RecordStatus),
RequisitionFrom = (r.RequisitionFrom),
RequisitionTo = (r.RequisitionTo)
}).ToList();
foreach (var item in Data)
{
item.RequisitionCategory = DalCommon.ReturnRequisitionCategory(item.RequisitionCategory);
item.RequisitionType = DalCommon.ReturnOrderType(item.RequisitionType);
item.ReqRaisedOn = (Convert.ToDateTime(item.ReqRaisedOnTemp)).ToString("dd'/'MM'/'yyyy");
item.RecordStatus = DalCommon.ReturnRecordStatus(item.RecordStatus);
item.RequisitionFromName = DalCommon.GetStoreName(item.RequisitionFrom);
item.RequisitionToName = DalCommon.GetStoreName(item.RequisitionTo);
}
It looks like you confuse these two interfaces as two totally different things. In fact IQueryable is inherited from IEnumerable, so whatever worked for you using with latter, would work with former as well, so don't need to use AsEnumerable.
Behind the scenes though these interfaces are implemented quite differently - IEnumerable will process your collection in memory, and IQueryable would pass the query to the underlying data provider. You can imagine that if a database table contains millions of records and you try to sort it, DB server can do it very quickly (using indexes) so Queryable will shine. For IEnumerable, all the data need to be loaded to your computer memory and sorted there.
For a longer answers search for "IEnumerable IQueryable difference" on SO, you will see a plenty of details:
Random Link 1
Random Link 2
Update: If you remove call .ToList from your second example, then the result won't be automatically loaded to memory. At this point you need to decide which items you want to store in memory and call your functions for them only.
var Data = (from r in _context.PRD_ChemProdReq
orderby r.RequisitionNo descending
select new PRDChemProdReq
{
// do your initialization
});
var subsetOfData = Data.Take(100).ToList(); // Now it's loaded to memory
foreach (var item in subsetOfData)
{
item.RequisitionCategory = DalCommon.ReturnRequisitionCategory(item.RequisitionCategory);
item.RequisitionType = DalCommon.ReturnOrderType(item.RequisitionType);
item.ReqRaisedOn = (Convert.ToDateTime(item.ReqRaisedOnTemp)).ToString("dd'/'MM'/'yyyy");
item.RecordStatus = DalCommon.ReturnRecordStatus(item.RecordStatus);
item.RequisitionFromName = DalCommon.GetStoreName(item.RequisitionFrom);
item.RequisitionToName = DalCommon.GetStoreName(item.RequisitionTo);
}
Now if you actually need to assign these properties for all your data and data can be arbitrary large, you need to work out a strategy how you can do it. The very simple option is to save them to database to a new table, then the size of processed data will be only limited by the capacity of your database.
AsEnumerable() will be slower if you add any query elements after it.
Even though AsEnumerable() doesn't execute the query directly, applying a where or orderby after the AsEnumerable() means that the Sql will get all items and then apply filtering and ordering to the collection in memory.
In short:
Without AsEnumerable() = filtering and ordering done in SQL
With AsEnumerable() and then Where or orderby = applied to whole collection brought into memory.
You can only run user defined functions on a collection in memory (as your Linq to SQL will not be able to interpret your functions to SQL code). So your second code snippet (without AsEnumerable()) is probably best.
The only other alternative is to apply your user defined functions in SQL itself.
OK guys, I took notice of different points from all of you & came up with this:
var Data = (from r in _context.PRD_ChemProdReq.AsEnumerable()
//where r.RecordStatus == "NCF"
join rf in _context.SYS_Store on (r.RequisitionFrom==null?0: r.RequisitionFrom) equals rf.StoreID into requisitionfrom
from rf in requisitionfrom.DefaultIfEmpty()
join rt in _context.SYS_Store on (r.RequisitionTo == null ? 0 : r.RequisitionTo) equals rt.StoreID into requisitionto
from rt in requisitionto.DefaultIfEmpty()
orderby r.RequisitionNo descending
select new PRDChemProdReq
{
RequisitionID = r.RequisitionID,
RequisitionNo = r.RequisitionNo,
RequisitionCategory = DalCommon.ReturnRequisitionCategory(r.RequisitionCategory),
RequisitionType = r.RequisitionType == "UR" ? "Urgent" : "Normal",
ReqRaisedOn = (Convert.ToDateTime(r.ReqRaisedOn)).ToString("dd'/'MM'/'yyyy"),
RecordStatus = (r.RecordStatus=="NCF"? "Not Confirmed": "Approved"),
RequisitionFromName = (rf==null? null: rf.StoreName),
RequisitionToName = (rt == null ? null : rt.StoreName)
});
First of all I removed my ToList() which does nothing but executes the query which is already done when I called AsEnumerable(). No points to execute the same query twice. Also my custom method calls within the select block was also playing a major part slowing down things. I tried lessen the method calls, rather used join where possible. It makes things pretty faster. Thank You all.

Generic OrderBy

I'm new on development and want to make a small change by adding orderby to the existing code. Can someone enlighten me on how to orderby to this piece of code?
public List<Employee> GetAllEmployees()
{
ebll employeeBll = new EmployeeBLL();
return ebll.GetAllEmployees();
}
I agree with the answer that T.S. gave, however I would modify to return an IQueryable instead of returning the a .ToList() if you want to leave the option to further filter the list in the calling method. For example GetAllEmployees().Where(e=>e.Name="Brad") otherwise you are enumerating the list early and not taking advantage of using the underlying data source to do the heavy lifting of filtering therefore returning more data than is needed.
Set reference to Linq before doing this. I used employee LastName for order by
public List<Employee> GetAllEmployees()
{
ebll employeeBll = new EmployeeBLL();
return ebll.GetAllEmployees().OrderBy(e => e.LastName).ToList();
}

Custom Method in LINQ Query

I sum myself to the hapless lot that fumbles with custom methods in LINQ to EF queries. I've skimmed the web trying to detect a pattern to what makes a custom method LINQ-friendly, and while every source says that the method must be translatable into a T-SQL query, the applications seem very diverse. So, I'll post my code here and hopefully a generous SO denizen can tell me what I'm doing wrong and why.
The Code
public IEnumerable<WordIndexModel> GetWordIndex(int transid)
{
return (from trindex in context.transIndexes
let trueWord = IsWord(trindex)
join trans in context.Transcripts on trindex.transLineUID equals trans.UID
group new { trindex, trans } by new { TrueWord = trueWord, trindex.transID } into grouped
orderby grouped.Key.word
where grouped.Key.transID == transid
select new WordIndexModel
{
Word = TrueWord,
Instances = grouped.Select(test => test.trans).Distinct()
});
}
public string IsWord(transIndex trindex)
{
Match m = Regex.Match(trindex.word, #"^[a-z]+(\w*[-]*)*",
RegexOptions.IgnoreCase);
return m.Value;
}
With the above code I access a table, transIndex that is essentially a word index of culled from various user documents. The problem is that not all entries are actually words. Nubers, and even underscore lines, such as, ___________,, are saved as well.
The Problem
I'd like to keep only the words that my custom method IsWord returns (at the present time I have not actually developed the parsing mechanism). But as the IsWord function shows it will return a string.
So, using let I introduce my custom method into the query and use it as a grouping parameter, the is selectable into my object. Upon execution I get the omninous:
LINQ to Entities does not recognize the method
'System.String IsWord(transIndex)' method, and this
method cannot be translated into a store expression."
I also need to make sure that only records that match the IsWord condition are returned.
Any ideas?
It is saying it does not understand your IsWord method in terms of how to translate it to SQL.
Frankly it does not do much anyway, why not replace it with
return (from trindex in context.transIndexes
let trueWord = trindex.word
join trans in context.Transcripts on trindex.transLineUID equals trans.UID
group new { trindex, trans } by new { TrueWord = trueWord, trindex.transID } into grouped
orderby grouped.Key.word
where grouped.Key.transID == transid
select new WordIndexModel
{
Word = TrueWord,
Instances = grouped.Select(test => test.trans).Distinct()
});
What methods can EF translate into SQL, i can't give you a list, but it can never translate a straight forward method you have written. But their are some built in ones that it understands, like MyArray.Contains(x) for example, it can turn this into something like
...
WHERE Field IN (ArrItem1,ArrItem2,ArrItem3)
If you want to write a linq compatible method then you need to create an expresion tree that EF can understand and turn into SQL.
This is where things star to bend my mind a little but this article may help http://blogs.msdn.com/b/csharpfaq/archive/2009/09/14/generating-dynamic-methods-with-expression-trees-in-visual-studio-2010.aspx.
If the percentage of bad records in return is not large, you could consider enumerate the result set first, and then apply the processing / filtering?
var query = (from trindex in context.transIndexes
...
select new WordIndexModel
{
Word,
Instances = grouped.Select(test => test.trans).Distinct()
});
var result = query.ToList().Where(word => IsTrueWord(word));
return result;
If the number of records is too high to enumerate, consider doing the check in a view or stored procedure. That will help with speed and keep the code clean.
But of course, using stored procedures has disadvatages of reusability and maintainbility (because of no refactoring tools).
Also, check out another answer which seems to be similar to this one: https://stackoverflow.com/a/10485624/3481183

SQL "not in" syntax for Entity Framework 4.1

I have a simple issue with Entity Framework syntax for the "not in" SQL equivalent. Essentially, I want to convert the following SQL syntax into Entity Framework syntax:
select ID
from dbo.List
where ID not in (list of IDs)
Here is a method that I use for looking up a single record:
public static List GetLists(int id)
{
using (dbInstance db = new dbInstance())
{
return db.Lists.Where(m => m.ID == id);
}
}
Here is a pseudo-method that I want to use for this:
public static List<List> GetLists(List<int> listIDs)
{
using (dbInstance db = new dbInstance())
{
return db.Lists.Where(**** What Goes Here ****).ToList();
}
}
Can anyone give me pointers as to what goes in the Where clause area? I read some forums about this and saw mention of using .Contains() or .Any(), but none of the examples were a close enough fit.
Give this a go...
public static List<List> GetLists(List<int> listIDs)
{
using (dbInstance db = new dbInstance())
{
// Use this one to return List where IS NOT IN the provided listIDs
return db.Lists.Where(x => !listIDs.Contains(x.ID)).ToList();
// Or use this one to return List where IS IN the provided listIDs
return db.Lists.Where(x => listIDs.Contains(x.ID)).ToList();
}
}
These will turn into approximately the following database queries:
SELECT [Extent1].*
FROM [dbo].[List] AS [Extent1]
WHERE NOT ([Extent1].[ID] IN (<your,list,of,ids>))
or
SELECT [Extent1].*
FROM [dbo].[List] AS [Extent1]
WHERE [Extent1].[ID] IN (<your,list,of,ids>)
respectively.
This one requires you to think backwards a little bit. Instead of asking if the value is not in some list of ids, you have to ask of some list of id's does not contain the value. Like this
int[] list = new int[] {1,2,3}
Result = (from x in dbo.List where list.Contains(x.id) == false select x);
Try this for starters ...
m => !listIDs.Contains(m.ID)
This might be a way to do what you want:
// From the method you provided, with changes...
public static List GetLists(int[] ids) // Could be List<int> or other =)
{
using (dbInstance db = new dbInstance())
{
return db.Lists.Where(m => !ids.Contains(m.ID));
}
}
However I've found that doing so might raise error on some scenarios, specially when the list is too big and connection is somewhat slow.
Remember to check everything else BEFORE so this filter might have less values to check.
Also remember that Linq does not populate the variable when you build your filter/query (at least not by default). If you're going to iterate for each record, remember to call a ToList() or ToArray() method before, unless each record has 500MB or more...

Categories