Atomic updates in Entity Framework - c#

This is my simple example of incrementing record's value in EF:
public class context : DbContext
{
public DbSet<Result> Results { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(#"Server=(localdb)\mssqllocaldb;Database=Test;Trusted_Connection=True;");
}
}
public class Result
{
public int Id { get; set; }
public List<History> Histories { get; set; }
}
public class History
{
public int Id { get; set; }
public DateTime Date { get; set; }
public int Value { get; set; }
}
public static int Get()
{
using (var db = new context())
{
var entity = db.Results
.Include(x => x.Histories)
.First(x => x.Id == 1);
return entity.Histories.Last().Value;
}
}
public static void Increment(int newValue)
{
using (var db = new context())
{
using (var transaction = db.Database.BeginTransaction())
{
var entity = db.Results
.Include(x => x.Histories)
.First(x => x.Id == 1);
if (entity.Histories.Last().Value != newValue)
{
entity.Histories.Add(new History() { Value = newValue });
db.SaveChanges();
transaction.Commit();
}
}
}
}
Next, I launch simple console app several times:
while(true)
{
var val = Service.Get();
Service.Increment(val + 1);
}
What I expect is Increment to be atomic, which means, there should be no History records with the same value (because of if (entity.Histories.Last().Value != newValue)). Unfortunately, when I run this in sql:
SELECT [value], COUNT(value) AS amount
FROM history
WHERE ResultId = 1
GROUP BY [value]
ORDER BY COUNT(value) DESC
I can see, that in fact, some values are duplicated
value amount
5 5
7 7
12 4
Whats wrong there? How can I make Increment to be atomic?

First a crash course on projection. EF is an ORM that allows you to query data in a relational database and map it to entities (classes) or project pieces of information from the relevant tables, working out the necessary SQL for you.
So for instance instead of this:
public static int Get()
{
using (var db = new context())
{
var entity = db.Results
.Include(x => x.Histories)
.First(x => x.Id == 1);
return entity.Histories.Last().Value;
}
}
It is much faster to do this:
public static int Get()
{
using (var db = new context())
{
var lastHistoryValue = db.Results
.Where(x => x.Id == 1)
.SelectMany(x => x.Histories)
.OrderByDescending(x => x.Date)
.Select(x => x.Value)
.FirstOrDefault();
return lastHistoryValue;
}
}
The first example is loading a Result and all Histories for that result into memory then attempting to get the last history, just to return it's value. (Accounting for the bug that there is no order by to determine what is the last item.)
The second example builds a query that will return the latest Historical Value. This doesn't load any entities into memory. You will get back a value of "0" if no History record is found.
Now when it comes to incrementing, this is where we typically do want to load entities and their related entities. EF works with a Transaction by default, so there is no need to complicate things introducing explicit ones:
public static void Increment(int newValue)
{
using (var db = new context())
{
var entity = db.Results
.Include(x => x.Histories)
.Single(x => x.Id == 1);
var latestHistory = entity.Histories
.OrderByDescending(x => x.Date)
.FirstOrDefault();
if (latestHistory == null || latestHistory.Value != newValue)
{
entity.Histories.Add(new History() { Value = newValue });
db.SaveChanges();
}
}
}
This also doesn't mean your check is that accurate since there is nothing stopping a value from being repeated. For instance:
Increment(4);
Increment(5);
Increment(4);
... would be perfectly valid. When the first increment happens provided the value wasn't already 4, the history would be added with a value of 4. Then a history would be added for 5. Then a history could be added for 4 again since the latest history would be "5", so you could end up with duplicates.

Related

Calculations on grouped rows using lambda functions in c#

I am trying to calculate Best and Worst body measurement changes for people who go on a fitness trip.
I have a database full of before and after body composition measurements for several people who go on various trips. Every participant and every trip has an Id. There are 3 types of readings, B(efore), M(iddle) and A(fter). Here is an example of the data:
ParticipantId TripId Type Weight BodyFatPct
1 2 B 195 22.8
1 2 B 189.6 24.1
1 2 A 186.6 21.2
1 2 A 187.6 23.8
2 3 B 199.2 23.7
2 3 B 198.4 25.1
2 3 A 193 22.4
Here is the class I'm using to represent the data:
public partial class Detail
{
public int ParticipantId { get; set; }
public int TripId { get; set; }
public string Type { get; set; }
public double? Weight { get; set; }
public double? BodyFatPct { get; set; }
}
Here is my highly inefficient C# code to calculate best and worst for weight and body fat.
List<Detail> result = new List<Detail>();
var _result = result.GroupBy(x => new { x.ParticipantId, x.TripId });
foreach(var res in _result)
{
var beforeHighWeight = res.Where(x => x.Type == "B").Max(x => x.Weight);
var beforeLowWeight = res.Where(x => x.Type == "B").Min(x => x.Weight);
var afterWeight = res.Where(x => x.Type == "A").Min(x => x.Weight);
var beforeHighFat = res.Where(x => x.Type == "B").Max(x => x.BodyFatPct);
var beforeLowFat = res.Where(x => x.Type == "B").Min(x => x.BodyFatPct);
var afterFat = res.Where(x => x.Type == "A").Min(x => x.BodyFatPct);
var BestWeightDiff = BeforeHighWeight - afterWeight;
var WorstWeightDiff = BeforeLowWeight - afterWeight;
var BestFatDiff = BeforeHighFat - afterFat;
var WorstFatDiff = BeforeLowFat - afterFat;
}
In actuality, I have about 15 fields to calculate, not just two. Is there a lambda function that does row-wise calculations on grouped data? Any help appreciated.
Performance optimizations normally come with the cost of less maintainable code. So if performance is almost sufficient you should probably follow the hint as commented by Prasad Telkikar and avoid filtering res more than once with the same predicate, i.e. you should assign the filtered lists and work with them:
var resA = res.Where(x => x.Type == "A")
var resB = res.Where(x => x.Type == "B")
If performance is an issue and affords work, you can enumerate the list once and maybe stream the values from the database by using Aggregate. You could e.g. create a class that holds all the values you want to calculate and has a method to update the values given a new entry. You could e.g. create the classes
public class Statistic
{
public int BeforeHighWeight { get; private set; }
public int BeforeLowWeight { get; private set; }
// Add the dimensions you are interested in
public Statistic(Detail detail)
{
// initialize with given Detail
}
public Statistic AddDetail(Detail detail)
{
// update Statistic with given Detail
return this;
}
}
public class Statistics
{
private readonly ConcurrentDictionary<(int, int), Statistic> _statistics = new ConcurrentDictionary<(int, int), Statistic>();
public Statistics AddDetail(Detail detail)
{
_statistics.AddOrUpdate(
(detail.ParticipantId, detail.TripId),
key => new Statistic(detail),
(key, statistic) => statistic.AddDetail(detail)
);
return this;
}
}
Then you could aggregate your values like so:
var rand = new Random();
var result = Enumerable.Range(1, 1000000)
.Select(i => new Detail {ParticipantId = i % 10000, TripId = rand.Next(100000) /*, ...*/})
.Aggregate(
new Statistics(),
(statistics, detail) => statistics.AddDetail(detail)
);

EF Core 3.1 not allowing to update parent while adding child entities in one-to-many relationship using DDD approach

I'm following DDD approach and have a parent and child entity with one-to-many relationship configured as shown below:
Parent Entity:
public class AnnotationEntity
{
public int Id { get; private set; }
public string Value { get; private set; }
private readonly List<AnnotationObjectEntity> _annotationObjects = new List<AnnotationObjectEntity>();
public virtual IReadOnlyList<AnnotationObjectEntity> AnnotationObjects => _annotationObjects.ToList();
protected AnnotationEntity()
{
}
private AnnotationEntity(string value) : this()
{
Value = value;
}
private void UpdateContainer(string container)
{
Value = container;
}
public void AddAnnotation(AnnotationObjectEntity annotationObject)
{
if (!_annotationObjects.Any(x => x.Id == annotationObject.Id))
{
var annotationObjectEntity = new AnnotationObjectEntity
(
annotationObject.Id,
annotationObject.PageNumber,
annotationObject.AnnotationType
);
_annotationObjects.Add(annotationObjectEntity);
return;
}
var existingAnnotation = _annotationObjects.FirstOrDefault(x => x.Id.Equals(annotationObject.Id));
if (existingAnnotation != null)
{
existingAnnotation.Update(annotationObject);
}
}
public void RemoveAnnotation(Guid id)
{
XDocument annotation = XDocument.Parse(Value);
var annotationObjectEntity = _annotationObjects.FirstOrDefault(x => x.Id == id);
if (annotationObjectEntity != null)
{
_annotationObjects.Remove(annotationObjectEntity);
annotation.Descendants("Object").Where(x => x.Element("Guid").Value == annotationObjectEntity.Id.ToString()).Remove();
}
UpdateContainer(annotation.ToString());
}
public static AnnotationEntity Create(int folderId, int documentId, string annotation)
{
XDocument doc = XDocument.Parse(annotation);
var documentAnnotations = new List<AnnotationEntity>();
var annotationEntity = new AnnotationEntity
(
annotation
);
doc.Descendants("Container").ToList().ForEach(container =>
{
container.Descendants("Object").ToList().ForEach(annotationObject =>
{
var annotationObjectEntity = new AnnotationObjectEntity
(
Guid.Parse(annotationObject.Element("Guid").Value.ToString()),
Convert.ToInt32(container.Element("PageNumber").Value.ToString()),
true
);
annotationEntity.AddAnnotation(annotationObjectEntity);
});
});
return annotationEntity;
}
public void UpdateAnnotation(string modifiedAnnotationXml)
{
var modifiedAnnotationEntity = Create(modifiedAnnotationXml);
//Removing deleted entities
var deletedAnnotationsId = _annotationObjects.Where(x => !modifiedAnnotationEntity.AnnotationObjects.Any(y => y.Id.Equals(x.Id))).Select(x => x.Id).ToList();
foreach (var annotationId in deletedAnnotationsId)
{
RemoveAnnotation(annotationId);
}
XDocument annotation = XDocument.Parse(Value);
XDocument modifiedAnnotation = XDocument.Parse(modifiedAnnotationXml);
//update or add
foreach (var annotationObject in modifiedAnnotationEntity.AnnotationObjects)
{
AddAnnotation(annotationObject);
annotation.Descendants("Object").Where(x => x.Element("Guid").Value == annotationObject.Id.ToString()).Remove();
// extract annotation object xml from modified xml and add to existing xml
var modifiedAnnotationObject = modifiedAnnotation.Descendants("Object").SingleOrDefault(x => x.Element("Guid").Value == annotationObject.Id.ToString());
annotation
.Descendants("Container").SingleOrDefault(x => x.Element("PageNumber").Value == annotationObject.PageNumber.ToString())
.Descendants("Objects").SingleOrDefault()
.Add(modifiedAnnotationObject);
UpdateContainer(annotation.ToString());
}
}
}
Child Entity:
public class AnnotationObjectEntity
{
public Guid Id { get; private set; }
public int PageNumber { get; private set; }
public bool AnnotationType { get; private set; }
public AnnotationEntity Annotation { get; }
protected AnnotationObjectEntity()
{
}
public AnnotationObjectEntity(Guid id, int pageNumber, bool annotationType) : this()
{
Id = id;
PageNumber = pageNumber;
AnnotationType = privateToSelectedUserGroup;
}
public void Update(AnnotationObjectEntity annotationObject)
{
PageNumber = annotationObject.PageNumber;
AnnotationType = annotationObject.AnnotationType;
}
}
I have configured my entities in my DbContext as shown below:
DbContext:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<AnnotationEntity>(x =>
{
x.Property(y => y.Value).IsRequired();
x.HasMany(p => p.AnnotationObjects).WithOne(p => p.Annotation)
.OnDelete(DeleteBehavior.Cascade)
.Metadata.PrincipalToDependent.SetPropertyAccessMode(PropertyAccessMode.Field);
});
modelBuilder.Entity<AnnotationObjectEntity>(x =>
{
x.HasKey(k => k.Id);
x.Property(p => p.Id).HasColumnName("Id");
x.HasOne(p => p.Annotation).WithMany(p => p.AnnotationObjects);
});
}
After reading the parent entity along with child entity and if I remove/update existing child and add new child and when I try to call save to database as shown below:
Repository:
annotationEntity.UpdateAnnotation(annotationToUpdate);
_context.Annotation.Attach(annotationEntity);
//var changes = _context.ChangeTracker.Entries().Where(x => x.Entity is AnnotationObjectEntity).Select(x => (AnnotationObjectEntity)x.Entity).ToList();
//foreach (var change in changes)
//{
// _context.Entry(change).State = EntityState.Added;
//}
await _context.SaveChangesAsync();
I get the following error:
Database operation expected to affect 1 row(s) but actually affected 0
row(s). Data may have been modified or deleted since entities were
loaded. See http://go.microsoft.com/fwlink/?LinkId=527962 for
information on understanding and handling optimistic concurrency
exceptions.
Upon further analysis I found that this happens when I add new child objects to the parent object. And also I noticed that ChangeTracker has the Entity State as Modified for newly added entities. When I change it manually to Added. Then SaveChangesAsync() works.
Please assist on what I'm doing wrong and why ChangeTracker() wrongly detects the Added entity state as Modified.

