Use Linq Expression object inside and outside query - c#

I'm trying to accept a Func as a parameter to a function, and then use it both inside and outside a Linq query.
Here, idSelector is a Func of some kind which will return a particular SubLedger id in the Transaction object (e.g. t => t.SubLedger1).
public class Transaction {
public int SubLedger1 { get; set; }
public int SubLedger2 { get; set; }
public int SubLedger3 { get; set; }
public decimal Balance { get; set; }
}
public IEnumerable<Transaction> GetSubLedger(DateTime StartDate, Func<Transaction, int> idSelector) {
// simply returns IQueryable of all
DbSet<Transaction> txns = txnRepo.GetAll();
// get opening balance for each sub ledger
var subLedgers = txns.Where(t => t.Date < StartDate)
.GroupBy(idSelector, t => t, (id, txn) => new { ID = id, Balance = txn.Sum(t => t.Amount) })
.ToDictionary(t => t.ID, t => t.Balance);
// fill running balance
var filtered = txns.Where(t => t.Date >= StartDate).ToList();
foreach (var t in filtered)
{
t.Balance = subLedgers[idSelector.Invoke(t)].Balance += t.Amount;
}
return filtered;
}
I need to use idSelector in two places: first in the Linq query to group all transactions into subLedgers, and second to get the running balance for the particular subledger in the filtered results. I realize that Linq requires an Expression<...> instead, but I can't quite figure out how to Invoke that in the second context.
It's possible I'm going about this the wrong way, is there some other way I should try? This question may also be a little muddled, I did try to pare down the code sample as much as possible, so please ask me if anything is unclear.

Use Compile to get the invokeable method from the expression:
t.Balance = subLedgers[idSelector.Compile()(t)].Balance += t.Amount;
(Assuming idSelector is an Expression<Func<Transaction, int>>.)

Related

c# linq lambda The best way to fill the static List

I have a class inheriting from another class
I am doing a query from the database
How do I fill in the static List without loop using linq lambda
If he finds a lot of data. this will not be fast
I want to escape from loop
public class Currencys
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public long Curr_Id { get; set; }
[StringLength(50)]
public string Curr_Name { get; set; }
[StringLength(50)]
public string CentName { get; set; }
[StringLength(50)]
public string curr_abbrivation { get; set; }
[StringLength(50)]
public string en_curr_name { get; set; }
[StringLength(50)]
public string en_centname { get; set; }
}
public class test1 : Currencys
{
static List<test1> _currenciesList;
public static void Fill()
{
if (_currenciesList != null)
{
_currenciesList.Clear();
}
_currenciesList = new List<test1>();
using (var context = new ContextFormeDb())
{
var list = context.Currencies.ToList();
list.ForEach(o=>
{
test1 _test1 = new test1();
_test1.Curr_Id = o.Curr_Id;
_test1.Curr_Name = o.Curr_Name;
_test1.CentName = o.CentName;
_test1.curr_abbrivation = o.curr_abbrivation;
_test1.en_curr_name = o.en_curr_name;
_test1.en_centname = o.en_centname;
_currenciesList.Add(_test1);
});
}
}
}
Is there anything better than this? without loop
list.ForEach(o=>
{
test1 _test1 = new test1();
_test1.Curr_Id = o.Curr_Id;
_test1.Curr_Name = o.Curr_Name;
_test1.CentName = o.CentName;
_test1.curr_abbrivation = o.curr_abbrivation;
_test1.en_curr_name = o.en_curr_name;
_test1.en_centname = o.en_centname;
_currenciesList.Add(_test1);
});
Is there anything better than this? without loop?
It depends on what you would call better. Faster? Probably not. Not much anyway. Easier to read and understand, easier to test, to debug, to change, to reuse? Probably.
Without Loop? there must be a loop somewhere, but it can be hidden inside a LINQ statement.
Whenever you want to fetch items from a database using entity framework, and you don't want to update the fetched items, always use Select, and select only the properties that you plan to use. Don't fetch the complete items, nor use Include. This will cost you overhead that you will only use if you update the fetched data.
So instead of:
var result = dbContext.Schools
.Where(school => school.Name == "Hogwarts")
.Include(school => school.Students)
.ToList();
consider to use:
var result = dbContext.Schools
.Where(school => school.Name == "Hogwarts")
.Select(school => new
{
// Select only the properties that you plan to use
Id = school.Id,
Name = school.Name,
...
students = dbContext.Students
.Where(student => student.SchoolId == school.Id)
.Select(student => new
{
Id = student.Id,
Name = student.Name,
...
// not needed, you know the value
// SchoolId = student.SchoolId,
})
.ToList(),
})
.ToList();
It will prevent the transfer of properties that you won't use
It will prevent that the fetched data will be copied to DbContext.ChangeTracker.
If you don't put data that won't be changed in the ChangeTracker, then SaveChanges will be faster.
So in your case, your code would be easier to understand, easier to reuse, easier to test and debug, and without "for each" if you use Select:
var fetchedData = dbContext.Currencies
.Where(currency => ...) // if you don't want all currencies
.Select(currency => new
{
// Select only the properties that you plan to use:
Id = currency.Id,
Name = currency.Name,
...
})
.ToList();
I used an anonymous type (new without specifying a class). This way you won't have to create a "dummy" class. The advantage is that you just write the properties and you'll have the object, you even have an "equality by value". If in future you need to add or remove a property, just do it, without any problem, no need to change your dummy class.
Disadvantage: you can't use it outside the current block, and certainly not as a return value of a procedure.
So if you need it outside your procedure:
.Select(currency => new Test1
{
// Select only the properties that you plan to use:
Id = currency.Id,
Name = currency.Name,
...
})
.ToList(),
If two lists are of the same type , you can use AddRange.
if not and for any reason you need to map properties or its diffrent object type, i would suggest configure AutoMapper in your app and like this you can easily convert you List from Type A to Type B and after that use AddRange

