Present multiple IEnumberables and single value properties in single webgrid - asp.net-mvc-3

I have an Inventory Class that contains not only its own fields but several reference IDs to other classes.
public class Inventory {
public int Id { get; set; }
public string RtNum { get; set; }
public string AcntNum { get; set; }
public string CardNum { get; set; }
public string Num { get; set; }
[Range(1,3)]
public int Type { get; set; }
public int CompanyId { get; set; }
public int BranchId { get; set; }
public int PersonId { get; set; } }
In my action I generate several IEnumerable lists of the relevant fields from the other classes. I also have several non-list values I want to pass to the View. I know how to create a ViewModel to pass everything to the webgrid but have no way of iterating through the lists. I also know how to AutoMap an index to one list, see How to display row number in MVC WebGrid.
How would you combine the two so that you could use the index to iterate through multiple lists?
Update #1 (more detail)
public class Company {
public int Id { get; set; }
public string Name { get; set; } }
public class Branch {
public int Id { get; set; }
public string Name { get; set; } }
public class Person {
public int Id { get; set; }
public string Name { get; set; } }
public class MyViewModel {
public int PageNumber { get; set; }
public int TotalRows { get; set; }
public int PageSize { get; set; }
public IEnumerable<Inventory> Inventories { get; set; }
public int Index { get; set; }
public IEnumerable<string> CmpNm { get; set; }
public IEnumerable<string> BrnNm { get; set; }
public IEnumerable<string> PrnNm { get; set; } }
Controller
public class InventoryController : Controller
{ // I have a paged gird who’s code is not relevant to this discussion but a pagenumber,
// pagesize and totalrows will be generated
private ProjectContext _db = new ProjectContext();
public ActionResult Index() {
IEnumerable<Inventory> inventories = _db.Inventories;
List<string> cmpNm = new List<string>; List<string> brnNm = new List<string>; List<string> prnNm = new List<string>;
foreach (var item in inventories) { string x1 = "";
Company cmps = _db. Company.SingleOrDefault(i => i.Id == item.CompanyId); if (cmps!= null)
{ x1 = cmps.Name; } cmpNm.Add(x1); x1 = "";
Branch brns = _db. Branch.SingleOrDefault(i => i.Id == item. Branch Id); if (brns!= null) { x1 = brns.Name; } brnNm.Add(x1); x1 = "";
Person pers = _db.Persons.SingleOrDefault(i => i.Id == item. PersonId);
if (pers!= null) { x1 = pers.Name; } prnNm.Add(x1);
// the MyViewModel now needs to populated with all its properties and generate an index
// something along the line of
new MyViewModel { PageNumber= pagenumber, PageSize= pagesize, TotalRows=Totalrows, Inventories = inventories; CmpNm=cmpNm, BrnNm=brnNm, PrnNm=prnNm}
View (How to create the Index is the problem)
#model.Project.ViewModels.MyViewModel
#{ var grid = new WebGrid(Model.Inventories, Model.TotalRows, rowsPerPage: Model.PageSize); }
#grid.GetHtml( columns: grid.Columns(
Grid.Column(“PrnNm”, header: "Person", format: #Model.PrnNm.ElementAt(Index))
Grid.Column(“BrnNm”, header: "Branch", format: #Model.BrnNm.ElementAt(Index))
Grid.Column(“CmpNm”, header: "Company", format: #Model.CmpNm.ElementAt(Index))
grid.Column("RtNum", header: "Route"),
grid.Column("AcntNum", header: "Account"),
grid.Column("CardNum", header: "Card")
… ) )
What the grid should look like is self-evident.

It's pretty unclear what is your goal. But no matter what it is I would recommend you to define a real view model reflecting the requirements of your view and containing only the information you are interested in seeing in this grid:
public class InventoryViewModel
{
public int Id { get; set; }
public string PersonName { get; set; }
public string BranchName { get; set; }
public string CompanyName { get; set; }
public string RouteNumber { get; set; }
public string AccountNumber { get; set; }
public string CardNumber { get; set; }
}
Now you could have the main view model:
public class MyViewModel
{
public int PageNumber { get; set; }
public int TotalRows { get; set; }
public IEnumerable<InventoryViewModel> Inventories { get; set; }
}
Alright, the view is now obvious:
#model MyViewModel
#{
var grid = new WebGrid(
Model.Inventories,
rowsPerPage: Model.PageSize
);
}
#grid.GetHtml(
columns: grid.Columns(
grid.Column("Id", header: "Inventory id"),
grid.Column("PersonName", header: "Person"),
grid.Column("BranchName", header: "Branch"),
grid.Column("CompanyName", header: "Company"),
grid.Column("RouteNumber", header: "Route"),
grid.Column("AccountNumber", header: "Account"),
grid.Column("CardNumber", header: "Card")
)
)
Now all that's left is build this view model in your controller. Since I don't know what you are trying to achieve here, whether you need an inner join or a left outer join on those columns, I will take as an example here a left outer join:
public ActionResult Index()
{
var inventories =
from inventory in _db.Inventories
join person in _db.Persons on inventory.PersonId equals person.Id into outerPerson
join company in _db.Companies on inventory.CompanyId equals company.Id into outerCompany
join branch in _db.Branch on inventory.BranchId equals branch.Id into outerBranch
from p in outerPerson.DefaultIfEmpty()
from c in outerCompany.DefaultIfEmpty()
from b in outerBranch.DefaultIfEmpty()
select new InventoryViewModel
{
PersonName = (p == null) ? string.Empty : p.Name,
CompanyName = (c == null) ? string.Empty : c.Name,
BranchName = (b == null) ? string.Empty : b.Name,
Id = inventory.Id,
AccountNumber = inventory.AcntNum,
CardNumber = inventory.CardNum,
RouteNumber = inventory.RtNum
};
var model = new MyViewModel
{
PageSize = 5,
// TODO: paging
Inventories = inventories.ToList()
};
return View(model);
}
And that's pretty much it. Of course in this example I am leaving the pagination of the Inventories collection for you. It should be pretty trivial now to .Skip() and .Take() the number of records you need.
As you can see ASP.NET MVC is extremely simple. You define a view model to reflect the exact requirements of what you need to show in the view and then populate this view model in the controller. Most people avoid view models because they fail to populate them, probably due to lack of knowledge of the underlying data access technology they are using. As you can see in this example the difficulty doesn't lie in ASP.NET MVC at all. It lies in the LINQ query. But LINQ has strictly nothing to do with MVC. It is something that should be learned apart from MVC. When you are doing MVC always think in terms of view models and what information you need to present to the user. Don't think in terms of what you have in your database or wherever this information should come from.

Related

Fluent LINQ EF Core - Select filtered child property

I have the classes below:
public class User
{
public Guid Id { get; set; }
public string Name { get; set; }
}
public class ParentEntity
{
public Guid Id { get; set; }
public string SomeProperty { get; set; }
public ICollection<ChildEntity> ChildEntities { get; set; }
}
public class ChildEntity
{
public Guid Id { get; set; }
public int Vote { get; set; }
public Guid UserId { get; set; }
}
public class ReturnedParentDto
{
public Guid Id { get; set; }
public string SomeProperty { get; set; }
public int Vote { get; set; }
}
I want to be able to return a full list of ParenEntities, but take an Id of the User class (UserClassId), then filter the ParentEntity's ICollection where UserUid = UserClassId, so only 1 ChildEntity is always returned. Then I would want to extract a specific field from that returned ChildEntity and merge it with the ParentEntity fields. The end result should be like the ReturnedParentDto.
I want to do it in the style like
ParentEntities.Include(v => v.ChildEntities).ToList()
That seems to be possible in EF Core 5, but my project is in 3.1.
You can do this as below
Approach 1:
var result = result = parentEntities.Include(x => x.ChildEntities.Where(y => y.UserId == userId))
.Select(x => new ReturnedParentDto {
Id = x.Id,
SomeProperty = x.SomeProperty,
Vote = x.ChildEntities.FirstOrDefault()?.Vote // userId is the variable here
});
Approach 2:
var result = parentEntities.Select(x =>
new ReturnedParentDto {
Id = x.Id,
SomeProperty = x.SomeProperty,
Vote = x.ChildEntities.FirstOrDefault(y => y.UserId == userId)?.Vote // userId is the variable here
});

Joining in LINQ to select a sublist within a list

I have two classes as follows:
public class HRM_SERVICE_PERD_BNFT_DTLModel
{
public string SRVC_PERD_BNFT_CODE { get; set; }
public string RTR_BNFT_CODE { get; set; }
public string RTR_BNFT_NAME { get; set; }
public string RTR_BNFT_CALC_MODE { get; set; }
public string SAL_HEAD_CODE { get; set; }
public string SAL_HEAD_NAME { get; set; }
public string RTR_BNFT_IN_PERCENT { get; set; }
public string RTR_BNFT_AMOUNT { get; set; }
public string RTR_BNFT_INACTV_DATE { get; set; }
public short? RTR_BNFT_SLNO { get; set; }
}
public class HRM_RETIREMENT_BENEFITModel : BaseModel
{
public string RTR_BNFT_CODE { get; set; }
public string RTR_BNFT_NAME { get; set; }
public string RTR_BNFT_SRTNM { get; set; }
public string RTR_BNFT_REM { get; set; }
public short? RTR_BNFT_SLNO { get; set; }
}
Now I want to select data from the first model for a specific 'SRVC_PERD_BNFT_CODE', then I need to select the 'RTR_BNFT_NAME' from the second table for all the filtered data along with some other value from the first model. I'm trying kind of joining, but not getting the name. It's showing system.collection.string instead of value.
Here is what I'm trying so far:
public List<HRM_SERVICE_PERD_BNFT_DTLModel> GetBenefitData(string mstcode)
{
var model2 = DataContext.HRM_SERVICE_PERD_BNFT_DTL.AsEnumerable().Where(m => m.SRVC_PERD_BNFT_CODE == mstcode).Select(s=>s).ToList();
var model = DataContext.HRM_SERVICE_PERD_BNFT_DTL.AsEnumerable().Where(m => m.SRVC_PERD_BNFT_CODE == mstcode)
.Select(s => new HRM_SERVICE_PERD_BNFT_DTLModel
{
RTR_BNFT_CODE = s.RTR_BNFT_CODE,
RTR_BNFT_SLNO = s.RTR_BNFT_SLNO,
RTR_BNFT_CALC_MODE = s.RTR_BNFT_CALC_MODE,
RTR_BNFT_AMOUNT = (s.RTR_BNFT_AMOUNT).ToString(),
RTR_BNFT_NAME = (from x in model2
join c in DataContext.HRM_RETIREMENT_BENEFIT on x.RTR_BNFT_CODE equals c.RTR_BNFT_CODE into p
from b in p.AsEnumerable()
select b.RTR_BNFT_NAME).ToList().ToString(),
RTR_BNFT_IN_PERCENT = (s.RTR_BNFT_IN_PERCENT).ToString()
}).ToList();
return model;
}
What I'm doing wrong? Please help.
Following is suspicious.
.ToList().ToString()
Remove the ToString part and replace it with something like FirstorDefault, then it should work.
Reason for getting something like System.Collection.String is that List<T>.ToString() is usually typename until it is overridden in some way.
Following minimal snipped produce System.Collections.Generic.List1[System.String]`
List<String> list = new List<String>();
var str = list.ToString();
Console.WriteLine(str); //System.Collections.Generic.List`1 [ System.String ]

most efficient Entity Framework Code First method of flattening / projecting parent entity with specific child

I have a parent entity Widget with core members and multiple WidgetTranslation children that have language translated members i.e. Description text available in English, French, German etc.
e.g.
public class Widget
{
public int Id { get; set; }
public string Code { get; set; }
public virtual ICollection<WidgetTranslation> WidgetTranslations { get; set; }
}
public class WidgetTranslation
{
public int WidgetId { get; set; }
public virtual Widget Widget { get; set; }
public int LanguageId { get; set; }
public virtual Language Language { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Summary { get; set; }
}
What is the most efficient method of querying the widget collection, flattening for a given LanguageId & projecting to a TranslatedWidget DTO
public class TranslatedWidget
{
public int Id { get; set; }
public string Code { get; set; }
public int LanguageId { get; set; }
public virtual Language Language { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Summary { get; set; }
}
Given languageId I've started with
DbSet.Select(w => new TranslatedWidget
{
Id = w.Id,
Code = w.Code,
LanguageId = w.LanguageId,
Name = w.WidgetTranslations.First(wt=>wt.LanguageId == languageId).Name,
Description = w.WidgetTranslations.First(wt=>wt.LanguageId == languageId).Description,
Summary = w.WidgetTranslations.First(wt=>wt.LanguageId == languageId).Summary
});
But I've a feeling this is inefficient and won't scale for more properties on WidgetTranslation.
Thanks
Use SelectMany to flatten structures via a single join:
var widgetQuery = from w in dbSet.Widgets
from wt in w.WidgetTranslations
where wt.Language == languageId
select new TranslatedWidget
{
Id = w.Id,
Code = w.Code,
LanguageId = w.LanguageId,
Name = wt.Name,
Description = wt.Description,
Summary = wt.Summary
});
I'm assuming here that you only have a single translation for each widget in a given language.
I would move Name, Description and Summary into a nested class of your DTO...
public class TranslatedWidgetTranslation
{
public string Name { get; set; }
public string Description { get; set; }
public string Summary { get; set; }
}
public class TranslatedWidget
{
public int Id { get; set; }
public string Code { get; set; }
public int LanguageId { get; set; }
public TranslatedWidgetTranslation Translation { get; set; }
}
Then you can project into that class and need First only once which would result in only one TOP(1) subquery in SQL instead of three:
DbSet.Select(w => new TranslatedWidget
{
Id = w.Id,
Code = w.Code,
LanguageId = languageId,
Translation = w.WidgetTranslations
.Where(wt => wt.LanguageId == languageId)
.Select(wt => new TranslatedWidgetTranslation
{
Name = wt.Name,
Description = wt.Description,
Summary = wt.Summary
})
.FirstOrDefault()
});
You must use FirstOrDefault here, First is not supported in a LINQ-to-Entities projection.
If you don't want that nested type you can project into anonymous types first and then convert into your final class, but the code will be a bit longer:
DbSet.Select(w => new
{
Id = w.Id,
Code = w.Code,
LanguageId = languageId,
Translation = w.WidgetTranslations
.Where(wt => wt.LanguageId == languageId)
.Select(wt => new
{
Name = wt.Name,
Description = wt.Description,
Summary = wt.Summary
})
.FirstOrDefault()
})
.AsEnumerable()
.Select(x => new TranslatedWidget
{
Id = x.Id,
Code = x.Code,
LanguageId = x.LanguageId,
Name = x.Translation != null ? x.Translation.Name : null,
Description = x.Translation != null ? x.Translation.Description : null,
Summary = x.Translation != null ? x.Translation.Summary : null
});

Speed up large Linq to Entities call MVC3

Below is the code I am using and the database table it is pulling from has about 92000 records in it. The way it is pulling right now it is pulling all 92000 records then doing the filtering.
What I am looking to do is the filtering on the initial pull from the DB so that it does not take aproximately 40 seconds to load the page.
This is something I am still new at so I am lost as to how to do this and make it work with my view
public ViewResult Makes()
{
var items = (from item in DBCacheHelper.recallslist
orderby item.MFGTXT ascending
select item.ToDomainRecall()).GroupBy(item => item.MFGTXT).Select(grp => grp.First());
return View(items);
}
public static IEnumerable<Recall> recallslist
{
get
{
if (c["GetAllRecalls"] == null)
{
c.Insert("GetAllRecalls", GetAllRecalls());
return (IEnumerable<Recall>)c["GetAllRecalls"];
}
else
{
return (IEnumerable<Recall>)c["GetAllRecalls"];
}
}
}
public static IEnumerable<Recall> GetAllRecalls()
{
using (DealerContext context = new DealerContext())
{
var items = from item in context.recalls.ToList<Recall>()
select item.ToDomainRecall();
return items.ToList<Recall>();
}
}
SELECT
[Extent1].[RecallsId] AS [RecallsId],
[Extent1].[RECORD_ID] AS [RECORD_ID],
[Extent1].[CAMPNO] AS [CAMPNO],
[Extent1].[MAKETXT] AS [MAKETXT],
[Extent1].[MODELTXT] AS [MODELTXT],
[Extent1].[YEARTXT] AS [YEARTXT],
[Extent1].[MFGCAMPNO] AS [MFGCAMPNO],
[Extent1].[COMPNAME] AS [COMPNAME],
[Extent1].[MFGNAME] AS [MFGNAME],
[Extent1].[BGMAN] AS [BGMAN],
[Extent1].[ENDMAN] AS [ENDMAN],
[Extent1].[RCLTYPECD] AS [RCLTYPECD],
[Extent1].[POTAFF] AS [POTAFF],
[Extent1].[ODATE] AS [ODATE],
[Extent1].[INFLUENCED_BY] AS [INFLUENCED_BY],
[Extent1].[MFGTXT] AS [MFGTXT],
[Extent1].[RCDATE] AS [RCDATE],
[Extent1].[DATEA] AS [DATEA],
[Extent1].[RPNO] AS [RPNO],
[Extent1].[FMVSS] AS [FMVSS],
[Extent1].[DESC_DEFECT] AS [DESC_DEFECT],
[Extent1].[CONEQUENCE_DEFECT] AS [CONEQUENCE_DEFECT],
[Extent1].[CORRECTIVE_ACTION] AS [CORRECTIVE_ACTION],
[Extent1].[NOTES] AS [NOTES],
[Extent1].[RCL_CMPT_ID] AS [RCL_CMPT_ID]
FROM [dbo].[Recalls] AS [Extent1]
Update:
Ultimately I would like to only pull records from the Recalls Table where the MFGTXT is equal to the
MakeName in the AutoMake Table
public class AutoMake
{
[Key]
public int MakeID { get; set; }
public string MakeName { get; set; }
public AutoMake ToDomainAutoMakes()
{
return new AutoMake
{
MakeID = this.MakeID,
MakeName = this.MakeName
};
}
}
public class Recall
{
[Key]
public int RecallsId { get; set; }
public string RECORD_ID { get; set; }
public string CAMPNO { get; set; }
public string MAKETXT { get; set; }
public string MODELTXT { get; set; }
public string YEARTXT { get; set; }
public string MFGCAMPNO { get; set; }
public string COMPNAME { get; set; }
public string MFGNAME { get; set; }
public string BGMAN { get; set; }
public string ENDMAN { get; set; }
public string RCLTYPECD { get; set; }
public string POTAFF { get; set; }
public string ODATE { get; set; }
public string INFLUENCED_BY { get; set; }
public string MFGTXT { get; set; }
public string RCDATE { get; set; }
public string DATEA { get; set; }
public string RPNO { get; set; }
public string FMVSS { get; set; }
public string DESC_DEFECT { get; set; }
public string CONEQUENCE_DEFECT { get; set; }
public string CORRECTIVE_ACTION { get; set; }
public string NOTES { get; set; }
public string RCL_CMPT_ID { get; set; }
public Recall ToDomainRecall()
{
return new Recall
{
RECORD_ID = this.RECORD_ID,
CAMPNO = this.CAMPNO,
MAKETXT = this.MAKETXT,
MODELTXT = this.MODELTXT,
YEARTXT = this.YEARTXT,
MFGCAMPNO = this.MFGCAMPNO,
COMPNAME = this.COMPNAME,
MFGNAME = this.MFGNAME,
BGMAN = this.BGMAN,
ENDMAN = this.ENDMAN,
RCLTYPECD = this.RCLTYPECD,
POTAFF = this.POTAFF,
ODATE = this.ODATE,
INFLUENCED_BY = this.INFLUENCED_BY,
MFGTXT = this.MFGTXT,
RCDATE = this.RCDATE,
DATEA = this.DATEA,
RPNO = this.RPNO,
FMVSS = this.FMVSS,
DESC_DEFECT = this.DESC_DEFECT,
CONEQUENCE_DEFECT = this.CONEQUENCE_DEFECT,
CORRECTIVE_ACTION = this.CORRECTIVE_ACTION,
NOTES = this.NOTES,
RCL_CMPT_ID = this.RCL_CMPT_ID
};
}
}
If you want to add server side filtering outside of your repository methods, you need to return your types as IQueryable rather than IEnumerable and not call .ToList, .AsEnumerable, or any other method that would cause .GetEnumerator to be called. Additionally, your cast `(IEnumerable)c["GetAllRecalls"];' forces LINQ to Objects to be used for subsequent requests rather than retaining the expression tree and using Entity Framework. That being said, you may need to move your call to ToDomainRecall method to after the additional filter is applied as well because that can't be translated to your database. Here are some of the changes you would need to make:
public ViewResult Makes()
{
var items = (from item in DBCacheHelper.recallslist
orderby item.MFGTXT ascending
select item.ToDomainRecall()).GroupBy(item => item.MFGTXT).Select(grp => grp.First());
return View(items);
}
public static IQueryable<Recall> recallslist
{
get
{
if (c["GetAllRecalls"] == null)
{
c.Insert("GetAllRecalls", GetAllRecalls(context));
}
return c["GetAllRecalls"];
}
}
public static IQueryable<Recall> GetAllRecalls(DealerContext context)
{
var items = context.recalls;
return items;
}
Looks like your DBaccess is done in the call to DBCacheHelper.recallslist.
You need to edit the sql that runs from/in this function.
As Eranga pointed out, you don't show how you are filtering the large number down to a smaller number of records. I assume you want 20 or 100 at a time? If so, please see the accepted answer here:
efficient way to implement paging
Specifically, this part which shows how to only retrieve rows x to y (where x = #p0 + 1 AND y = #p0 + #p1):
SELECT [t1].[CodCity],
[t1].[CodCountry],
[t1].[CodRegion],
[t1].[Name],
[t1].[Code]
FROM (
SELECT ROW_NUMBER() OVER (
ORDER BY [t0].[CodCity],
[t0].[CodCountry],
[t0].[CodRegion],
[t0].[Name],
[t0].[Code]) AS [ROW_NUMBER],
[t0].[CodCity],
[t0].[CodCountry],
[t0].[CodRegion],
[t0].[Name],
[t0].[Code]
FROM [dbo].[MtCity] AS [t0]
) AS [t1]
WHERE [t1].[ROW_NUMBER] BETWEEN #p0 + 1 AND #p0 + #p1
ORDER BY [t1].[ROW_NUMBER]

ASP.NET MVC - Join Tables using LINQ

My application is structured as:
namespace DomainModel.Abstract
{
public interface IContentItemsRepository
{
IQueryable<Content> ContentItems { get; }
IQueryable<Category> Categories { get; }
IQueryable<ContentCategory> ContentCategories { get; }
}
}
namespace DomainModel.Entities
{
[Table(Name = "Content")]
public class Content
{
[Column(IsPrimaryKey = true,
IsDbGenerated = true,
AutoSync = AutoSync.OnInsert)]
public int content_id { get; set; }
[Column]
public string type { get; set; } // article, video, recipe, tool
[Column]
public string title { get; set; }
...
namespace DomainModel.Entities
{
[Table(Name = "Content_Categories")]
public class ContentCategory
{
[Column(IsPrimaryKey = true,
IsDbGenerated = true,
AutoSync = AutoSync.OnInsert)]
public int content_category_id { get; set; }
[Column]
public int content_id { get; set; }
[Column]
public int category_id { get; set; }
...
namespace DomainModel.Entities
{
[Table(Name = "Categories")]
public class Category
{
[Column(IsPrimaryKey = true,
IsDbGenerated = true,
AutoSync = AutoSync.OnInsert)]
public int category_id { get; set; }
[Column]
public string category_name { get; set; }
[Column]
public string type { get; set; } //recipe,tool,other
[Column]
public int ordering { get; set; }
...
I can do this:
var articlesInCategory = _contentRepository.ContentItems
.Where(x => x.type == "article");
and get a list of articles. No problem.
However, I now need to select Content based on categories. So, I need to join Content to ContentCategory to Category.
I have no idea how to do this. Any help will be much appreciated.
Thanks.
EDIT:
I think part of my problem is that I don't even know how to call what I'm doing, so it's hard to search for this. Am I even doing LINQ to SQL, or LINQ to Entities, or is it LINQ to Objects?
The join query will be something like this.
var content=
from category in _contentRepository.Category
join contentCategory in _contentRepository.ContentCategory
on category.category_id equals contentCategory.category_id
join content in _contentRepository.Content
on content.content_id equals contentCategory.content_id
where category.category_id==#yourcategoryId
select new {type , title }
The concept you are looking for is called SelectMany in linq, and there are a number of ways to accomplish it.
One is:
var content =
from category in _categoryRepository.CategoryItems
join contCat in _contentCategoryRepository.Items
on category.category_id == conCat.category_id
where category.category_id == parameter
select contCat.content_id;
From here you should be able to extend it into pulling out all the data you need...look into the into keyword and check out this link if you haven't already.

Resources