Context SqlQuery<>() without detailing all columns - entity-framework

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);
}
}
}
}

Related

Linq query to join three tables and return a object along with a list of another object in Entity Framework Core

I have three tables, Organization, Department, and OrganizationDepartments. here is the relationship between them.
Now I would like to join these three tables and create another object for a DTO class. This DTO object has some properties and a list of other DTOs. Here is the DTO Class.
Organization DTO:
public class OrganizationDto
{
public string Id { get; set; }
public string OrganizationName { get; set; }
public string Logo { get; set; }
public bool? IsActive { get; set; }
public IList<OrganizationDepartmentDto> OrganizationDepartments { get; set; }
}
OrganizationDepartment DTO:
public class OrganizationDepartmentDto
{
public string OrganizationId { get; set; }
public string OrganizationName { get; set; }
public string DepartmentId { get; set; }
public string DepartmentName { get; set; }
}
Now I would like to write a LINQ query to get a Organization object along with all the departments related to that organization. The query is imcomplete because I don't know how can I get all the department information as list in a single query. The code is below:
var organizationInfo = (from org in _dbContext.Organizations
join orgDept in _dbContext.OrganizationDepartments on org.Id equals orgDept.OrganizationId
join dept in _dbContext.Departments on orgDept.DepartmentId equals dept.Id
where org.Id.ToUpper() == id.ToUpper()
orderby org.CreatedOn ascending
select new OrganizationDto
{
Id = org.Id,
OrganizationName = org.OrganizationName,
Logo = org.Logo,
IsActive = org.IsActive,
OrganizationDepartments = //TODO:..
}
);
Can anyone help me to get the department lists of that organization's object (see the TODO:)?
If your entities are mapped correctly, and the relationships are correctly configured.
you can use .Include("OrganizationDepartment") and .ThenInclude("Department")to ensure relations are included into the generated Query.
If you insist on using Query Syntax. e.g from org in context.Organization
you can write out the query like this.
var q = (from org in _dbContext.Organizations
where org.Id.ToUpper() == id.ToUpper()
orderby org.CreatedOn ascending
select new OrganizationDto
{
Id = org.Id,
OrganizationName = org.OrganizationName,
Logo = org.Logo,
IsActive = org.IsActive,
OrganizationDepartments = org.OrganizationDepartments.ToList()
}
Depending on your usecase. Sometimes you are not interested in actually showing the "many to many" table outside of the scope of your database.
so it might make more sense to actually flatten the Dto.
that query would look like
var q = (from org in _dbContext.Organizations
where org.Id.ToUpper() == id.ToUpper()
orderby org.CreatedOn ascending
select new OrganizationDto
{
Id = org.Id,
OrganizationName = org.OrganizationName,
Logo = org.Logo,
IsActive = org.IsActive,
Departments= org.OrganizationDepartments.Select(t => t.Departments).ToList()
}

Inlining collections with EF Projection

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.

how to read data from junction table code first approach

how can I retrieve data from junction table in Code first approach.i know i can add data like this.
Movie.Tag.Add(item)
but want to know how to get back those data in the junction table
This gives you all rows in the junction table - each row is represented as an anonymous object with the two Ids of the row as properties:
var junctionTableDataList = (from m in context.Movies
from t in m.Tags
select new
{
MovieId = m.MovieId,
TagId = t.TagId
}).ToList();
You can add a where clause before the select if you want to limit the result to a specific or a few movies.
Edit
The same with extension methods and lambda expressions would look like this:
var junctionTableDataList = context.Movies
.SelectMany(m => m.Tags.Select(t => new
{
MovieId = m.MovieId,
TagId = t.TagId
}))
.ToList();
Edit 2
If you want return the data from a method you can create a litte helper type and return a list of those "named" objects instead of anonymous objects:
public class JunctionData
{
public int MovieId { get; set; }
public int TagId { get; set; }
}
List<JunctionData> junctionTableDataList
= (from m in context.Movies
from t in m.Tags
select new JunctionData
{
MovieId = m.MovieId,
TagId = t.TagId
}).ToList();
Or ...Select(t => new JunctionData... for the extension method syntax.

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);
}

Parse string to poco class enum in linq query MVC 2.0

I have the following enum and POCO class
public enum Gender
{
Male,
Female,
Unknown
}
public class Person
{
public int PersonId { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public Gender? Gender { get; set; }
}
I would like to perform a "get all people" query in my repository such that it would look something like this:
return from p in _db.People
select new Model.Person
{
PersonId = p.PersonId,
LastName = p.LastName,
FirstName = p.FirstName,
Gender = p.Gender,
};
Unfortunately I get an error "Cannot implicitly convert type 'string' to 'Model.Gender'"
I would like to convert the string which is being queried from the entity framework to my Gender enum and assign it to my POCO class.
Enums are not supported in Entity Framework. There is a workaround by Alex James, but it's quite involved.
Instead, i prefer to do this:
public enum Gender : byte
{
Male = 1,
Female,
Unknown
}
public class Person
{
public int PersonId { get; set; }
public string LastName { get; set; }
public string FirstName { get; set; }
public byte Gender { get; set; } // this is the EF model property
public Gender GenderType // this is an additional custom property
{
get { return (Gender) Gender; }
set { Gender = (byte)value; }
}
}
It's basically a hook/wrapper for the actual value. In your database, store Gender as a tinyint (which maps to byte on the conceptual side).
Then you can use a byte enum to map to and from the model property:
return from p in _db.People
select new Model.Person
{
PersonId = p.PersonId,
LastName = p.LastName,
FirstName = p.FirstName,
Gender = p.Gender, // sets byte
};
But then if you access that ViewModel, because your setting the byte field for Gender, you will also have access to the enum property GenderType.
Does that solve your problem?
The Entity Framework that I am familiar with does not provide support for enums. EF uses your query expression to create an SQL statement that it then sends to the server, if it cannot create the SQL equivalent of some operation it will throw a NotSupportedException for that operation. If you are expecting to return a small set of data you can separate from the Entity Framework by creating an object in memory using the ToArray method.
var myEntities = (from entity in _db.Entities
where /* condition */
select entity)
.ToArray();
This will create a sequence of entities in memory. Any further query statements will then be in the realm of LINQ to Objects which allows parsing of strings into enums:
return from myEntity in myEntities
select new MyDataContract
{
ID = myEntity.ID,
Gender g = (Gender)Enum.Parse(typeof(Gender), myEntity.Gender, true)
};
Or you could even break it out into a foreach loop:
List<MyDataContract> myDataContracts = new List<MyDataContract>();
foreach (var myEntity in myEntities)
{
var dataContract = new MyDataContract { ID = myEntity.ID };
if (Enum.IsDefined(typeof(Gender), myEntity.Gender))
dataContract.Gender = (Gender)Enum.Parse(typeof(Gender), myEntity.Gender, true);
myDataContracts.Add(dataContract);
}
return myDataContracts.AsEnumerable();
if (Enum.IsDefined(typeof(Gender), genderstring))
Gender g = (Gender) Enum.Parse(typeof(Gender), genderstring, true);
else
//Deal with invalid string.
try
Gender = p.Gender != null ? (Gender)Enum.Parse(typeof(Gender), p.Gender) : (Gender?)null;
To parse the string as one of the enums
here's a workaround but it means changing your nice and clean POCO
http://blogs.msdn.com/b/alexj/archive/2009/06/05/tip-23-how-to-fake-enums-in-ef-4.aspx