Order by array values in Linq to Entity Framework Query

I am trying to write an OrderBy clause in a Linq to EntityFramework query. My problem is that the entity table I am looking at stores an ID, that relates to a table in a different database and I cannot adjust the database.
MainDatabase.EntityToOrder
ID
Name
OtherID
SecondDatabase.OtherEntity
ID
Name
My C# EntityToOrder Model looks like this, and I need to be able to order by "OtherName"
EntityToOrder.cs
public class EntityToOrder
{
[DataMember]
public long ID { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public long OtherId { get; set; }
public string OtherName { get; set; }
}
So, I would like to Order EntityToOrder by "OtherName" in the most efficient way possible. My existing query looks like this.
var entities = mainContext.EntityToOrder.OrderBy(e => e.Name).Skip(startIndex).Take(pageSize).ToList();
var otherIds = entities.Select(e => e.OtherID).ToList();
Dictionary<long, string> otherNames = secondContext.OtherEntity
.Where(oe => otherIds.Contains(oe.ID))
.Select(oe => new { ID = oe.ID, Name = oe.Name })
.ToDictionary(oe => oe.ID, oe => oe.Name);
entities.ForEach(e => OtherName = otherNames[e.OtherID]);
How can I write the most efficient query to order by "OtherName", preferably avoiding selecting the whole EntityToOrder table into memory.
Update
For clarity, here is some code that achieves the OrderBy, but needs to retrieve the entire EntityToOrder table into memory. I was hoping this could be achieved in a more efficient way. Also, the OtherEntity can belong to many EntityToOrder rows.
var entities = mainContext.EntityToOrder.ToList();
var otherIds = entities.Select(e => e.OtherID).ToList();
Dictionary<long, string> otherNames = secondContext.OtherEntity
.Where(oe => otherIds.Contains(oe.ID))
.Select(oe => new { ID = oe.ID, Name = oe.Name })
.ToDictionary(oe => oe.ID, oe => oe.Name);
entities.ForEach(e => OtherName = otherNames[e.OtherID]);
return entities.OrderBy(e => e.OtherName).Skip(startIndex).Take(pageSize).ToList();
Quite challenging task. I was thinking initially just to switch the roles and perform pagination (OrderBy/Skip/Take) on OtherEntity table, but unfortunately that doesn't work due to one to many relationship. So I ended up with doing some pre pagination in memory on OtherEntity. However, in order to do that I needed counts of the matching items in EnityToOrder, so this is retrieved with additional db query, which makes the solution involving 3 db queries and some memory processing. Here it is
var countByOtherId = db.EntityToOrder
.GroupBy(e => e.OtherId)
.Select(g => new { ID = g.Key, Count = g.Count() })
.ToDictionary(e => e.ID, e => e.Count);
var other = new Dictionary<long, string>();
int skipCount = startIndex, useCount = 0;
foreach (var e in db.OtherEntity.OrderBy(e => e.Name))
{
int count;
if (!countByOtherId.TryGetValue(e.ID, out count)) continue;
if (skipCount > 0 && other.Count == 0)
{
if (skipCount >= count) { skipCount -= count; continue; }
count -= skipCount;
}
other.Add(e.ID, e.Name);
if ((useCount += count) >= pageSize) break;
}
var entities = db.EntityToOrder
.Where(e => other.Keys.Contains(e.OtherId))
.AsEnumerable()
.Select(e => new EntityToOrder { ID = e.ID, Name = e.Name,
OtherId = e.OtherId, OtherName = other[e.OtherId] })
.OrderBy(e => e.OtherName).ThenBy(e => e.Name)
.Skip(skipCount).Take(pageSize)
.ToList();
Now, I'm not quite sure if that's better to what are you doing currently, but it's worth trying.
If you can change the model, then you might try the following:
public class EntityToOrder
{
[DataMember]
public long ID { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public long OtherId { get; set; }
[ForeignKey("OtherId")]
public OtherEntity OtherEntity{ get; set; }
}
Then, you should be able to perform this query:
using System.Data.Entity;
var entities = mainContext
.EntityToOrder
.Include(x => x.OtherEntity)
.OrderBy(e => e.OtherEntity.Name)
.Skip(startIndex)
.Take(pageSize)
.ToList();
Edit :
Sorry, I missed the point that you had 2 databases....
I found an alternative which I thought I would post in case it is useful to anyone. I used a .Join() to merge the dictionary of OtherEntity into my query. This still selects into an IEnumerable so I don't think it is more efficient.
var entities = mainContext.EntityToOrder;
var otherIds = entities.Select(e => e.OtherID).ToList();
Dictionary<long, string> otherNames = secondContext.OtherEntity
.Where(oe => otherIds.Contains(oe.ID))
.Select(oe => new { ID = oe.ID, Name = oe.Name })
.ToDictionary(oe => oe.ID, oe => oe.Name);
Func<EntityToOrder, KeyValuePair<long, string>, EntityToOrder> joinFunc = ((a, b) => {
a.OtherName= b.Value;
return a;
});
return entities.Join(otherNames, e => e.OtherID, oe => oe.Key, joinFunc)
.OrderBy(e => e.OtherName)
.Skip(startIndex)
.Take(pageSize)
.ToList();
Note on Includes
When applying Join you select into an IEnumerable and therefore lose the ability to access properties from a linked table. To counter this you would need to add a .Include() for any linked table you need to access before applying the .Join(). E.g.
var entities = mainContext.EntityToOrder
.Include("LinkedEntity");
return entities.Join(otherNames, e => e.OtherID, oe => oe.Key, joinFunc)
.OrderBy(e => e.OtherName)
.ThenBy(e => e.LinkedEntity.Name) //reference to linked table
.ToList();

QueryOver multiple tables with NHibernate

I'm trying to left join multiple tables and project some columns that result from this join onto a new entity, and then taking a few records from this result from my database. I've taken a look on a few similar questions here on SF, but I'm not managing to assemble all of those parts into a piece of code that works.
Here is the query I'm trying to generate with NHibernate:
select * from
( select LOC_Key.KeyName, LOC_Translation.TranslationString, LOC_Translation.Comments
from LOC_Key
left join LOC_Translation
on LOC_Key.ID = LOC_Translation.KeyID and LOC_Translation.LanguageID = 6
order by LOC_Key.KeyName
) as keyTable
limit 0,100
I have three entities here, Key, Translation and Language. A Key is a unique string identifier for different translations of a same word in different languages. I want to show the first n keys in alphabetical order for a language, but I want all keys listed, not only the ones that are translated (that's why I'm using a left join).
I took a look at QueryOver<>, Select() method and List<object[]>() method but I can't even manage to have a code that compiles in the first place.
I could use C# linq after getting all records from the tables Key and Translation, having something like this:
IEnumerable<string> k = RepositoryKey.GetLimits( offset, size ).Select( x => x.KeyName );
IEnumerable<TranslationDescriptor> t = RepositoryTranslation.GetAllWhere( x => x.LanguageID.LanguageCode == language && k.Contains ( x.KeyID.KeyName ) ).ToList().ConvertAll( new Converter<Translation, TranslationDescriptor>( ( x ) => { return new TranslationDescriptor { LanguageCode = x.LanguageID.LanguageCode, KeyName = x.KeyID.KeyName, Comments = x.Comments, TranslationString = x.TranslationString }; } ) );
var q = from key in k
join trl in t on key equals trl.KeyName into temp
from tr in temp.DefaultIfEmpty()
select new TranslationDescriptor { KeyName = key, LanguageCode = language, Comments = ( tr == null ) ? string.Empty : tr.Comments, TranslationString = ( tr == null ) ? string.Empty : tr.TranslationString };
However, that's very slow. By the way, my implementation for GetLimits and GetAllWhere is:
public IEnumerable<T> GetAllWhere(Func<T, bool> func)
{
var products = Session.Query<T>().Where(func);
return products;
}
public IEnumerable<T> GetLimits(int offset, int size)
{
return Session.CreateCriteria(typeof(T)).SetFirstResult(offset).SetMaxResults(size).List<T>();
}
Thank you for your help!
Bruno
I'm guessing a little bit at your entities and mappings, but the following might help you get ideas. It joins Key to Translation with a left outer join, then projects the results to a new DTO object.
[Test]
public void LeftOuterProjection()
{
using (var s = OpenSession())
using (var t = s.BeginTransaction())
{
// Set up aliases to use in the queryover.
KeyDTO dtoAlias = null;
Key keyAlias = null;
Translation translationAlias = null;
var results = s.QueryOver<Key>(() => keyAlias)
.JoinAlias(k => k.Translations, () => translationAlias, JoinType.LeftOuterJoin)
.Where(() => translationAlias.LanguageId == 6)
.OrderBy(() => keyAlias.KeyName).Asc
.Select(Projections.Property(() => keyAlias.KeyName).WithAlias(() => dtoAlias.KeyName),
Projections.Property(() => translationAlias.TranslationString).WithAlias(() => dtoAlias.TranslationString),
Projections.Property(() => translationAlias.Comments).WithAlias(() => dtoAlias.Comments))
.TransformUsing(Transformers.AliasToBean<KeyDTO>())
.List<KeyDTO>();
}
}
public class KeyDTO
{
public string KeyName { get; set; }
public string TranslationString { get; set; }
public string Comments { get; set; }
}
public class Key
{
public int Id { get; set; }
public string KeyName { get; set; }
public IList<Translation> Translations { get; set; }
}
public class Translation
{
public Key Key { get; set; }
public int LanguageId { get; set; }
public string TranslationString { get; set; }
public string Comments { get; set; }
}
The modifications I made to my code following ngm suggestion (thanks!):
Language l = RepositoryLanguage.GetSingleOrDefault(x => x.LanguageCode
== language);
KeyTranslationDTO dtoAlias = null;
Key keyAlias = null;
Translation translationAlias = null;
var results = RepositoryKey.GetSession()
.QueryOver<Key>(() => keyAlias)
.OrderBy(() => keyAlias.KeyName).Asc
.JoinQueryOver<Translation>( k => k.Translations, () => translationAlias, JoinType.LeftOuterJoin, Restrictions.Where( () => translationAlias.LanguageID == l ) )
.Select(Projections.Property(() => keyAlias.KeyName).WithAlias(() => dtoAlias.KeyName),
Projections.Property(() => translationAlias.TranslationString).WithAlias(() => dtoAlias.TranslationString),
Projections.Property(() => translationAlias.Comments).WithAlias(() => dtoAlias.Comments))
.TransformUsing(Transformers.AliasToBean<KeyTranslationDTO>())
.Skip(index).Take(amount)
.List<KeyTranslationDTO>();

A way around LINQ to Entities does not recognize the method?

I've got a method I've been using against IEnumerable no problem. However I want to start using it against IQueryable as well. However, the way I currently have it wont work as its trying to execute my method against the database.
The situation is as follows. I want a property on the object I'm selecting into to be be null if the value selecting from is null or the Id and Name of the property if it exists. For example:
var foos = FooRepository.All().Select(s => new FooBrief()
{
Id = s.Id,
Customer = SimpleData.To(s.Customer, m => m.Id, m => m.Name)
});
where SimpleData.To looks like:
public class SimpleData
{
public int Id { get; set; }
public string Name { get; set; }
public static SimpleData To<T>(T t, Func<T, int> id, Func<T, string> name) where T : class
{
if (t != null)
{
return new SimpleData { Id = id(t), Name = name(t) };
}
return null;
}
}
Is there someway I can get this behaviour whilst allowing it to execute against the database?
NOTE: Because of reasons elsewhere in my code I cannot use .ToList(). I may be adding additional filtering at a later point
The simplest approach is just to perform the selection outside the database, using AsEnumerable:
var foos = FooRepository.All()
.Select(x => new { Id = x.Id,
CustomerId = x.Customer.Id,
CustomerName = x.Name })
.AsEnumerable()
.Select(s => new FooBrief {
Id = s.Id,
Customer = new SimpleData {
Id = s.CustomerId,
Name = s.CustomerName
}
});
The first Select is just to make sure that the database query only pulls out the required fields. If you really still want to use your SimpleData.To method:
// Outdented to avoid scrolling
var foos = FooRepository.All()
.Select(x => new { Id = x.Id,
CustomerId = x.Customer.Id,
CustomerName = x.Name })
.AsEnumerable()
.Select(s => new FooBrief {
Id = s.Id,
Customer = SimpleData.To(s,
s => s.CustomerId,
s => s.CustomerName)
});

Categories

Resources