Need to convert Left join SQL to linq query - help appreciated

Here is my code the issue I have is the less than comparison in the On clause ... Since Linq doesn't allow this .... Migrating down into the where clause wont work as I am comparing one of the fields to null.
Here is the sql query (THE a.UserID= is hardcoded for now)
SELECT A.Policy, A.Comments, A.EventDTTM, A.Status, A.Reason, A.FollowUp
FROM PP_PolicyActivity A
LEFT JOIN PP_PolicyActivity B
ON(A.Policy = B.Policy AND A.EventDTTM < B.EventDTTM)
WHERE A.UserID = 'Ixxxxxx'
AND B.EventDTTM IS NULL AND a.status = 'open - Pending'
order by A.EventDTTM DESC
I need the result set from the above query as an IEnumerable list to populate a view
I'm tasked with rebuilding an old VB ASP NET that has a set of standing production databases behind it ... i don't have the option of changing the db design. I connecting to the server and database and this query was going against a table on that database.. the model also reflects the layout of the actual table.
The problem is with A.EventDTTM < B.EventDTTM - I can't move this to the where clause as I also have to deal with B.EventDTTM IS NULL in the where clause.
I need to retool the query someway so that it is 'linq' friendly
public class PolicyActivityModel
{
public string Policy { get; set; }
public int PolicyID { get; set; }
public string Status { get; set; }
public string Reason { get; set; }
public string Comments { get; set; }
public DateTime EventDTTM { get; set; }
public string UserID { get; set; }
public DateTime FollowUp { get; set; }
}
Company policy prohibits me from showing the connection string.
I am extremely new to Linq, Any help greatly appreciated
thank you
You can use the navigation property after you get the policy from the database.
var policy = DbContext.First(x => x.Id == 1000);
var otherPolicies = policy.ConnectedPolicies.Where(p => ...);
It's weird being a self-join but this is the most direct translation to Linq:
var query = from leftPP in PP_PolicyActivity
join rightPP in PP_PolicyActivity
on new { Policy = leftPP.Policy, EventDTTM = leftPP.EventDTTM }
equals new { Policy = rightPP.Policy, EventDTTM = rightPP.EventDTTM }
into pp from joinedRecords.DefaultIfEmpty()
where leftPP.UserId == 1
&& leftPP.EventDTTM < rightPP.DTTM)
&& rightPP.EventDTTM == null
&& leftPP.status = "open - Pending"
select new
{
leftPP,
rightPP
}
I free typed this, without models or Intellisense, thus there might be some smaller errors.
You could add the order by in that clause, but it's also still an IQUeryable, so I'd leave it.
And then, to get a List of models:
var results = query.OrderByDescending(x => x.EventDTTM).ToList();
The actual join is lines 2,3,4 and 5. It's verbose and "backwards" from SQL, and most importantly uses anonymous types. Accessing indidual properties will look something like:
results[0].leftPP.PolicyId

