Optimizing simple usage of Linq in C# - c#

I have replicated a stripped-down version of my code that has recently been re-written to use linq to access the database.
However, in my opinion, the linq is really simple and could probably be optimized quite a bit, especially around line 90 where there is a linq statement inside a foreach loop.
It'd be really helpful to see how someone else would go about writing this simple task using linq. Thanks in advance! Below is a snippet of my source code.
// Model objects - these are to be populated from the database,
// which has identical fields and table names.
public class Element
{
public Element()
{
Translations = new Collection<Translation>();
}
public int Id { get; set; }
public string Name { get; set; }
public Collection<Translation> Translations { get; set; }
public class Translation
{
public int Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public Language Lang { get; set; }
}
}
public class Language
{
public int Id { get; set; }
public string Name { get; set; }
public string Code { get; set; }
}
// Stripped-down functions for adding and loading Element
// objects to/from the database:
public static class DatabaseLoader
{
// Add method isn't too bulky, but I'm sure it could be optimised somewhere.
public static void Add(string name, Collection<Translation> translations)
{
using (var db = DataContextFactory.Create<ElementContext>())
{
var dbElement = new Database.Element()
{
Name = name
};
db.Elements.InsertOnSubmit(dbElement);
// Must be submit so the dbElement gets it's Id set.
db.SubmitChanges();
foreach (var translation in translations)
{
db.Translations.InsertOnSubmit(
new Database.Translation()
{
FK_Element_Id = dbElement.Id,
FK_Language_Id = translation.Lang.Id,
Title = translation.Title,
Content = translation.Content
});
}
// Submit all the changes outside the loop.
db.SubmitChanges();
}
}
// This method is really bulky, and I'd like to see the many separate linq
// calls merged into one clever statement if possible (?).
public static Element Load(int id)
{
using (var db = DataContextFactory.Create<ElementContext>())
{
// Get the database object of the relavent element.
var dbElement =
(from e in db.Elements
where e.Id == id
select e).Single();
// Find all the translations for the current element.
var dbTranslations =
from t in db.Translations
where t.Fk_Element_Id == id
select t;
// This object will be used to buld the model object.
var trans = new Collection<Translation>();
foreach (var translation in dbTranslations)
{
// Build up the 'trans' variable for passing to model object.
// Here there is a linq statement for EVERY itteration of the
// foreach loop... not good (?).
var dbLanguage =
(from l in db.Languages
where l.Id == translation.FK_Language_Id
select l).Single();
trans.Add(new Translation()
{
Id = translation.Id,
Title = translation.Title,
Content = translation.Content,
Language = new Language()
{
Id = dbLanguage.Id,
Name = dbLanguage.Name,
Code = dbLanguage.Code
}
});
}
// The model object is now build up from the database (finally).
return new Element()
{
Id = id,
Name = dbElement.Name,
Translations = trans
};
}
}
}

Using some made-up constructors to oversimplify:
public static Element Load(int id)
{
using (var db = DataContextFactory.Create<ElementContext>())
{
var translations = from t in db.Translations
where t.Fk_Element_Id == id
join l in db.Languages on t.FK_Language_Id equals l.Id
select new Translation(t, l);
return new Element(db.Elements.Where(x => x.Id == id).Single(), translations);
}
}

First thing I don't like in here is all the "new Translation() { bla = bla } because they're big blocks of code, I would put them in a method where you hand them the objects and they return the new for you.
Translations.InsertOnSubmit(CreateDatabaseTranslation(dbElement, translation));
and
trans.Add(CreateTranslationWithLanguage(translation, dbLanguage));
etc, wherever you have code like this, it just muddles the readability of what you're doing in here.

Related

Get parent object plus its child count without anonymous type

