most efficient way to populate composite class - c#

I just want to get some opinions on the most efficient and quickest way to populate my composite class using the EF4.0. I have a parent class which has a structure similar to the class below. It mirrors my database structure.
public class Person
{
public string FirstName { get; set; }
........
public Address WorkAddress { get; set; }
public IList<Account> Workspace { get; set; }
}
public class Address
{
public string FirstName { get; set; }
......
}
public class Account
{
public string SortCode { get; set; }
public IList<string> TransactionHistory {get; set;}
......
}
So, at this moment in time I pull back all the 'Persons' from the EF and I loop through each of them and populate the Address and Accounts for each. Lazy loading is enabled, so I have to encapsulate all my loops in the using statement or my Accounts will be empty when I try to iterator through them. So, can I disable lazy loading for this call only or should approach the population of my list of persons in another manner.
using (var entities = new PersonEntities())
{
var dbPeople = (from person in entities.Persons
select person).ToList();
foreach(var person in dbPeople)
{
foreach(var account in person.Accounts)
{
// In here I populate my 'Person' business object account and add it to my collection to return.
}
}
}

If I got you right, you're going to include the relation keeping the LazyLoading enabled. You can do this for the query using the Include method:
using (var entities = new PersonEntities())
{
var dbPeople = entities.Persons.Include("Accounts").ToList();
foreach(var person in dbPeople)
{
//Do nothing with Accounts if the relation is mapped correct
}
}
EDIT:
Also you can disable LazyLoading for the created PersonEntities instance's lifetime:
using (var entities = new PersonEntities())
{
entities.Configuration.LazyLoadingEnabled = false;
//...
}

Related

Reading data from DB using Entity Framework results in missing fields of an object

I currently have 2 models. One model references the other model. I am able to write the data perfectly well to the db. however when I read the data I get an incomplete object. One of the fields of that object is missing. This is what my models look like
public class Student
{
public int Id { get; set; }
public List<Sport> Sports { get; set; }
public string StudentName { get; set; }
}
public class Sport
{
public int Id { get; set; }
public string SportName { get; set; }
}
public class DbContext : DbContext
{
public DbContext()
: base("name=StudentContext")
{
}
public DbSet<Student> Students { get; set; }
}
Now I am writing the Student object like this
//Writing to the DB
Student stud = new Student()
{
StudentName = "Andrew",
};
stud.Sports = sportList; //Contains a sport List
this.context.Students.Add(device);
this.context.SaveChanges();
After writing the object to the DB I noticed that both the tables got populated and it looks good. However when I attempt to read the object back like this
The Sport List in Student does not get populated and comes back as NULL
//Reading from DB - Sports field missing
var a = this.context.Students.FirstOrDefault();
List<Sport> actions = a.Sports; //Returns null Why ??
Any suggestions on what I might be doing wrong ? How can I get the sports field as well ?
change in edmx file
<EntityContainer Name="StudentContext" annotation:LazyLoadingEnabled="false">
OR
{
public DbContext()
: base("name=StudentContext")
{
this.Configuration.LazyLoadingEnabled = false;
this.Configuration.ProxyCreationEnabled = false;
}
public DbSet<Student> Students { get; set; }
}
Or you can also modify your query
var a = this.context.Students.Include(s => s.Sports).FirstOrDefault();
Actually you do NOT need to do this (and you should not because in this way all the navigation properties inside the current DbContext will be eager loaded):
this.Configuration.LazyLoadingEnabled = false;
According to the reference you have already disabled lazy loading for the navigation property Sports by declaring it as a non-virtual property.
Loading of the Sports collection can still be achieved using eager loading or the Load method.
(Adapted to this case from the doc)

Expanding nested object using OData client v3

I am encountering some issues, using Microsooft.Data.Services.Client in a .NET Framework project, and trying to expand objects of 2nd level.
Here is an example, having following data model:
public class Customer
{
public Order Order { get; set; }
}
public class Order
{
public Item Item { get; set; }
}
public class Item
{
public int Id { get; set; }
public string Description { get; set; }
}
If I'm using the .Execute() method, and provide a RequestUri like the following:
"V3/Customer?$expand=Order,Order/Item"
it will actually work, and do lazy loading on the sub-elements to include in my query.
If I'm using the DataServiceQuery like this (where context is an instance of DataServiceContext):
var q = context.Customers.Expand(x => x.Order).Expand(x => x.Order.Item);
This will load the Order object to the customer result, but NOT the Item of the order.
If I look on the query that the context will create, it is similar to the Uri use in the Execute.
How do I load nested elements (in this case 'Order/Item') using DataServiceContext with OData client V3?
The solution is to use projections and set the MergeOption to OverwriteChanges in this case.
I.e.
using(var ctx = new DataServiceContext(...){MergeOption = OverwriteChanges})
{
from c in ctx.Customers
select new Customer
{
Order = new Order
{
Item = new Item
{
Id = c.Order.Item.Id,
Description = c.Order.Item.Description
}
}
}
}

