AutoMapper using Linq - c#

I want to use Automapper with linq select statement. I am not able to find
how can I implement this.
Please help me for sort out this,
return studentData.Select(studData => new student {
Enable = true,
Name = studData.Name,
Result = studData.Result,
Class = studData.Class,
Address = studData.Address,
Country= getCountry(Country),
State = studData.State,
City = studData.City,
Mobile = studData.Mobile
}).ToList();
This is not mapped directly because I have customized fields that are
Enable , Country.
I am not directly mapping like this
return studentData.Select(studData => mapper.map<Student>(studData))}).ToList();
If I were to use simple without linq then I will customized that field like
var student = studentData.Select(studData => mapper.map<Student>(studData))}).ToList();
student.Enable = true;
student.Country= getCountry(Country);
return student;
Then how can I customized field in Auto Mapper using Linq?

Related

Query Entity Framework using C# and Navigation Properties

First of all I am new to both C# and EF.
I have created a number of entities with the the Model designer in VS 2015 CE and set the relationships.
I would like to query the entities to return all the customers for a specific Contract (e.g. Contract_ID = 1), along with related properties from the CustomerLocker and ContractCustomer entities (For the CustomerLocker Entity if they are present, or null if they are not). I also have the LockerNumber value from the Contract entity (e.g. 100).
I would be grateful if someone can help with the LINQ query required to select the properties I require. I would prefer to be able to use navigation properties if possible.
So far I am able to select the customers but not able to select properties from the CustomerLocker entity.
var myCustomers = (from cc in context.ContractCustomers
where cc.Contract_ID.Equals(contractID)
select new
{
Licencee = cc.IsLicencee,
Added = cc.AddedDate,
Firstname = cc.Customer.FirstName,
Lastname = cc.Customer.LastName,
DOB = cc.Customer.DateOfBirth,
Postcode = cc.Customer.PostCode,
CustomerNumber = cc.CustomerNumber
}
)
entities shown in VS Model Designer
You could get the HasCard from CustomerLockers by filtering on LockerNumber;
CustomerLockers = cc.Customer.CustomerLockers
The query;
var myCustomers = (from cc in context.ContractCustomers
where cc.Contract_ID.Equals(contractID)
select new
{
Licencee = cc.IsLicencee,
Added = cc.AddedDate,
Firstname = cc.Customer.FirstName,
Lastname = cc.Customer.LastName,
DOB = cc.Customer.DateOfBirth,
Postcode = cc.Customer.PostCode,
CustomerNumber = cc.CustomerNumber,
CustomerLockerHasCard = cc.Customer.CustomerLockers
.Where(x => x.LockerNumber == 1000)
.Select(x => x.HasCard)
}
)
Also, I suggest you to define model classes as known type instead of using anonymous type.
An option would be to get the list of customers instead of just the customer's number :
var myCustomers = (from cc in context.ContractCustomers
where cc.Contract_ID.Equals(contractID)
select new
{
Licencee = cc.IsLicencee,
Added = cc.AddedDate,
Firstname = cc.Customer.FirstName,
Lastname = cc.Customer.LastName,
DOB = cc.Customer.DateOfBirth,
Postcode = cc.Customer.PostCode,
CustomerNumber = cc.CustomerNumber,
listOfCustomers = cc.Customer.ToList() // <-Here, a list
}
)
Then you can use a loop :
foreach(var customer in myCustomers.listOfCustomers)
{
var listOfLockers = customer.CustomerLockers.ToList();
}
But this is more a beginner's way, remember it's always better to take everything you need in a single query, like Stormcloack's answer.
This answer is just to show you how you can dig in the entitys the easy way.

How to select only particular columns from navigation properties using EF

