EF code first iterates over all properties while creating object - c#

I use EF Code First in my application and have the following class:
[Table("TBL_XYZ")]
public class XYZ
{
[Required]
public string PropA { get; set; }
[Required]
public int PropB { get; set; }
public int FormulaA
{
get
{
return PropB * Math.PI / 100;
}
}
}
This is how I get the data from the database:
var data = (from e in db.XYZ where e.PropB < 100 select e).ToList();
After I added some more fields which do calculations and don't have a set accessor (like FormulaA), I realized a drop in performance when executing the above line.
After some debugging I found out that EF iterates over all Properties. It calls all get-functions of the properties, while creating the object, even if I don't access them.
What is the purpose of this behaviour and is there a workaround. Does this maybe have something to do with keeping track of changes?
It is really convenient for me to have my formulas in the object itself, but right now it severely affects the performance.

You can try to add
[NotMapped]
public int FormulaA ....
And also in query select only properties what you really need.
select new {e.PropA, e.PropB}

Related

Automapper flattening not behaving as expected

I'm trying to flatted out a nested object. All of the documentation and stack threads seem to suggest the solution is very simple, but it just isn't working.
Consider the following (simplified) objects:
public class CRouteHold
{
public int HoldId { get; set; }
public virtual Hold? Hold { get; set; }
public bool? fhandstart { get; set; }
public bool? ffootstart { get; set; }
public bool? rhandstart { get; set; }
public bool? rfootstart { get; set; }
}
public class Hold
{
public double? xpos { get; set; }
}
and then a flat Dto I want to map to:
public record CRouteHoldDto(
int HoldId,
bool fhandstart,
bool ffootstart,
bool rhandstart,
bool rfootstart,
double xpos
);
Now, I've tried a bunch of mapping profiles, some work and some don't. The one I want to work and which seems like it should work doesn't. Here's what I've tried.
Simple unflatted setup: If I comment out the xpos on the dto and just map the 'root' layer of the RouteHold object like so:
CreateMap<CRouteHold, CRouteHoldDto>();
This works, and gives a CRouteHoldDto.
Flattening setup using a naming convention - if I change the name of xpos on the dto to HoldXpos (camelcase of ObjectProperty) then automapper figures it out and maps the xpos from the child Hold on CRouteHold to the HoldXpos property. The mapping profile is the same as above. Nothing needs to change.
Flatting without naming convention, using ForMember - I don't want the long HoldXpos name on my Dto. I just want xpos, the same as on the child Hold. To do this, the documentation and a myriad of Stack threads seem to suggest I can use a mapping profile like this:
CreateMap<CRouteHold, CRouteHoldDto>()
.ForMember(rh => rh.xpos, m => m.MapFrom(rh => rh.Hold.xpos));
However, it just doesn't seem to work. It throws an error about needing an empty constructor or optional args.
Am I missing something here?
Edit: From comments I also tried using the IncludeMembers by creating an additional map from Hold to CRouteHoldDto:
CreateMap<CRouteHold, CRouteHoldDto>()
.IncludeMembers(s => s.Hold);
CreateMap<Hold, CRouteHoldDto>(MemberList.None);
Unfortuantely, the result is the same.
SOLUTION
Thanks to #LucianBargaoanu. For those in the future, the exact mapping profile now looks like this:
CreateMap<CRouteHold, CRouteHoldDto>()
.ForCtorParam("xpos", m => m.MapFrom(s => s.Hold.xpos))
.ForCtorParam("otherproperty", m => m.MapFrom(s => s.Hold.otherproperty))
So just keep adding on the ForCtorParam for each property you want to map.
You need ForCtorParam instead of ForMember. A record is immutable, so in fact you're doing constructor mapping.
See https://docs.automapper.org/en/latest/Construction.html.

Avoiding transposed parameters without creating coupling