ViewModel Object Convert to Entity Framework Object

Goal: to save ViewModel object by Entity Framework. I have UserViewModel object which has list of UnitViewModel. Then, I have a UserAdapter class which converts UserViewModel into Entity Framework User object (see Convert()below how).
Now, my question is how do I convert this list of UnitViewModel to its corresponding Entity Framework Unit list? - Do I have to get each object from DB Context by calling something like context.Units.Where(u=>myListofUnitIDs.Contains(u.UnitID))?
public class UserViewModel
{
public Guid? UserID { get; set; }
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Password { get; set; }
public DateTime? CreateTime { get; set; }
public List<UnitViewModel> UserUnits { get; set; }
}
public class UnitViewModel
{
public Guid UnitID { get; set; }
public string Name { get; set; }
public int? SortIndex { get; set; }
public DateTime CreateTime { get; set; }
public bool Assigned { get; set; }
}
public class UserAdapter
{
public static User Convert(UserViewModel userView)
{
User user;
if (userView.UserID.HasValue)
{
using (var provider = new CoinsDB.UsersProvider())
{
user = provider.GetUser(userView.UserID.Value);
}
}
else
{
user = new User();
}
user.FirstName = userView.FirstName;
user.LastName = user.LastName;
user.Password = StringHelper.GetSHA1(userView.Password);
user.UserName = user.UserName;
user.CreateTime = DateTime.Now;
// Problem here :)
// user.Units = userView.UserUnits;
return user;
}
}
UPDATE: The main concern here is that I have to retrieve each Unit from database to match (or map) it with ViewModel.Unit objects, right? Can I avoid it?
For your information, this operation is called as Mapping mainly. So, you want to map your view model object to the entity object.
For this, you can either use already existed 3rd party library as AutoMapper. It will map properties by reflection which have same name. Also you can add your custom logic with After method. But, this approach has some advantages and disadvantages. Being aware of these disadvantages could help you to decide whether you must use this API or not. So, I suggest you to read some articles about advantages and disadvantages of AutoMapper especially for converting entities to other models. One of such disadvantages is that it can be problem to change the name of one property in the view model in the future, and AutoMapper will not handle this anymore and you won't get any warning about this.
foreach(var item in userView.UserUnits)
{
// get the mapped instance of UnitViewModel as Unit
var userUnit = Mapper.Map<UnitViewModel, UserUnit>(item);
user.Units.Add(userUnit);
}
So, I recommend to write your custom mappers.
For example, I have created a custom library for this and it maps objects lik this:
user.Units = userView.UserUnits
.Select(userUnitViewModel => userUnitViewModel.MapTo<UserUnit>())
.ToList();
And I am implementing these mapping functions as:
public class UserUnitMapper:
IMapToNew<UnitViewModel, UserUnit>
{
public UnitViewModel Map(UserUnit source)
{
return new UnitViewModel
{
Name = source.Name,
...
};
}
}
And then in runtime, I am detecting the types of the objects which will be used during mapping, and then call the Map method. In this way, your mappers will be seperated from your action methods. But, if you want it urgently, of course you can use this:
foreach(var item in userView.UserUnits)
{
// get the mapped instance of UnitViewModel as Unit
var userUnit= new UserUnit()
{
Name = item.Name,
...
};
user.Units.Add(userUnit);
}

EF7 How to handle the update operation for nested entities