So I have the next method (which works) to return a list of claims plus its observations. One claim can have zero-or-many observations. Code works but I'm afraid its a mess, with the anonymous type and then parsing it into a new Claim type, setting the count.
public async Task<IEnumerable<Claim>> GetClaims(ClaimStatusCode status, int take = 10, int skip = 0)
{
using (var db = new DataContext())
{
var pendingclaims = await (from claim in db.Claims
where claim.OfficeCode == _officeCode
where claim.ClaimStatusCode == status
select new
{
ID = claim.ID,
ClaimStatusCode = claim.ClaimStatusCode,
OpenDate = claim.OpenDate,
LastUpdateDate = claim.LastUpdateDate,
CloseDate = claim.CloseDate,
ProductCode = claim.ProductCode,
IssueCode = claim.IssueCode,
SpecificIssueCode = claim.SpecificIssueCode,
OfficeCode = claim.OfficeCode,
Summary = claim.Summary,
ObservationsCount = claim.Observations.Count
}).OrderBy(c => c.OpenDate).Take(take).Skip(skip).ToListAsync();
var list = new List<Claim>();
foreach (var claim in pendingclaims)
{
Claim c = new Claim()
{
ID = claim.ID,
ClaimStatusCode = claim.ClaimStatusCode,
OpenDate = claim.OpenDate,
LastUpdateDate = claim.LastUpdateDate,
CloseDate = claim.CloseDate,
ProductCode = claim.ProductCode,
IssueCode = claim.IssueCode,
SpecificIssueCode = claim.SpecificIssueCode,
OfficeCode = claim.OfficeCode,
Summary = claim.Summary,
ObservationsCount = claim.ObservationsCount
};
list.Add(c);
}
return list;
}
}
I think maybe I'm missing something to reduce the mess of the resulting SQL query, but don't figure what. Any idea?
UPDATE
As requested, here's the Claim and Observation class, I'm using a plain simple Entity Code First One to Many relationship:
Claim
public class Claim
{
public Claim()
{
Observations = new List<Observation>();
}
[Key]
public Guid ID { get; set; }
...
public virtual ICollection<Observation> Observations { get; set; }
[NotMapped]
public int ObservationsCount { get; set; }
}
Observation
public class Observation
{
public Observation()
{ }
[Key]
public Guid ID { get; set; }
...
public virtual Guid ClaimID { get; set; }
[ForeignKey("ClaimID")]
public virtual Claim Claim { get; set; }
}
There is no way in EF6 to get what you want without some intermediate projection (being it anonymous type or concrete type, as soon as it's not an entity type). But if you need all the object fields plus child count, you can simplify the implementation like this:
var pendingclaims = await (from claim in db.Claims.AsNoTracking()
where claim.OfficeCode == _officeCode
where claim.ClaimStatusCode == status
orderby claim.OpenDate
select new
{
claim,
ObservationsCount = claim.Observations.Count
}).Take(take).Skip(skip).ToListAsync();
return pendingclaims.Select(item =>
{
item.claim.ObservationsCount = item.ObservationsCount;
return item.claim;
}).ToList();

C# - The entity or complex type cannot be constructed in a LINQ to Entities query

I'm trying to send data with a view and print it out, but I'm struggling really hard since I'm very new to C#.
So here's my model ViewModel:
namespace P104.Models
{
public class ViewModel
{
}
public class Location
{
public int loc_id { get; set; }
public string loc_name { get; set; }
}
public class Competentie
{
public int Comp_id { get; set; }
public string competentie { get; set; }
}
public class MyViewModel
{
public User User { get; set; }
public IEnumerable<Locations> Locations { get; set; }
public IEnumerable<Competenties> Competenties { get; set; }
}
}
This is the function I have in the controller
public ActionResult Details(int? id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
User user = db.user.Find(id);
if (user == null)
{
return HttpNotFound();
}
var competenties =
from usercomp in dbE.UserComp
join comp in dbE.Competenties on usercomp.fk_Comp_id equals comp.comp_id
where usercomp.fk_user_id == id
select new Competenties { competentie = comp.competentie };
var locations =
from userloc in dbE.UserLoc
join loc in dbE.Locations on userloc.fk_location_id equals loc.loc_id
where userloc.fk_user_id == id
select new Locations { loc_name = loc.loc_name };
var model = new MyViewModel
{
User = user,
Locations = locations.ToList(), // eagerly fetch the data that will be needed in the view
Competenties = competenties.ToList(), // eagerly fetch the data that will be needed in the view
};
return View(model);
}
And he's how I try to print it out in the view:
#foreach (var location in Model.Locations)
{
<dt>#location.locname</dt>
<dd>#location.locname</dd>
}
#foreach (var competentie in Model.Competenties)
{
<dt>#competentie.competentie</dt>
<dd>#competentie.competentie</dd>
}
I always recevie this error
The entity or complex type 'P104.Models.Locations' cannot be
constructed in a LINQ to Entities query.
I've found a few solutions, but I'm struggling to apply them to my code so they don't work.
Thanks in advance for your help
You seem to have got a typo here:
select new Locations { loc_name = loc.loc_name };
This should be:
select new Location { loc_name = loc.loc_name };
The model you are projecting against is called Location, not Locations. It's unclear what the Locations model is since you haven't shown that in your question.
And of course adapt your view accordingly:
#foreach (var location in Model.Locations)
{
<dt>#location.loc_name</dt>
<dd>#location.loc_name</dd>
}
By the way I will recommend you following standard C# naming conventions. This convention dictates that in C# a property name should start with an uppercase letter and not contain _. So basically you would rather use LocationName or just Name instead of loc_name. Same remark for your Competentie model.

LINQ to Objects Performance - Huge dataset for long running process

I have a detail list (200,000 records) which was pulled from database and I need to find the locations for each detail and below is the code which is looping through the detail list and assigning location to the list. This loop is taking more than 15 minutes to execute but if don’t populate the Locations property then it takes less than a minute.
How can I optimize this code?
class Program
{
static void Main(string[] args)
{
List<Details> databaseDetailList = GetDetailsFromdatabase();
List<Location1> databaseLocation1List = GetLocations1Fromdatabase();
List<Location2> databaseLocation2List = GetLocations2Fromdatabase();
List<Details> detailList = new List<Details>();
foreach (var x in databaseDetailList)
{
detailList.Add(new Details
{
DetailId = x.DetailId,
Code = x.Code,
//If I comment out the Locations then it works faster
Locations = new LocationIfo {
Locations1 = databaseLocation1List
.Where(l=>l.DetailId == x.DetailId && l.Code == x.Code).ToList(),
Locations2 = databaseLocation2List
.Where(l => l.DetailId == x.DetailId && l.Code == x.Code).ToList()
}
});
}
}
private static List<Details> GetDetailsFromdatabase()
{
//This returns 200,000 records from database
return new List<Details>();
}
private static List<Location1> GetLocations1Fromdatabase()
{
//This returns 100,000 records from database
return new List<Location1>();
}
private static List<Location2> GetLocations2Fromdatabase()
{
//This returns 100,000 records from database
return new List<Location2>();
}
}
public class Details
{
public string DetailId { get; set; }
public string Code { get; set; }
public LocationIfo Locations { get; set; }
}
public class LocationIfo
{
public List<Location1> Locations1 { get; set; }
public List<Location2> Locations2 { get; set; }
}
public class Location1
{
public int LocationId { get; set; }
public string DetailId { get; set; }
public string Code { get; set; }
public string OtherProperty { get; set; }
}
public class Location2
{
public int LocationId { get; set; }
public string DetailId { get; set; }
public string Code { get; set; }
public string OtherProperty { get; set; }
}
What you're doing conceptually here is a Join. Using the proper operations will ensure that it executes much more effectively. Ideally you would even be doing the Join on the database side of things, rather than after pulling all of the data down into lists, but even if you do pull all of the data down, joining it in memory using Join will be much more efficient.
var query = from detail in databaseDetailList
join location1 in databaseLocation1List
on new { detail.DetailId, detail.Code }
equals new { location1.DetailId, location1.Code }
into locations1
join location2 in databaseLocation2List
on new { detail.DetailId, detail.Code }
equals new { location2.DetailId, location2.Code }
into locations2
select new Details
{
Code = detail.Code,
DetailId = detail.DetailId,
Locations = new LocationIfo
{
Locations1 = locations1.ToList(),
Locations2 = locations2.ToList(),
}
};
This is a very similar problem I faced a couple of days ago (in Ruby).
Finally the best solution I found was to convert the List(or Array) into a Dictionary(or Hash)
Create a Dictionary with your searching fields concatenated as a key and your list item as as value:
var dict1 = databaseLocation1List.ToDictionary(x => x.DetailId.ToString() + x.Code.ToString(), x);
And then the search in the Dictionary by key will be very fast:
Locations1 = dict1[x.DetailId.ToString() + x.Code.ToString()].ToList()
This is a bit hacky - but you could use lookups:
If it's the look-up portion of your code that is taking the longest - and you can't change the database code, as suggested with a Join - you could consider indexing your Location return-types in an in-memory Lookup.
Build a Lookup key, that's a combination of your two values - this will need to be specifically unique, dependent on your requirements.
DetailId-Code - "123123-343"
private static ILookup<int, Location1> GetLocations1Fromdatabase()
{
//This returns 100,000 records from database
return new List<Location2>()
.ToLookup(l => l.DetailId + "-" + l.Code);
}
Then:
Locations1 = databaseLocation1List[l.DetailId + "-" + l.Code].ToList()

Linq "join" with a IList<T> getting "Error Unable to create a constant value.."

I have a Save Method that saves with a Linq query a manually re-orderd list (in a web form) that is passed as the parameter to my method, and I try to update the Order Property of the IEnumerable<VM_CategoryLabel> I retrieve from the database (EF) with the corresponding value in the list (maybe would that be clearer with my code below):
public static void SaveFromList(IList<VM_CategoryLabelExtra> listTemplate)
{
int idCat = listTemplate.Select(x => x.IdCat).FirstOrDefault();
var test = (int)listTemplate.Where(z => z.Id == 8).Select(z => z.Order).FirstOrDefault();
using (var context = new my_Entities())
{
var requete = from x in context.arc_CatLabel
where x.ID_Categorie == idCat
orderby x.Sequence_Cat
select new VM_CategoryLabel
{
Id = x.ID_LabelPerso,
//Order = x.Sequence_Cat,
Order = (int)listTemplate.Where(z => z.Id == x.ID_LabelPerso).Select(z => z.Order).First(),
Label = x.arc_Label.Label,
Unit = x.arc_Label.Unit
};
context.SaveChanges();
}
}
I used the "test" var to see if my "sub-query" gets the correct value, and it does, but when I use my Linq expression inside the Select (the commented Order line), I get the following error:
Unable to create a constant value of type 'Namespace.Models.VM_CategoryLabelExtra. "Only primitive types and enumeration types are supported in this context.
Here are my classes:
public class VM_CategoryLabel
{
public int Id { get; set; }
public int Order { get; set; }
public string Label { get; set; }
public string Unit { get; set; }
public bool Checked { get; set; }
}
public class VM_CategoryLabelExtra
{
public int Id { get; set; }
public int IdCat { get; set; }
public int Order { get; set; }
public string Label { get; set; }
public string Unit { get; set; }
public bool Checked { get; set; }
}
So I suppose that I should not query the list inside my query ? So how do I "match" the 2 lists of values ?
I also tried the following (after having replace in the Linq query: Order = x.Sequence_Cat)that is not working neither because the iteration variable is
read-only:
foreach (var item in requete)
{
item.Order = listTemplate.Where(x => x.Id == item.Id).Select(x => x.Order).FirstOrDefault();
}
try
{
context.SaveChanges();
I suggest using this.
It is the let clause.
public static void SaveFromList(IList<VM_CategoryLabelExtra> listTemplate)
{
int idCat = listTemplate.Select(x => x.IdCat).FirstOrDefault();
var test = (int)listTemplate.Where(z => z.Id == 8).Select(z => z.Order).FirstOrDefault();
using (var context = new my_Entities())
{
var requete = from x in context.arc_CatLabel
where x.ID_Categorie == idCat
orderby x.Sequence_Cat
let list = listTemplate
select new VM_CategoryLabel
{
Id = x.ID_LabelPerso,
Order = list.Where(z => z.Id == x.ID_LabelPerso).Select(z => z.Order).First(),
Label = x.arc_Label.Label,
Unit = x.arc_Label.Unit
};
context.SaveChanges();
}
}
edit: instead offrom you can just do let list = listTemplate
Should work now :)
example for let:
// The let keyword in query expressions comes in useful with subqueries: it lets
// you re-use the subquery in the projection:
from c in Customers
let highValuePurchases = c.Purchases.Where (p => p.Price > 1000)
where highValuePurchases.Any()
select new
{
c.Name,
highValuePurchases
}
If you do not know how Let working than please download LinqPad and see an example

How to select all the values of an object's property on a list of typed objects in .Net with C#

Ugh, how do I explain this one... Probably a simple question but my mind is fried.
Suppose I have this class:
public class NestedObject
{
public string NestedName { get; set; }
public int NestedIntValue { get; set; }
public decimal NestedDecimalValue { get; set; }
}
public class SomeBigExternalDTO
{
public int Id { get; set; }
public int UserId { get; set; }
public int SomeIntValue { get; set; }
public long SomeLongValue { get; set; }
public decimal SomeDecimalValue { get; set; }
public string SomeStringValue { get; set; }
public NestedObject SomeNestedObject { get; set; }
// ... thousands more of these properties... inherited code
}
And the class I'd like to populate is here:
public class MyResult
{
public int UserId { get; set; } // user id from above object
public string ResultValue { get; set; } // one of the value fields from above with .ToString() executed on it
}
What I'd like to do is create a helper to return the property values (a cross section is the best way I could describe it I guess) of all instances in a list of this object:
var foo = new List<SomeBigExternalDTO>();
foo = GetMyListOfSomeBigExternalDTO();
public static List<MyResult> AwesomeHelper(List<SomeBigExternalDTO> input, SearchableProperty thePropertyIWant)
{
// some magic needs to happen here...
}
The tricky part here is I want to dynamically pass in the property based on a link selector (I have no clue how to do this):
var output = AwesomeHelper(GetMyListOfSomeBigExternalDTO(), x => x.SomeIntValue);
var output2 = AwesomeHelper(GetMyListOfSomeBigExternalDTO(), x => x.SomeNestedObject.NestedIntValue);
And this should return a list of MyResult objects with the UserId and SomeIntValue.ToString() corresponding to each item in the input list.
Wow, I really hope this makes sense. Please let me know if this is not clear I'll provide more details. I'm really hoping this is something baked into the libraries that I've overlooked.
Any ideas on I'd accomplish this?
You could implement it as an extension method:
public static IEnumerable<MyResult> AwesomeHelper(this IEnumerable<SomeBigExternalDTO> input,
Func<SomeBigExternalDTO, int> intMapper)
{
foreach (var item in input)
yield return new MyResult()
{
UserId = item.UserId,
ResultValue = intMapper(item)
};
}
Now you can use it like this:
var output = GetMyListOfSomeBigExternalDTO().AwesomeHelper( x => x.SomeIntValue);
var output2 = GetMyListOfSomeBigExternalDTO().AwesomeHelper( x => x.SomeNestedObject.NestedIntValue);
Having said that - dont' do that - it somehow looks like you are reinventing what Linq already offers you, you can do just the same using only Linq:
var output = GetMyListOfSomeBigExternalDTO().Select( x=> new MyResult()
{
UserId = item.UserId,
ResultValue = x.SomeIntValue
});
var output2 = GetMyListOfSomeBigExternalDTO().Select( x=> new MyResult()
{
UserId = item.UserId,
ResultValue = x.SomeNestedObject.NestedIntValue
});
Often when trying to create a general purpose list operator you end up reimplementing what LINQ already offers you.
Here's the LINQ code for what you're after (without an AwesomeHelper function):
var results = list.Select(l => new MyResult()
{
UserId = l.UserId,
ResultValue = l.SomeDecimalValue.ToString()
}).ToList();
Fairly simple.
If you want to have an AwesomeHelper function as you requested then it looks like this:
public static List<MyResult> AwesomeHelper(
List<SomeBigExternalDTO> input,
Func<SomeBigExternalDTO, object> selector)
{
return input
.Select(i => new MyResult()
{
UserId = i.UserId,
ResultValue = selector(i).ToString()
})
.ToList();
}
And the calling code look like this:
var results = AwesomeHelper(list, x => x.SomeIntValue);
To me, though, this is now less readable than the LINQ option. Now there is some magic being wrought and it's hard to work out what.
I have an alternative that will give you the best of both worlds.
First, define an extension method called ToMyResult that maps a single SomeBigExternalDTO instance into a single MyResult with a field selector, like this:
public static class AwesomeHelperEx
{
public static MyResult ToMyResult(
this SomeBigExternalDTO input,
Func<SomeBigExternalDTO, object> selector)
{
return new MyResult()
{
UserId = input.UserId,
ResultValue = selector(input).ToString()
};
}
}
Now the calling code is crystal clear, flexible and concise. Here it is:
var results = (
from item in list
select item.ToMyResult(x => x.SomeLongValue)
).ToList();
I hope this helps.

Categories