Inlining collections with EF Projection - entity-framework

I have the following classes:
public class Customer {
public int Id {get;set;}
public string Name {get;set;}
public List<Order> Orders {get;set;}
//other attributes
}
public class Order{
public int Id {get;set;}
public string Name {get;set;}
public decimal Value {get;set;}
}
Given a customerId I wish to only select the customer name and the order Id using projection in EF.
I am doing the following:
IQueryable<Customer> customer = DataContextFactory.GetDataContext().Set<Customer>();
var tempCustomer = customer.Where(x => x.Id == customerId).Select( c=>
new
{
Name = c.Name
}
)
This gives me the customer name. Which I can then pass back to the entity like so:
var customerToReturn = tempCustomer.ToList().Select(x => new Customer
{ Name = x.Name});
If I include the order on the query like this:
var tempCustomer = customer.Where(x => x.Id == customerId).Select( c=>
new
{
Name = c.Name,
Orders = new {
Id = c.Orders.Id
}
}
)
Then I get a duplicate customer per order line (as per the SQL generated by EF). Is there a way I can inline this generation into a single SQL call?
Currently I am getting around this by calling each child object separately.

Related

list in entity framework and webapi

I am trying to return employee with three column in .net webapi with entity framework but return gives error saying cannot convert type pf anonymous to emp ..what I am missing
public List<EMPLOYEE_DTLS> GetLoginInfo(string UserId)
{
var result = (from emp in db.EMPLOYEE_DTLS
where emp.UserId == UserId
select new
{
emp.FullName,
emp.Role,
emp.Designation
}).ToList();
return result;
}
You should use a DTO to return the list.
public class EmployeeDTO
{
public string FullName {get; set;}
public string Role {get; set;}
public string Designation {get; set;}
}
public List<EmployeeDTO> GetLoginInfo(string UserId)
{
var result = (from emp in db.EMPLOYEE_DTLS
where emp.UserId == UserId
select new EmployeeDTO
{
FullName = emp.FullName,
Role = emp.Role,
Designation = emp.Designation
}).ToList();
return result;
}
Your method returns List<EMPLOYEE_DTLS>, but you are trying to return List of anonymous type.
Assuming you EMPLOYEE_DTLS class has properties FullName, Role and Designation change the type you are selecting:
public List<EMPLOYEE_DTLS> GetLoginInfo(string UserId)
{
var result = (from emp in db.EMPLOYEE_DTLS
where emp.UserId == UserId
select new EMPLOYEE_DTLS
{
FullName = emp.FullName,
Role = emp.Role,
Designation = emp.Designation
}).ToList();
return result;
}

Automapper MapFrom Subquery

UPDATE: Issue fixed in current release https://github.com/AutoMapper/AutoMapper/issues/742
Using AutoMapper 3.3, QueryableExtensions and EF6
I have a user requirement to return a Count of other users created before the current user.
I have the following
public class User
{
public int Id {get;set;}
public string Name { get; set; }
public DateTime? DateActivated {get;set;}
}
public class UserViewModel
{
public int Id {get;set;}
public string Name { get; set; }
public DateTime? DateActivated {get;set;}
public int position {get;set;}
}
public class AutoMapperConfig
{
public static void ConfigAutoMapper() {
var db = new DB();
Mapper.CreateMap<User, UserViewModel>()
.ForMember(a => a.position, opt => opt.MapFrom(src => db.Users.Where(u => u.DateActivated < src.DateActivated).Count()));
Mapper.AssertConfigurationIsValid();
}
}
and finally the actual mapping:
user = db.Users.Project().To<T>(new { db = db }).FirstOrDefault(a => a.id == id);
db is a local DbContext variable and I'm using AutoMapper parameters to insert it into the mapper (https://github.com/AutoMapper/AutoMapper/wiki/Queryable-Extensions#parameterization)
So far so good, this compiles and runs, but the result for user.position is 0
I checked with sql profiler and here is the relevant section of the generated query:
CROSS JOIN (SELECT
COUNT(1) AS [A1]
FROM [dbo].[Users] AS [Extent4]
WHERE ([Extent4].[DateActivated] < [Extent4].[DateActivated]) ) AS [GroupBy1]
Notice how it refers to Extent4.DateActivated in both sides of the comparison, which will obviously yield 0 results.
So is what i'm doing just not possible? or did I do something wrong.
(and if I could do away with the parameterization and have automapper be able to refer to the current underlying db context that would be a bonus).
Thank you
EDIT
Just to make it clear, this count will be dynamic, since there are other criteria to filter prior users that I omitted from simplified the example.