Sorting by Children in Entity Framework doesn't return sorted list

I know that Entity framework doesn't support sort or filter in the children collections yet. What I thought is that first I get the data then use foreach loop to sort it. The result gives me an unsorted list. My goal is to get the Participants (any order) and CurrentHospitaliztions (order by id descending) which is a child of participants. The models and the query is below. Any help will be appriciated.
public class Participant
{
public int Id { get; set; }
.. other fields
public ICollection<CurrentHospitalization> CurrentHospitalizations { get; set; }
public Participant()
{
CurrentHospitalizations = new Collection<CurrentHospitalization>();
}
}
public class CurrentHospitalization
{
public int Id { get; set; }
.. other fields
public Participant Participant { get; set; }
public int ParticipantId { get; set; }
}
The query that I use:
public async Task<IEnumerable<Participant>> GetList()
{
var participants = await context.Participants
.Include(x => x.CurrentHospitalizations)
.ToListAsync();
foreach (var p in participants )
{
var s = p.CurrentHospitalizations;
foreach (var q in s)
{
s.OrderByDescending(u => u.Id);
}
}
return participants ;
}
You sorted the right piece, in the wrong place, and then didn't really do anything with it. You don't need the nested iteration, you can just do it from the single foreach loop like this:
foreach (var p in participants)
p.CurrentHospitalizations = p.CurrentHospitalizations.OrderByDescending( ch => ch.Id ).ToList();
What you ahve here is a failure to understand the basics of LINQ and not reading the manual.
var s = p.CurrentHospitalizations;
foreach (var q in s)
{
s.OrderByDescending(u => u.Id);
}
does effectively nothing else than waste processor time.
You make a variable s. You assign to it the current hospuzatlizations, not sorted.
Then you call OrderByDescending - generating an expression that you COULD execute, except you never DO execute it. So, you created some object tree and - throw it away.
o.CurrentHospitalizations = s.OrderByDescending(u => u.Id).ToList()
would assign s to be a list and execute it. The ToList() is missing - as well as the assigning it, so the sorted result is not just thrown away.
Those are LINQ basics - orderby etc. are not changing order, they return an ordered result, and you must materialize it.

Entity Framework 6.1: navigation properties not loading