I'm trying to select only 2 columns from the navigation properties but unable to do it, can anyone let me know how can I achieve this?
It works good default way(selecting all columns) :
var productreceipts = db.productreceipts.Include(p => p.employee).Include(p => p.productmaster).Include(p => p.vendor);
What I want:
Select only 2 columns form each of employee, productmaster, vendor tables.
I know how to select if I have only one table:
var productreceipt = db.productreceipts.Select(p => new { p.ReceiptId, p.ReceivedBy });
EDIT :
I want to select all properties of first table(productreceipts) and only a few selected from others.
Any help will be much appreciated.
Thanks in advance.
You can use like :
var productreceipts = db.productreceipts.Include(p => p.employee).
.Select(p => new { propertyName = p.productmaster.SomeProperty })
If you have lazy loading enabled then you can directly use
.Select(p => new { ProductId = p.productmaster.ProductId }
else you will have to take inner join and select individual columns in single select.
Try this:
var productreceipt = db.productreceipts
.Select(p =>
new { productreceipts_prop_1 = p.productreceipts_prop_1,
productreceipts_prop_2 = p.productreceipts_prop_2,
productreceipts_prop_3 = p.productreceipts_prop_3,
productreceipts_prop_4 = p.productreceipts_prop_4,
employee_prop_1 = p.employee.employee_prop_1,
employee_prop_2 = p.employee.employee_prop_2,
productmaster_prop_1 = p.productmaster.productmaster_prop_1,
productmaster_prop_2 = p.productmaster.productmaster_prop_2,
vendor_prop_1 = p.vendor.vendor_prop_1,
vendor_prop_1 = p.vendor.vendor_prop_1,
});
EDIT:
There is no need to use the .Include() You can directly use the navigator property to retrieve the data.

Linq: let Count result into a specified column

I've got a table Installation which can contains one or many Equipements.
And for functionnal reasons, I've overwritten my table Installation and added a field NbrEquipements.
I want to fill this field with Linq, but I'm stuck...
Due to special reasons, there is no relation between these to tables. So, no Installation.Equipements member into my class. Therefore, no Installation.Equipements.Count...
I'm trying some stuff. Here is my code:
var query = RepoInstallation.AsQueryable();
// Some filter
query = query.Where(i => i.City.RegionId == pRegionId));
int?[] etatIds = { 2, 3 };
query = (from i in query
select new Installation
{
NbrEquipements= (from e in RepoEquipement.AsQueryable()
where e.InstallationSpecialId == i.SpecialId
&& (etatIds.Contains(e.EquEtat))
select e.SasId
).Count()
});
But with this try, I got this error:
The entity or complex type 'myModel.Installation' cannot be constructed in a LINQ to Entities query
I've tried some other stuff but I'm always turning around...
Another thing that can be useful for me: It would be great to fill a field called Equipements which is a List<Equipement>.
After that, I would be able to Count this list...
Is it possible ?
Tell me if I'm not clear.
Thanks in advance.
Here is the final code:
//In the class:
[Dependency]
public MyEntities MyEntities { get; set; }
//My Methode code:
var query = MyEntities .SasInstallations.AsQueryable();
// Some filter
query = query.Where(i => i.City.RegionId == pRegionId));
var liste = new List<Installation>();
var queryWithListEquipements =
from i in query
select new
{
Ins = i,
EquipementsTemp = (from eq in MyEntities.Equipements.AsQueryable()
where eq.SpecialId == i.SpecialId
&& (etatIds.Contains(eq.SasEquEtat))
select eq
).ToList()
};
var listWithListEquipements = queryWithListEquipements.ToList();
foreach (var anonymousItem in listWithListEquipements)
{
var ins = anonymousItem.Ins;
ins.Equipements = anonymousItem.EquipementsTemp;
ins.NumberEquipements = ins.Equipements.Count();
liste.Add(ins);
}
return liste;
By the way, this is very very fast (even the listing of Equipements). So this is working exactly has I wished. Thanks again for your help everyone!
Use an anonymous type. EF does not like to instantiate entity classes inside a query.
var results = (from i in query
select new
{
NbrEquipements= (from e in RepoEquipement
where e.InstallationSpecialId == i.SpecialId
&& (etatIds.Contains(e.EquEtat))
select e.SasId
).Count()
})
.ToList();
Notice how I used select new instead of select new Installation.
You can then use the data inside the list (which is now in memory) to create instances of type Installation if you want like this:
var installations = results.Select(x =>
new Installation
{
NbrEquipements = x.NbrEquipements
}).ToList();
Here is how to obtain the list of equipment for each installation entity:
var results = (from i in query
select new
{
Installation = i,
Equipment = (from e in RepoEquipement
where e.InstallationSpecialId == i.SpecialId
&& (etatIds.Contains(e.EquEtat))
select e).ToList()
})
.ToList();
This will return a list of anonymous objects. Each object will contain a property called Installation and another property called Equipment (which is a list). You can easily convert this list (of anonymous objects) to another list of whatever type that you want.