Context SqlQuery<>() without detailing all columns

Let's say I have a Person entity with 3 columns:
public PERSON {
public int OID { get; set; }
public string NAME { get; set; }
public string SURNAME { get; set; }
}
I want to query it with raw SQL but without specifying all columns so I write:
var query = "select NAME, SURNAME from PERSON";
var list = context.SqlQuery<PERSON>(query).ToList();
But it throws Exception:
System.Data.Entity.Core.EntityCommandExecutionException : The data reader is incompatible with the specified '...'. A member of the type, 'OID', does not have a corresponding column in the data reader with the same name.
So it seems like it tries to map all columns, and, if some are missing, it throws.
Is there a way to make it ignore columns that are not present in raw SQL query and map just the columns that are accessible?
The reason is, I have a lot of columns for some entities and sometimes I just want to query partial columns. I don't want to create new class with just the necessary columns for each query.
I can think of 3 options off the top of my head that could work for you.
Option 1: Rewrite your queries and use a standard Linq query:
var persons = from p in context.Persons
select new PERSON
{
NAME = p.NAME,
SURNAME = p.SURNAME
};
Option 2: Return a dummy value for columns you don't need
var query = "select 0 AS OID, NAME, SURNAME from PERSON";
var list = context.SqlQuery<AlmostPERSON>(query).ToList();
Option 3: Create your own intermediate classes with the columns you need from the database:
public class AlmostPERSON
{
public string NAME { get; set; }
public string SURNAME { get; set; }
}
var query = "select NAME, SURNAME from PERSON";
var list = context.SqlQuery<AlmostPERSON>(query).ToList();
You could even project this intermediate class onto your standard entity like this:
var list = context.SqlQuery<AlmostPERSON>(query)
.Select(ap => new PERSON
{
NAME = ap.NAME,
SURNAME = ap.SURNAME
})
.ToList();
Found one more possible solution: using ADO.NET IDataReader with AutoMapper
public List<T> ExecuteFor<T>(string sql)
{
using (_connection = new OracleConnection(_connectionString))
{
_connection.Open();
using (var command = new OracleCommand(sql, _connection))
{
using (OracleDataReader reader = command.ExecuteReader())
{
Mapper.CreateMap<IDataReader, List<T>>();
return Mapper.Map<IDataReader, List<T>>(reader);
}
}
}
}

How to perform a left outer join using Entity Framework using navigation properties

I'm trying to find out how I would define the code first navigation properties on these two classes to perform something similiar to this query:
SELECT USERID, FIRSTNAME, LASTNAME, COURSEID, NAME
FROM User
LEFT OUTER JOIN Course ON User.USERID = Course.USERID
WHERE COURSEID = 1
So I'm trying to find a list of users together with if they have attended a certain course.
public class User
{
public int UserId {get;set; }
public string FirstName {get;set;}
public string LastName {get;set;}
public virtual ICollection<Course> Courses { get; set; }
}
public class Course
{
public int CourseId { get;set; }
public int UserId { get;set; }
public string Name { get;set; }
public virtual User User {get;set;}
}
If I was to write a query to achieve this
var u = Users.Where(x => x.Courses.Any(x => x.CourseId = 1));
This does a subquery, which is not what I wanted (as people who didnt attend the course would not show).
How would the navigation property be defined?
HasMany(t => t.Courses).WithOptional(t => t.User).HasForeignKey(t => t.UserId);
Check this link:
http://msdn.microsoft.com/en-us/library/vstudio/bb397895.aspx
Left outer joins in LINQ are done via DefaultIfEmpty method.
var u = Users.Select ( x => new {
User = x,
AttendedCourse = x.Courses.Any()
} );
For specific course id,
var u = Users.Select ( x => new {
User = x,
AttendedCourse = x.Courses.Any( c => c.CourseID == 1 )
} );
Sub query is the only way to write related queries, However, EF will choose the best suitable join type and will give you correct results. And EF can manage mostly all kinds of queries without doing joins.

Converting ESQL to LINQ to Entities. Sort by related entities