I'm currently building a test application that manages parts for engineers, and I've hit a snag. I have a few different classes, including PartsModel and EngineerModel, and I want to update a list of parts that an engineer has, but I'm mindful of issues from either transposed parameters or from structuring the code in a way that unnecessarily couples to a particular class.
The two classes, with some relevant properties:
public class PartModel
{
public int PartId { get; private set; }
public string PartTitle { get; set; }
public string PartDescription { get; set; }
public int Quantity { get; set; }
public int MinimumStock { get; set; }
public void AddToStock (int quantityToAdd) {
Quantity += quantityToAdd;
}
public void RemoveFromStock (int quantityToRemove) {
Quantity -= quantityToRemove;
CheckMinimumStock();
}
}
public class EngineerModel
{
public int EngineerId { get; private set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public List<PartModel> PartsInStock { get; set; } = Factory.CreatePartsList();
}
As you can see, each engineer has a list of parts they have in stock via a List<PartModel>. I want to pass another list to this one so that I can update it respectively (incrementing or decrementing quantities, and then adding or removing parts to the list as necessary).
The first warning bell is that it takes two inputs of the same type, and is going to fill one from the other one (which isn't needed afterwards), so you're essentially modifying one input and destroying the other. To me, this presents a danger of the inputs getting transposed and the wrong list being either returned or updated (depending on whether it returns or just acts on the list). Because it removes items that have no quantity, it can't check the list length and just update the longer one, because there are possible cases where the engineer's list is shorter (maybe they're a new engineer, or maybe they just had a large shipment of parts sent when they were running low on stock). If it did just keep parts with quantity zero, then you're threatening scalability of both engineers and parts (not to mention any other objects that use the same operation).
So, put it as a method in the EngineerModel class and operate on PartsInStock, right? But what about when I want to use the same operation on other classes (e.g. if I have a list of parts associated to a work task)? Then I extract the method out to another class and... I'm passing the two lists as parameters in the method, so I'm back to where I was.
Am I being reasonable in not wanting to have two parameters of the same type, and how do I structure the code to deal with this, but without creating unnecessary coupling? If I'm not being reasonable, what am I overlooking?
Use an extension method
Thanks to #DavidBrowne-Microsoft for clarifying this. By defining an extension method for List<PartModel>, it only needs the one parameter - the list containing the updates (foreach below based on #Valentin's answer to this question).
public static class PartsHandler
{
public static List<PartModel> UpdateStockQuantitiesWith(this List<PartModel> stockToBeUpdated, List<PartModel> stockUpdates) {
foreach ( var part in stockUpdates )
{
var partToBeUpdated = stockToBeUpdated.FirstOrDefault(x => x.PartId == part.PartId);
if ( partToBeUpdated != null )
{ partToBeUpdated.Quantity += part.Quantity; }
else
{ stockToBeUpdated.Add(part); }
}
stockToBeUpdated.RemoveAll(x => x.Quantity <= 0);
return stockToBeUpdated;
}
}
Now any class that needs to implement this can simply call it in a method on the respective property. For example, in the EngineerModel class, it can operate on the PartsInStock property:
public void AddPartsToStock(List<PartModel> partsSent) {
PartsInStock.UpdateStockQuantitiesWith(partsSent);
}

Joining two datatables as parent-child in linq

I need to build a typed list of parent-child objects that are read from two different Excel sources: One describes parent object, another describes child objects. The hierarchy is only 2 layers ever.
Reading into excel is not the issue, as it is read into 2 untyped datatables, but joining the information is.
The structure is very plain:
Parent has an ID and some text fields
Children have a parentID (so its 1-m) and some text fields
The objects that these are to be populated into looks like this:
public class ParkingSites
{
public List<ParkingLot> Lots { get; set; }
public ParkingSites(List<ParkingLot> arg)
{
Lots = arg;
}
}
public class ParkingLot
{
public List<Bay> Bays{ get; set; }
public int Id { get; set; }
public List<string> ParkingLotDetails { get; set; }
public ParkingLot()
{
}
}
public class Bay
{
public List<string> BayDetails { get; set; }
public int ParentId { get; set; }
public Bay()
{
}
}
The excel sources have a fixed column order with the parent sheet's first column being the parentId, and the first column on the child sheet also being the parentId.
EDIT: After playing around a bit, I just made both parent and child classes typed, as the initial reason for leaving them mostly untyped lead to more problems than it prevented. This is part of a larger project where the untypedness is a better solution for our problem on the other classes with data that is not hierarchial.
You can simply group the list of children by the parent id, and then iterate over the parents and add each child that belongs to it.
For example, you could use ToLookup:
// assuming you have all Bay instances in a collection called bays
var baymap = bays.ToLookup(b => b.ParentId);
// and all ParkingLot instances in a collection called lots
foreach(var lot in lots)
lot.Bays.AddRange(baymap[lot.Id]);
or, using the first element in the details lists:
var baymap = bays.ToLookup(b => b.BayDetails[0]);
foreach(var lot in lots)
lot.Bays.AddRange(baymap[lot.ParkingLotDetails[0]]);
or, using Where without a lookup (probably slower, depends on your data):
foreach(var lot in lots)
lot.Bays.AddRange(bays.Where(b => b.ParentId == lot.Id));

Custom POCO-Objects to table

Currently I used inherited DbContext-Classes containing DbSet<T> members to store POCO objects into database. This works fine for 'compile-time-known' classes and their structures.
Now I have a class like
public class ResourceSet
{
public long Id
{
get;
set;
}
public long OwnerId
{
get;
set;
}
public double[] Resources
{
get;
set;
}
}
and would like to store it into a database-scheme:
Id, OwnerId, Res_1, Res_2, Res_3, ... (depending on size of Resources, which will be fixed during start-up of program).
Currently I use CTP5 of EFCodeFirst
DbSet<ResourceSet> fails of course. It does not support indexed properties.
I would like to avoid the .edmx files
My question:
Is it possible to create a proxy/wrapper/transformation class, which maps the Resources Array into the Res_X columns. Do you have a hint within document where I can start?
LINQ for searching into 'Id' and 'OwnerId' shall be still possible, for Resources, it is not necessary.
If Resources are never needed in a Linq query you can map it to a string backing property and then save that back in the database.
public double[] Resources
{
get
{
var result = from r in ResourceString.Split(new string[] {";"}, StringSplitOptions.RemoveEmptyEntries)
select double.Parse(r);
return result.ToArray();
}
set
{
ResourceString = string.Empty;
foreach (var d in value)
{
ResourceString += d + ";";
}
}
}
private string ResourceString
{
get;
set;
}
If you update your Entity Framework to the latest version (trough NuGet) and specify Resources as not mapped everything should work.

How do I turn a one-to-many relationship into a list property using SauceDB?

Using SauceDB, how do I turn a one-to-many relationship, between say table "A" and table "B" respectively, into a list property (containing B objects) in the class that corresponds to table A? The relationship is represented by a foreign key in table B referring to table A (so that many B records can belong to one A record).
Sauce does not support Linq2SQL style navigation properties. However, there are two supported ways to work around this depending on your requirements.
1) Just do the join in your code
IDataStore dstore = .GetDataStore();
var query = from i in dstore.Query<MyTable>()
join x in dstore.Query<MyTable>() on i.Name equals x.Name
select new { };
2) Another way to do it is as follows, and gives a more Navigation Property Style use. Modify your object definition to contain a list and use an [AdditionalInit]
public class Foo
{
public int ID { get; set; }
public string Name { get; set; }
[IgnoredField]
public List<Bar> Bars { get; set; }
[AdditionalInit]
private void LoadBars(IDataStore dstore)
{
Bars = dstore.Query<Bar>().Where(r=> r.Foo = this.ID).ToList();
}
}
That should do what you seek, if you have any more questions let me know.
I found that I could use the AdditionalInit attribute in order to define a hook which gets called as a database object gets initialized. Since this hook can accept a data store, I can deduce the one-to-many relationship right there.
Here's the relevant excerpt of class A:
public class A
{
...
public List<B> Bs { get; private set; }
[AdditionalInit]
public void OnInit(IDataStore dstore)
{
Bs = dstore.Query<B>().Where(b => b.A.Id == Id).ToList();
}
}
Bear in mind I haven't been able to test this code yet.

Categories