This is my first time using Entity Framework 6.1 (code first). I keep running into a problem where my navigation properties are null when I don't expect them to be. I've enabled lazy loading.
My entity looks like this:
public class Ask
{
public Ask()
{
this.quantity = -1;
this.price = -1;
}
public int id { get; set; }
public int quantity { get; set; }
public float price { get; set; }
public int sellerId { get; set; }
public virtual User seller { get; set; }
public int itemId { get; set; }
public virtual Item item { get; set; }
}
It has the following mapper:
class AskMapper : EntityTypeConfiguration<Ask>
{
public AskMapper()
{
this.ToTable("Asks");
this.HasKey(a => a.id);
this.Property(a => a.id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
this.Property(a => a.id).IsRequired();
this.Property(a => a.quantity).IsRequired();
this.Property(a => a.price).IsRequired();
this.Property(a => a.sellerId).IsRequired();
this.HasRequired(a => a.seller).WithMany(u => u.asks).HasForeignKey(a => a.sellerId).WillCascadeOnDelete(true);
this.Property(a => a.itemId).IsRequired();
this.HasRequired(a => a.item).WithMany(i => i.asks).HasForeignKey(a => a.itemId).WillCascadeOnDelete(true);
}
}
Specifically, the problem is that I have an Ask object with a correctly set itemId (which does correspond to an Item in the database), but the navigation property item is null, and as a result I end up getting a NullReferenceException. The exception is thrown in the code below, when I try to access a.item.name:
List<Ask> asks = repo.GetAsksBySeller(userId).ToList();
List<ReducedAsk> reducedAsks = new List<ReducedAsk>();
foreach (Ask a in asks)
{
ReducedAsk r = new ReducedAsk() { id = a.id, sellerName = a.seller.username, itemId = a.itemId, itemName = a.item.name, price = a.price, quantity = a.quantity };
reducedAsks.Add(r);
}
Confusingly, the seller navigation property is working fine there, and I can't find anything I've done differently in the 'User' entity, nor in its mapper.
I have a test which recreates this, but it passes without any problems:
public void canGetAsk()
{
int quantity = 2;
int price = 10;
//add a seller
User seller = new User() { username = "ted" };
Assert.IsNotNull(seller);
int sellerId = repo.InsertUser(seller);
Assert.AreNotEqual(-1, sellerId);
//add an item
Item item = new Item() { name = "fanta" };
Assert.IsNotNull(item);
int itemId = repo.InsertItem(item);
Assert.AreNotEqual(-1, itemId);
bool success = repo.AddInventory(sellerId, itemId, quantity);
Assert.AreNotEqual(-1, success);
//add an ask
int askId = repo.InsertAsk(new Ask() { sellerId = sellerId, itemId = itemId, quantity = quantity, price = price });
Assert.AreNotEqual(-1, askId);
//retrieve the ask
Ask ask = repo.GetAsk(askId);
Assert.IsNotNull(ask);
//check the ask info
Assert.AreEqual(quantity, ask.quantity);
Assert.AreEqual(price, ask.price);
Assert.AreEqual(sellerId, ask.sellerId);
Assert.AreEqual(sellerId, ask.seller.id);
Assert.AreEqual(itemId, ask.itemId);
Assert.AreEqual(itemId, ask.item.id);
Assert.AreEqual("fanta", ask.item.name);
}
Any help would be extremely appreciated; this has been driving me crazy for days.
EDIT:
The database is SQL Server 2014.
At the moment, I have one shared context, instantiated the level above this (my repository layer for the db). Should I be instantiating a new context for each method? Or instantiating one at the lowest possible level (i.e. for every db access)? For example:
public IQueryable<Ask> GetAsksBySeller(int sellerId)
{
using (MarketContext _ctx = new MarketContext())
{
return _ctx.Asks.Where(s => s.seller.id == sellerId).AsQueryable();
}
}
Some of my methods invoke others in the repo layer. Would it better for each method to take a context, which it can then pass to any methods it calls?
public IQueryable<Transaction> GetTransactionsByUser(MarketContext _ctx, int userId)
{
IQueryable<Transaction> buyTransactions = GetTransactionsByBuyer(_ctx, userId);
IQueryable<Transaction> sellTransactions = GetTransactionsBySeller(_ctx, userId);
return buyTransactions.Concat(sellTransactions);
}
Then I could just instantiate a new context whenever I call anything from the repo layer: repo.GetTransactionsByUser(new MarketContext(), userId);
Again, thanks for the help. I'm new to this, and don't know which approach would be best.
Try to add
Include call in your repository call:
public IQueryable<Ask> GetAsksBySeller(int sellerId)
{
using (MarketContext _ctx = new MarketContext())
{
return _ctx.Asks
.Include("seller")
.Include("item")
.Where(s => s.seller.id == sellerId).AsQueryable();
}
}
Also, there is an extension method Include which accepts lambda expression as parameter and provides you type checks on compile time
http://msdn.microsoft.com/en-us/data/jj574232.aspx
As for the context lifespan, your repositories should share one context per request if this is a web application. Else it's a bit more arbitrary, but it should be something like a context per use case or service call.
So the pattern would be: create a context, pass it to the repositories involved in the call, do the task, and dispose the context. The context can be seen as your unit of work, so no matter how many repositories are involved, in the end one SaveChanges() should normally be enough to commit all changes.
I can't tell if this will solve the lazy loading issue, because from what I see I can't explain why it doesn't occur.
But although if I were in your shoes I'd like to get to the bottom of it, lazy loading is something that should not be relied on too much. Take a look at your (abridged) code:
foreach (Ask a in asks)
{
ReducedAsk r = new ReducedAsk()
{
sellerName = a.seller.username,
itemName = a.item.name
};
If lazy loading would work as expected, this would execute two queries against the database for each iteration of the loop. Of course, that's highly inefficient. That's why using Include (as in Anton's answer) is better anyhow, not only to circumvent your issue.
A further optimization is to do the projection (i.e. the new {) in the query itself:
var reducedAsks = repo.GetAsksBySeller(userId)
.Select(a => new ReducedAsk() { ... })
.ToList();
(Assuming – and requiring – that repo.GetAsksBySeller returns IQueryable).
Now only the data necessary to create ReducedAsk will be fetched from the database and it prevents materialization of entities that you're not using anyway and relatively expensive processes as change tracking and relationship fixup.

I need help with using linq

I want to filter the objects that I have by their topic.
I have many topics: Arts, Economics, Business, Politics. Each topic is a property within the object that I try to classify from a list of those objects.
Here is part of my objects:
public class AllQuestionsPresented
{
public string Name{ get; set; }
public string ThreadName { get; set; }
public string Topic { get; set; }
public string Subtopic { get; set; }
public int Views { get; set; }
public int Replies { get; set; }
public int PageNumber { get; set; }
public DateTime Time { get; set; }
// snip
I created many of those objects feed their properties with different values and put them into a List:
List<AllQuestionsPresented> forumData;
Now I want to group them all into linq by their topics..
var groupedByPages =
from n in forumData
group n by forumData
select .....
Basically i dont know how to continue cause i am not used to deal with linq.. what i want to get is some dictionary..
Dictionary<string,AllQuestionsPresented> dictionary..
If i dont use linq, and add to a dictionary every topic, it will put several "AllQuestionsPresented" objects with the same topic..which will throw an exception..so i have to use group by..but dont know how to achieve that manipulation
You can use ToLookup, which will give you a key/list of values collection. Your key will be the Topic, and you will get a list of AllQuestionsPresented for each key.
var lookup = forumData.ToLookup(f => f.Topic);
Reference on ToLookup
var groupedByTopics =
from n in forumData
group n by forumData.Topic into g
select new { Topic = forumData.Topic, Questions = g }
You may also want to keep this around for reference :-)
http://msdn.microsoft.com/en-us/vcsharp/aa336746
The grouped results are returned as an IEnumerable<IGrouping<TKey, T>>, which in your case will be IEnumerable<IGrouping<string, AllQuestionsPresented>>.
The code below shows how you can access the data in the grouping.
var groupedByTopic = from question in forumData
group question by question.Topic;
foreach (var group in groupedByTopic)
{
Console.WriteLine(group.Key);
foreach (var question in group)
{
Console.WriteLine("\t" + question.Name);
}
}
To create a dictionary from the above you can do the following
var groupingDictionary = groupedByTopic.ToDictionary(q=>q.Key, q=>q.ToList());
Which will give you a Dictionary<string, List<AllQuestionsPresented>>
If you went the LookUp route, which is nicely demonstrated by #wsanville
, then you can get the dictionary the same way
var lookup = forumData.ToLookup(q => q.Topic);
var groupingDictionary = lookup.ToDictionary(q => q.Key, q => q.ToList());
You can just call ToDictionary. The parameters are a function to select the keys and another to select the values:
var groupedByPages =
from n in forumData
group n by n.Topic;
IDictionary<string, IEnumerable<AllQuestionsPresented>> dictionary =
groupedByPages.ToDictionary(x => x.Key, x => x.AsEnumerable());
But if all you need from the IDictionary interface is the indexing operation, it's easier to just use a ILookup:
ILookup<string, AllQuestionsPresented> groupedByPages = forumData.ToLookup(x => x.Topic);
var groupedByPages =
from n in forumData
group n by forumData.Topic
select n;

Categories

Resources