I have a question about selecting specific columns from table using entity framework. The problem is, that I'm using Find() method to get my desired table, by primary key, then taking from it some data.
I have one table with massive amounts of columns and if I call Find() method, it will return all columns of that row, but I want to use only, for example, the data from 2 columns.
MyTable table = context.MyTable.Find(id); //Get MyTable object from context, id = primary key
string p1 = table.Prop1;
string p2 = table.Prop2;
This will return single object with all (for example it has Prop1, Prop2,...,PropN) properties filled (if its filled in database).
So I know that I can use anonymous objects or data transfer objects (DTO), but [question1] is there any other (yet simple) method to get specific columns? [question2] Is it affecting on performance if I use Find() (or I should use Where()/Select())?
var items = context.MyTable.Where(x => x.Id == id)
.Select(x => new
{
P1 = table.Prop1,
P2 = table.Prop2
});
This will translate into a sql call like:
SELECT p.Prop1, p.Prop2 FROM mytable p WHERE p.Id = id
Use Data Transfer Objects: DTO, which is a recommened microsoft pattern.
Putting it simple, they are just objects that hold data.
Then do like someone suggested:
public class MyDto
{
public string Prop1 {get;set;} = String.Empty
public string Prop2 {get;set;} = String.Empty
}
MyDto x = new MyDto();
x = context.MyTable.Where(x => x.Id == id)
.Select(x => new MyDto
{
P1 = table.Prop1
//I don't want prop 2, for example
});
And pass around the object. Set defaults for Auto Properties (C# 6 and up) and initialize only the properties you want.
EDIT:
I've read you don't want to use anonymous and DTO, then how you want to do it. You either use objects or anonymous.
Other ways is just build a layered structure and call the query method directly where you need it. Patterns exists for a reason.
You can call queries against Dynamic objects. With these you may assign fields that will be resolved at runtime, at the cost of losing strong typing.
You might also want to check if it's performance-whorty to use dynamics.
Another option is to project the class back to itself, and only provide the columns you want.
var table = context.MyTable.Where(mt => mt.Id == id)
.Select(mt => new MyTable
{
Prop1 = mt.Prop1,
Prop2 = mt.Prop2
})
.FirstOrDefault();
string p1 = table.Prop1;
string p2 = table.Prop2;
Effectively, you get the strong typing of a DTO without having to create/maintain another class. All columns not specified will be populated with the default value of the column's type.
It translates to the following in SQL:
SELECT TOP(1) m.Prop1, m.Prop2 FROM MyTable m WHERE m.Id = #id
Which indeed gives a performance boost over Find() assuming you're not specifying all the columns.
EDIT: As Gert mentioned, use with caution, as it's not always obvious when a "partial entity" is being passed around.
You can use free AutoMapper's ProjectTo<> extension, so the query would look like this:
context.OrderLines
.Where(ol => ol.OrderId == orderId)
.ProjectTo<OrderLineDTO>(configuration)
.ToList();
Related
I have a database that contains 3 tables:
Phones
PhoneListings
PhoneConditions
PhoneListings has a FK from the Phones table(PhoneID), and a FK from the Phone Conditions table(conditionID)
I am working on a function that adds a Phone Listing to the user's cart, and returns all of the necessary information for the user. The phone make and model are contained in the PHONES table, and the details about the Condition are contained in the PhoneConditions table.
Currently I am using 3 queries to obtain all the neccesary information. Is there a way to combine all of this into one query?
public ActionResult phoneAdd(int listingID, int qty)
{
ShoppingBasket myBasket = new ShoppingBasket();
string BasketID = myBasket.GetBasketID(this.HttpContext);
var PhoneListingQuery = (from x in myDB.phoneListings
where x.phonelistingID == listingID
select x).Single();
var PhoneCondition = myDB.phoneConditions
.Where(x => x.conditionID == PhoneListingQuery.phonelistingID).Single();
var PhoneDataQuery = (from ph in myDB.Phones
where ph.PhoneID == PhoneListingQuery.phonePageID
select ph).SingleOrDefault();
}
You could project the result into an anonymous class, or a Tuple, or even a custom shaped entity in a single line, however the overall database performance might not be any better:
var phoneObjects = myDB.phoneListings
.Where(pl => pl.phonelistingID == listingID)
.Select(pl => new
{
PhoneListingQuery = pl,
PhoneCondition = myDB.phoneConditions
.Single(pc => pc.conditionID == pl.phonelistingID),
PhoneDataQuery = myDB.Phones
.SingleOrDefault(ph => ph.PhoneID == pl.phonePageID)
})
.Single();
// Access phoneObjects.PhoneListingQuery / PhoneCondition / PhoneDataQuery as needed
There are also slightly more compact overloads of the LINQ Single and SingleOrDefault extensions which take a predicate as a parameter, which will help reduce the code slightly.
Edit
As an alternative to multiple retrievals from the ORM DbContext, or doing explicit manual Joins, if you set up navigation relationships between entities in your model via the navigable join keys (usually the Foreign Keys in the underlying tables), you can specify the depth of fetch with an eager load, using Include:
var phoneListingWithAssociations = myDB.phoneListings
.Include(pl => pl.PhoneConditions)
.Include(pl => pl.Phones)
.Single(pl => pl.phonelistingID == listingID);
Which will return the entity graph in phoneListingWithAssociations
(Assuming foreign keys PhoneListing.phonePageID => Phones.phoneId and
PhoneCondition.conditionID => PhoneListing.phonelistingID)
You should be able to pull it all in one query with join, I think.
But as pointed out you might not achieve alot of speed from this, as you are just picking the first match and then moving on, not really doing any inner comparisons.
If you know there exist atleast one data point in each table then you might aswell pull all at the same time. if not then waiting with the "sub queries" is nice as done by StuartLC.
var Phone = (from a in myDB.phoneListings
join b in myDB.phoneConditions on a.phonelistingID equals b.conditionID
join c in ph in myDB.Phones on a.phonePageID equals c.PhoneID
where
a.phonelistingID == listingID
select new {
Listing = a,
Condition = b,
Data = c
}).FirstOrDefault();
FirstOrDefault because single throws error if there exists more than one element.
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.
In sql server, I have two tables (lets say Person and School). I want to return the Person.Name from one table and School.Name (since they are related I want the primary key of the Person also).
So the question here is can I return these data and make them all in a list? also for the return type, does it request that I create a custom type for it (I mean class)?
Edit: Sorry I forget to metion that I need it in LINQ.
Thanks,
Maybe there is a better way to do this, but I would create a new class and return a list of these objects. Like this:
First do a LiNQ query to get the values we need:
var personWithSchoolName = select p from db.Person
select s from db.School
where p.SchoolId == s.SchoolId
select new { p.PersonId as PersonId, p.Name as PersonName, s.Name as SchoolName };
peopleWithSchoolNamesList = personWithSchoolName.ToList();
Then loop through the results and add these to a new list:
foreach(object o in peopleWithSchoolNamesList)
{
PersonWithSchoolNameObject personWithSchoolNameObject = new PersonWithSchoolNameObject(o.PersonId, o.PersonName, o.SchoolName);
ListWithPeople.Add(PersonWithSchoolNameObject);
}
return ListWithPeople;
You can use Join operation of LINQ to combine data from more than one table
For return purposes you will need a Concrete class. You cannot return the results of LINQ query i.e. an anonymous type, directly.
Try this
SELECT Person.Id, Person.Name, School.Name AS SchoolName
from Person
LEFT JOIN School ON Person.SchoolId = School.Id
(actually you have to use well prepared linq to return this, yeah?)
and wrap the results into special predefined class like:
class PersonWithSchool
{
public int PersonId;
public string PersonName;
public string SchoolName;
}
I have a MembershipGroups table that is associated with a child Members table. The Members table has a Status column which can be set to Active or Inactive.
I want to select all MembershipGroups and only their active Members
As an example,
MembershipGroups
ID----Title
1-----Group #1
2-----Group #2
Members
MembershipGroupID-Name--Status
1-------------------------John----Active
1-------------------------Sally----Inactive
1-------------------------David---Inactive
I'm trying to create a query that looks something like the following (which doesn't currently work):
var query = from mg in db.MembershipGroups
where mg.Members.Status = "Active"
select mg
The result for this example should return a MembershipGroup of ID#1 with only one child Member entity
How can use LINQ-to-SQL to select a parent object that filters on child objects? If I were using straight T-SQL then this would be a simple join with a where clause but it seems to be much more difficult to do using LINQ-to-SQL.
Edit - Updated answer to return the MemberShipGroup object
var query = (from mg in db.MembershipGroups
join m in db.Members.Where(mem => mem.Status == "Active")
on mg.ID equals m.MembershipGroups into members
select new
{
MembershipGroup = mg,
Members = members
}).AsEnumerable()
.Select(m => new MembershipGroup
{
ID = m.MembershipGroup.ID,
Title = m.MembershipGroup.Title,
Members = m.Members
});
In LINQ to SQL, you can use the AssociateWith method on the DataLoadOptions to set your child filter at the context level.
DataLoadOptions opt = new DataLoadOptions();
opt.AssociateWith<Member>(m => m.Status == "Active");
db.LoadOptions = opt;
With this in place, you can simply return your member groups (or filter them for the active ones using where mg.Any(group => group.Members.Status == "Active"). Then when you try to drill into the Members of that group, only the Active ones will be returned due to the LoadOptions.
See also http://msdn.microsoft.com/en-us/library/system.data.linq.dataloadoptions.associatewith.aspx .
One word of warning, once you set the LoadOptions on a context instance, you can not change it. You may want to use a customized context to use this option.
As an alternative, you could use LINQ to SQL's inheritance model to create an ActiveMember type using the Status column as your discriminator and then create an association between the MemberGroups and ActiveMembers types. This would be the approach you would need to use to model this with the Entity Framework if you though about going that route as well as EF doesn't support the concept of the LoadOptions.
Make sure you are including the child objects you are trying to filter on, inside the query.
E.g.
var query = db.MembershipGroups
.Include("Members")
.Where(m => m.Members.Status == "Active");
This is easy for me to perform in TSQL, but I'm just sitting here banging my head against the desk trying to get it to work in EF4!
I have a table, lets call it TestData. It has fields, say: DataTypeID, Name, DataValue.
DataTypeID, Name, DataValue
1,"Data 1","Value1"
1,"Data 1","Value2"
2,"Data 1","Value3"
3,"Data 1","Value4"
I want to group on DataID/Name, and concatenate DataValue into a CSV string. The desired result should contain -
DataTypeID, Name, DataValues
1,"Data 1","Value1,Value2"
2,"Data 1","Value3"
3,"Data 1","Value4"
Now, here's how I'm trying to do it -
var query = (from t in context.TestData
group h by new { DataTypeID = h.DataTypeID, Name = h.Name } into g
select new
{
DataTypeID = g.Key.DataTypeID,
Name = g.Key.Name,
DataValues = (string)g.Aggregate("", (a, b) => (a != "" ? "," : "") + b.DataValue),
}).ToList()
The problem is that LINQ to Entities does not know how to convert this into SQL. This is part of a union of 3 LINQ queries, and I'd really like it to keep it that way. I imagine that I could retrieve the data and then perform the aggregate later. For performance reasons, that wouldn't work for my app. I also considered using a SQL server function. But that just doesn't seem "right" in the EF4 world.
Anyone care to take a crack at this?
If the ToList() is part of your original query and not just added for this example, then use LINQ to Objects on the resulting list to do the aggregation:
var query = (from t in context.TestData
group t by new { DataTypeID = t.DataTypeID, Name = t.Name } into g
select new { DataTypeID = g.Key.DataTypeID, Name = g.Key.Name, Data = g.AsEnumerable()})
.ToList()
.Select (q => new { DataTypeID = q.DataTypeID, Name = q.Name, DataValues = q.Data.Aggregate ("", (acc, t) => (acc == "" ? "" : acc + ",") + t.DataValue) });
Tested in LINQPad and it produces this result:
Some of the Answers suggest calling ToList() and then perform the calculation as LINQ to OBJECT. That's fine for a little amount of data, but what if I have a huge amount of data that I do not want to load into memory too early, then, ToList() may not be an option.
So, the better idea would be to process/format the data in the presentation layer and let the Data Access layer do only loading or saving raw data that SQL likes.
Moreover, in your presentation layer, most probably you are filtering the data by paging, or maybe you are showing one row in the details page, so, the data you will load into the memory is likely smaller than the data you load from the database. (Your situation/architecture may be different,.. but I am saying, most likely).
I had a similar requirement. My problem was to get the list of items from the Entity Framework object and create a formatted string (comma separated value)
I created a property in my View Model which will hold the raw data from the repository and when populating that property, the LINQ query won't be a problem because you are simply querying what SQL understands.
Then, I created a get-only property in my ViewModel which reads that Raw entity property and formats the data before displaying.
public class MyViewModel
{
public IEnumerable<Entity> RawChildItems { get; set; }
public string FormattedData
{
get
{
if (this.RawChildItems == null)
return string.Empty;
string[] theItems = this.RawChildItems.ToArray();
return theItems.Length > 0
? string.Format("{0} ( {1} )", this.AnotherRegularProperty, String.Join(", ", theItems.Select(z => z.Substring(0, 1))))
: string.Empty;
}
}
}
Ok, in that way, I loaded the Data from LINQ to Entity to this View Model easily without calling.ToList().
Example:
IQueryable<MyEntity> myEntities = _myRepository.GetData();
IQueryable<MyViewModel> viewModels = myEntities.Select(x => new MyViewModel() { RawChildItems = x.MyChildren })
Now, I can call the FormattedData property of MyViewModel anytime when I need and the Getter will be executed only when the property is called, which is another benefit of this pattern (lazy processing).
An architecture recommendation: I strongly recommend to keep the data access layer away from all formatting or view logic or anything that SQL does not understand.
Your Entity Framework classes should be simple POCO that can directly map to a database column without any special mapper. And your Data Access layer (say a Repository that fetches data from your DbContext using LINQ to SQL) should get only the data that is directly stored in your database. No extra logic.
Then, you should have a dedicated set of classes for your Presentation Layer (say ViewModels) which will contain all logic for formatting data that your user likes to see. In that way, you won't have to struggle with the limitation of Entity Framework LINQ. I will never pass my Entity Framework model directly to the View. Nor, I will let my Data Access layer creates the ViewModel for me. Creating ViewModel can be delegated to your domain service layer or application layer, which is an upper layer than your Data Access Layer.
Thanks to moi_meme for the answer. What I was hoping to do is NOT POSSIBLE with LINQ to Entities. As others have suggested, you have to use LINQ to Objects to get access to string manipulation methods.
See the link posted by moi_meme for more info.
Update 8/27/2018 - Updated Link (again) - https://web.archive.org/web/20141106094131/http://www.mythos-rini.com/blog/archives/4510
And since I'm taking flack for a link-only answer from 8 years ago, I'll clarify just in case the archived copy disappears some day. The basic gist of it is that you cannot access string.join in EF queries. You must create the LINQ query, then call ToList() in order to execute the query against the db. Then you have the data in memory (aka LINQ to Objects), so you can access string.join.
The suggested code from the referenced link above is as follows -
var result1 = (from a in users
b in roles
where (a.RoleCollection.Any(x => x.RoleId = b.RoleId))
select new
{
UserName = a.UserName,
RoleNames = b.RoleName)
});
var result2 = (from a in result1.ToList()
group a by a.UserName into userGroup
select new
{
UserName = userGroup.FirstOrDefault().UserName,
RoleNames = String.Join(", ", (userGroup.Select(x => x.RoleNames)).ToArray())
});
The author further suggests replacing string.join with aggregate for better performance, like so -
RoleNames = (userGroup.Select(x => x.RoleNames)).Aggregate((a,b) => (a + ", " + b))
You are so very close already. Try this:
var query = (from t in context.TestData
group h by new { DataTypeID = h.DataTypeID, Name = h.Name } into g
select new
{
DataTypeID = g.Key.DataTypeID,
Name = g.Key.Name,
DataValues = String.Join(",", g),
}).ToList()
Alternatively, you could do this, if EF doesn't allow the String.Join (which Linq-to-SQL does):
var qs = (from t in context.TestData
group h by new { DataTypeID = h.DataTypeID, Name = h.Name } into g
select new
{
DataTypeID = g.Key.DataTypeID,
Name = g.Key.Name,
DataValues = g
}).ToArray();
var query = (from q in qs
select new
{
q.DataTypeID,
q.Name,
DataValues = String.Join(",", q.DataValues),
}).ToList();
Maybe it's a good idea to create a view for this on the database (which concatenates the fields for you) and then make EF use this view instead of the original table?
I'm quite sure it's not possible in a LINQ statement or in the Mapping Details.