I am trying to figure out the best practices when updating an entity and all it's children. For example; I have an "Employer" update service that would update the employer entity and the "Address" entities of the employer and the "Phone" entities of each "Address". User may add new addresses to the existing employer or they may update the current addresses or they can delete some, same applies for the phones of each addresses. Could you help me to write the ideal code that would handle this scenario.
I am using EF7 rc1 and I use Automapper to map the Dto to Entity in my service.
public partial class Employer
{
public int EmployerId { get; set; }
public int Name { get; set; }
[InverseProperty("Employer")]
public virtual ICollection<Address> Address { get; set; }
}
public partial class Address
{
public int AddressId { get; set; }
public int Address1{ get; set; }
public int City { get; set; }
[ForeignKey("EmployerId")]
[InverseProperty("Address")]
public virtual Employer Employer { get; set; }
[InverseProperty("Address")]
public virtual ICollection<Phone> Phone { get; set; }
}
public partial class Phone
{
public int PhoneId { get; set; }
public string Number { get; set; }
[ForeignKey("AddressId")]
[InverseProperty("Phone")]
public virtual Address Address { get; set; }
}
My service method;
public async Task<IServiceResult> Update(EmployerDto employer)
{
var employerDbEntity = await _db.Employer
.Include(a=>a.Address).ThenInclude(p=>p.Phone)
.SingleOrDefaultAsync (a=>a.EmployerId == employer.EmployerId);
//How to handle the update operation for children?
var entity = Mapper.Map<Employer>(employer);
HandleChildren(employerDbEntity,entity);
await _db.SaveChangesAsync();
...
...
}
private void HandleChildren(Employer employerDbEntity,Employer entity)
{
//Delete
foreach (var existing in employerDbEntity.Address.ToList())
{
if (!entity.Address.Any(a => a.AddressId == existing.AddressId))
employerDbEntity.Address.Remove(existing);
}
//Update or Insert
foreach (var address in entity.Address)
{
var existing = employerDbEntity.Address.SingleOrDefault(a =>a.AddressId == address.AddressId);
//Insert
if (existing == null)
{
employerDbEntity.Address.Add(address);
}
//Update
else
{
Mapper.Map(address, existing);
}
}
}
This example looks like a decent way to handle child collections. Each collection has to be checked manually for the action performed. (Using generics sounds good, but always bites back in some way. Typically, performance.)
With that in mind, here's a few recommendations:
Move the child collection handling into separate methods/services.
If querying for existing entities, retrieve the whole collection in one query then iterate the results in memory.
Since you're writing async code, you can take advantage of processing the child collections in parallel! To do this, each operation should create it's own context. This explains why it's faster.
Here's an example using the recommendations:
private async Task UpdateAddresses(List<Address> addressesToUpdate)
{
using(var context = new Context())
{
var existingAddressIds = await context.Addresses
.Where(a => addressesToUpdate.Contains(a.AddressId))
.ToListAsync()
.ConfigureAwait(false);
existingAddressIds.ForEach(a => context.Addresses.Remove(a));
await context.SaveChangesAsync().ConfigureAwait(false);
}
}

Converting a string into a List and then using Entity Framework to insert into Database

I am converting a string into a list and then trying to use entity framework to insert it into a DB. The issue that I am having is that I don't know how to save the changes to the DB.
This is the code that I am trying to use and is where the string is converted to a list:
if (intCounter == 0)
{
return JsonConvert.DeserializeObject<List<foo>>(jsonString).Cast<T>().ToList();
}
Then in a seperate class below.
ConvertJson.Convert<CouncilEvent>(strResponseJSONContent, intCounter);
The Entity Framework Model that I am trying to use for the list.
namespace foo.Models
{
public partial class foo
{
public foo()
{
this.EventDates = new List<EventDate>();
}
public System.Guid foo_PK { get; set; }
public string EntityID { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string Address3 { get; set; }
public bool Adult { get; set; }
}
}
The class foo contains properties that match those in the string.
It is this foo that I am then trying to insert into the DB. foo is also part of my Entity Framework model.
I have never used a list in this situation before and I thought it would just be a matter of using db.SaveChanges() but that doesn't seem to work. Where would I place the necessary lines of code such as using (db = new contextFoo) and db.SaveChanges(). Also do I need to add the items? I haven't because I thought I was already adding them to the class and therefore didn't need to do this manually?
db.SaveChanges() will only 'update' your database to what was changed. So, you need to add something to the database, and then call SaveChanges() for it to work.
You can loop the list to add the objects to the context, then call SaveChanges()...
var councilEvents = ConvertJson.Convert<CouncilEvent>(strResponseJSONContent, intCounter);
using (var db = new contextFoo())
{
foreach (var councilEvent in councilEvents)
{
db.CouncilEvents.Add(councilEvent);
}
db.SaveChanges();
}

Categories