Reuse Object Creation Code With Entity Framework - Project to new class querying only the required columns

Can the select new MobileTeamModel be refactored into a reusable method and still be read by Entity Framework? I have quite a bit of requests that need this same data and would like to reuse it but I know Entity Framework complains about this type of stuff.
var teams = new MobileListResponse<MobileTeamModel>
{
List = (from e in _divisionsRepository.DataContext.DivisionTeams.Where(#where.Expand())
orderby e.Team.Name
select new MobileTeamModel
{
Id = e.Id,
Name = e.Team.Name,
Status = e.Status,
Paid = e.Paid,
Division = e.Division.Name,
City = e.Team.TeamAddress.Address.City,
StateRegion =
e.Team.TeamAddress.Address.StateRegionId.HasValue
? e.Team.TeamAddress.Address.StateRegion.Name
: null
}).ToList()
};
EDIT
The idea is to implement the select new MobileTeamModel { ... } in a reusable way, while having EF only query the required columns.
Probably a nice extension method:
public static class MobileTeamModelExtensions
{
public static IEnumerable<MobileTeamModel> ToMobileTeamModels
(this IQueryable<DivisionTeam> instance)
{
var result = instance.Select(e =>
select new MobileTeamModel
{
Id = e.Id,
Name = e.Team.Name,
Status = e.Status,
Paid = e.Paid,
Division = e.Division.Name,
City = e.Team.TeamAddress.Address.City,
StateRegion =
e.Team.TeamAddress.Address.StateRegionId.HasValue
? e.Team.TeamAddress.Address.StateRegion.Name
: null
}).ToList()
return result;
}
}
So you could:
var query = _divisionsRepository.DataContext.DivisionTeams
.Where(#where.Expand());
var list = query.ToMobileTeamModels();
var query = query.Where(<more where>);
var list2 = query.ToMobileTeamModels();
You simply have to add a constructor or a factory (static method that return a MobileTeamModel) and receives a Team.
Then you can make a simpler query like this:
select new MobileTeamModel(e.Team) // parametreized constructor
or this
select MobileTeamModel.FromTeam(e.Team) // factory
Finallyyou can use something like AutoMapper or ValueInjecter to project the data returned by the query to another class by using conventions or mapping.
NOTE: I can't see how your clasees look like, but this is the basic idea.
EDIT: getting EF to query only the necessary columns
As for your comment, you want to reuse the construction part, and have EF only query the required columns, and not he whole entities.
Another good explanation here: Los Techies. Efficient querying with LINQ, AutoMapper and Future queries
To do so you must use Automapper, with Queryable Extensions.
WHen you use this extensions, you can use a mapping from a query result to the destination class, and have EF only query the mapped columns.

How can I query hierarchies with LinqToSQL?

I have a hierarchy that I'd like to query with LinqToSql:
Country -> Region -> City -> ZipCode
Each entity holds both a reference to it's parent (eg. Region.Country) and a collection of it's children (eg. Region.Cities).
I'd like to eager load each entity's parent along with Countries and Regions but lazy load cities and zip codes.
To complicate things, each entity is being localized before being projected in to the model. So Country.Name changes based on the language.
Here's some snippets of what I have so far:
public IQueryable<Country> ListCountries()
{
return ProjectCountry(dataContext.GetTable<ec_Country>());
}
private IQueryable<Country> ProjectCountry(IQueryable<ec_Country> query)
{
var result = from country in query
join localized in dataContext.GetTable<ec_CountryLocalization>() on country.CountryID equals localized.CountryID
let regions = GetRegions(country.CountryID)
where localized.StatusID == 4 && localized.WebSiteID == this.webSiteID
select new Country(country.CountryID) {
CreatedDate = country.CreatedDate,
IsDeleted = country.IsDeleted,
IsoCode = country.IsoCode,
Name = country.Name,
Regions = new LazyList<Region>(regions),
Text = localized.Text,
Title = localized.Title,
UrlKey = country.UrlKey
};
return result;
}
private IQueryable<Region> GetRegions(Int32 countryID)
{
var query = from r in dataContext.GetTable<ec_Region>()
where r.CountryID == countryID
orderby r.Name
select r;
return ProjectRegion(query);
}
private IQueryable<Region> ProjectRegion(IQueryable<ec_Region> query)
{
var result = from region in query
join localized in dataContext.GetTable<ec_RegionLocalization>() on region.RegionID equals localized.RegionID
join country in ListCountries() on region.CountryID equals country.CountryID
let cities = GetCities(region.RegionID)
select new Region(region.RegionID) {
Cities = new LazyList<City>(cities),
Country = country,
CountryID = region.CountryID,
CreatedDate = region.CreatedDate,
IsDeleted = region.IsDeleted,
IsoCode = region.IsoCode,
Name = region.Name,
Text = localized.Text,
Title = localized.Title,
UrlKey = region.UrlKey
};
return result;
}
... etc.
[TestMethod]
public void DataProvider_Correctly_Projects_Country_Spike()
{
// Act
Country country = dataProvider.GetCountry(1);
// Assert
Assert.IsNotNull(country);
Assert.IsFalse(String.IsNullOrEmpty(country.Description));
Assert.IsTrue(country.Regions.Count > 0);
}
The test fails with:
System.NotSupportedException: Method 'System.Linq.IQueryable`1[Beeline.EducationCompass.Model.Region] GetRegions(Int32)' has no supported translation to SQL.
How would you recommend I go about this? Would it be simpler (or possible) if each level of the hierarchy was in the same table instead of separate ones?
You're going to want to use the linq designer to set up relationships between your objects. This gets you out of writing join after join after join by creating properties.
between a Country and its Regions
between a Region and its Cities
between a Country and its Localizations
between a Region and its Localizations
You're going to want to use ToList to seperate those operations you intend to be translated into SQL, and those operations you intend to be done in local code. If you don't do this, you'll keep seeing those "cannot translate your method into SQL" exceptions.
You're also going to want to use DataLoadOptions to eagerly load these properties in some cases. Here's my stab at it.
DataLoadOptions dlo = new DataLoadOptions();
//bring in the Regions for each Country
dlo.LoadWith<ec_Country>(c => c.Regions);
//bring in the localizations
dlo.AssociateWith<ec_Country>(c => c.Localizations
.Where(loc => loc.StatusID == 4 && loc.WebSiteID == this.webSiteID)
);
dlo.AssociateWith<ec_Region>(r => r.Localizations);
//set up the dataloadoptions to eagerly load the above.
dataContext.DataLoadOptions = dlo;
//Pull countries and all eagerly loaded data into memory.
List<ec_Country> queryResult = query.ToList();
//further map these data types to business types
List<Country> result = queryResult
.Select(c => ToCountry(c))
.ToList();
public Country ToCountry(ec_Country c)
{
return new Country()
{
Name = c.Name,
Text = c.Localizations.Single().Text,
Regions = c.Regions().Select(r => ToRegion(r)).ToList()
}
}
public Region ToRegion(ec_Region r)
{
return new Region()
{
Name = r.Name,
Text = r.Localizations.Single().Text,
Cities = r.Cities.Select(city => ToCity(city)).ToLazyList();
}
}
That's one sticky piece of code, and I wouldn't have answered this due to lack of relevant skill if anyone else had, but since you had no responses...
I can tell you what the error message means. It means the function GetRegions can't be translated into sql by the linq to sql provider. Some built-in functions can be, because the provider understands them, here is a list. Otherwise you can provide translations see here.
In your situation you need to 'inline' the logic of this query, the logic won't cross the boundary of a function call, because you are dealing with an expression tree, the sql server can't call back into your GetRegions method.
As to the exact way to do that, you'll have to have a go, I don't have the time to oblige you at the moment. (Unless someone else has time and skill?)
Good luck.

Categories

Resources