I am using EF + RIA and unfortunately meet some problems with sorting by related entities.
For such purpose there is ESQL query that I implemented (found only this solution):
var queryESQL = string.Format(
#" select VALUE ent from SomeEntities as ent
join Attributes as ea ON ea.EntityId = ent.Id
where ea.AttributeTypeId = #typeId
order by ea.{0} {1}", columnName, descending ? "desc" : "asc");
var query = ObjectContext.CreateQuery<SomeEntity>(queryESQL, new ObjectParameter("typeId", attributeTypeId));
Tables have following structure:
<Attribute>:
int Id;
decimal DecimalColumn;
string StringColumn;
int EntityId;
int AttributeTypeId;
<SomeEntity>:
int Id;
string Name;
Is there any way to rewrite this stuff(sorting), using LINQ to Entities approach?
Here's my attempt, I can't guarantee it will work. I need to think more on how to get a dynamic column name, I'm not sure on that one. EDIT: you can use a string for the order column.
int typeId = 1115;
bool orderAscending = false;
string columnName = "StringColumn";
var query = from ent in SomeEntities
join ea in Attributes on ea.EntityId = ent.Id
where ea.AttributeTypeId = typeId;
if(orderAscending)
{
query = query.OrderBy(ea => columnName).Select(ea => ea.Value);
}
else
{
query = query.OrderByDescending(ea => columnName).Select(ea => ea.Value);
}
var results = query.ToList(); // call toList or enumerate to execute the query, since LINQ has deferred execution.
EDIT: I think that ordering after the select stops is from ordering by. I moved the select statement to after the order by. I also added the "query =", but I'm not sure if that is needed. I don't have a way to test this at the moment.
EDIT 3: I fired up LINQPad today and made a few tweaks to what I had before. I modeled your data in a Code-first approach to using EF and it should be close to what you have.
This approach works better if you're just trying to get a list of Attributes (which you aren't). To get around that I added an Entity property to the MyAttribute class.
This code works in LINQPAD.
void Main()
{
// add test entities as needed. I'm assuming you have an Attibutes collection on your Entity based on your tables.
List<MyEntity> SomeEntities = new List<MyEntity>();
MyEntity e1 = new MyEntity();
MyAttribute a1 = new MyAttribute(){ StringColumn="One", DecimalColumn=25.6M, Id=1, EntityId=1, AttributeTypeId = 1, Entity=e1 };
e1.Attributes.Add(a1);
e1.Id = 1;
e1.Name= "E1";
SomeEntities.Add(e1);
MyEntity e2 = new MyEntity();
MyAttribute a2 = new MyAttribute(){ StringColumn="Two", DecimalColumn=198.7M, Id=2, EntityId=2, AttributeTypeId = 1, Entity=e2 };
e2.Attributes.Add(a2);
e2.Id = 2;
e2.Name = "E2";
SomeEntities.Add(e2);
MyEntity e3 = new MyEntity();
MyAttribute a3 = new MyAttribute(){ StringColumn="Three", DecimalColumn=65.9M, Id=3, EntityId=3, AttributeTypeId = 1, Entity=e3 };
e3.Attributes.Add(a3);
e3.Id = 3;
e3.Name = "E3";
SomeEntities.Add(e3);
List<MyAttribute> attributes = new List<MyAttribute>();
attributes.Add(a1);
attributes.Add(a2);
attributes.Add(a3);
int typeId = 1;
bool orderAscending = true;
string columnName = "StringColumn";
var query = (from ent in SomeEntities
where ent.Attributes.Any(a => a.AttributeTypeId == typeId)
select ent.Attributes).SelectMany(a => a).AsQueryable();
query.Dump("Pre Ordering");
if(orderAscending)
{
// query = is needed
query = query.OrderBy(att => MyEntity.GetPropertyValue(att, columnName));
}
else
{
query = query.OrderByDescending(att => MyEntity.GetPropertyValue(att, columnName));
}
// returns a list of MyAttributes. If you need to get a list of attributes, add a MyEntity property to the MyAttribute class and populate it
var results = query.Select(att => att.Entity).ToList().Dump();
}
// Define other methods and classes here
}
class MyAttribute
{
public int Id { get; set; }
public decimal DecimalColumn { get; set; }
public string StringColumn { get; set; }
public int EntityId { get; set; }
public int AttributeTypeId { get; set; }
// having this property will require an Include in EF to return it then query, which is less effecient than the original ObjectQuery< for the question
public MyEntity Entity { get; set; }
}
class MyEntity
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<MyAttribute> Attributes { get; set; }
public MyEntity()
{
this.Attributes = new List<MyAttribute>();
}
// this could have been on any class, I stuck it here for ease of use in LINQPad
// caution reflection may be slow
public static object GetPropertyValue(object obj, string property)
{
// from Kjetil Watnedal on http://stackoverflow.com/questions/41244/dynamic-linq-orderby
System.Reflection.PropertyInfo propertyInfo=obj.GetType().GetProperty(property);
return propertyInfo.GetValue(obj, null);
}