Linq to Sql Many-One relationship - c#

I have 2 views in SQL set up:
PurchaseOrder
PurchaseOrderLineItems
These have many columns aliased (the tables they view/join are not sensibly named... it's a 3rd party product)
I have 2 classes (simplified below)
class PurchaseOrder
{
public string PoNumber { get; set; }
public string Vendor { get; set; }
public DateTime DateCreated { get; set; }
public IEnumerable<PurchaseOrderLineItems> LineItems { get; set; }
}
and
class PurchaseOrderLineItems
{
public string PoNumber { get; set; }
public string Name { get; set; }
public double Price { get; set; }
}
I'm using Linq to Sql - with XML mapping file (created with help from sqlmetal.exe)
What I want to do is effectivly populate the IEnumerable in PurchaseOrder with records from the PurchaseOrderLineItem view - effectively joining the tables
I wanted to do this using POCO - without having to add EntitySet<> to my class, as eventually, I will change my ORM to something like nHibernate (which has bag attribute i believe...?)
Currently, I've got a stored procedure - sp_getPurchaseOrderLineItems which takes the PONumber, and then returns a list of PurchaseOrderLineItem objects, that i then add to my result set (this is far, far from ideal)
is there any way I can do what i need? So that basically, a query on PurchaseOrder returns an already populated IEnumerable of LineItems within the instance?
It's worth mentioning that this will only ever be read-only, we'll never be inserting / updating data using this.

You can extend your PurchaseOrder class to implement the OnLoadedMethod:
public partial class PurchaseOrder
{
partial void OnLoaded()
{
LineItems = FunctionToCall_sp_getPurchaseOrderLineItems_AndBuildSet();
}
}
This will at least get the line items automatically when you get your PO.

This is the n+1 problem. nHibernate has a solution for this, which is called join-fetch querying. What it basically does is a outer-join query between order and order-line, which will result in the product of the row counts of the two tables.
I don't think Linq2SQL does have a solution for it. But you can still use your stored procedure to generate the join-fetch output, and have some Linq2Objects code to distinct the unique orders and the order-lines out of the result.

Related

Is it possible to select mapped entity into another mapped entity in a single query?

Is there any elegant way to perform a nested LINQ selection into objects that are being selected themselves? In other words, let's assume there are three DB tables all with one-to-many relation: Schedule, Day (One schedule may have many days) and Activity (One day may have many activities. I would like to try to build a query which selects data into related mapped objects without the necessity of creating additional helper objects. Is that even possible? Please see below objects which map DB tables:
public Class Schedule{
public int ScheduleId { get; set; }
...
public byte[] RowVersion { get; set; }
[NotMapped]
public List<Day> days; //Supposed to store Day objects
}
public Class Day{
public int DayId { get; set; }
...
public byte[] RowVersion { get; set; }
[NotMapped]
public List<Activity> activities; //Supposed to store Activity objects
}
public Class Activity{
public int ActivityId { get; set; }
...
public byte[] RowVersion { get; set; }
}
At this point I use additional classes to fetch data: SchedulePrim, DaysPrim and ActivitiesPrim. After the query is executed I put Prims into [NotMapped] attributes of proper objects (see above) and then get rid of Prims. To me this seem like using unnecessary resources. The query looks somewhat like this:
from schedules in context.Schedules.Where(...)
select new SchedulePrim
{
Schedule = schedules
DaysPrim = from days in context.Days.Where(...)
select new DaysPrim
{
Day = days
ActivitiesPrim = from activities in context.Activities.Where(...)
select new DaysPrim
{
Activity = activities
}
}
}
Here comes the logic of reprocessing fetched data into proper entities.
Is there a faster way to do this? The way that lets selecting data into [NotMapped] attributes on the fly, without the need of introducing additional processing?
Just eliminate the NotMapped attributes and the "additional classes", make sure you have proper foreign keys, and load the data using Include.
db.Schedules.Include(s => s.Days).ThenInclude(d => d.Activities).Where(...)

How can I get data from different DbSet entities and combine into one object?

When using Entity Framework 6, how is the most efficient way to create an object or objects with additional data from other DbSet entities, when I have a DbContext or IQueryable<T>?
Here is some code:
If I have an Data class as follows:
public class Data
{
[Key]
public int id { get; set; }
public string name { get; set; }
public string data { get; set; }
public int parentId { get; set; }
public int otherDataId { get; set; }
}
And an OtherData class as follows:
public class OtherData
{
[Key]
public int id { get; set; }
public string name { get; set; }
public string data { get; set; }
}
In the Data class, the parentId is a foreign key reference to another Data object in the same DbSet, and the otherDataId is a foreign key reference to an OtherData object in a DbSet<OtherData>.
I would like to get all Data objects in the DbSet<Data>, with the additional DbSet data of the parent Data objects id and name and the OtherData object's id and name. I need this to be in one object to be sent from a webservice GET.
I am not sure on how to do this.
Do I need some code along the lines of:
var result = DbContext.Data.Select(x=> x...).Join(y=> y...) .. new { id = x.id... y.name.. }
Can I please have some help with this code?
You can use a join and project the result. In the below snippet CombinedData is a another class with 2 string fields Name and OtherName. You can also use a view but I think the Join is less work.
IQueryable<CombinedData> result = DbContext.Data.Join(
DbContext.Data.DbContext.OtherData,
outer => outer.OtherDataId,
inner => inner.Id),
(outer, inner) => new { Name = outer.Name, OtherName = inner.Name}
);
Depending on your overall architecture, this may be a good or a bad answer but often when faced with this in the past our teams would create a view in the database to combine the fields. Write your optimized query in the view and then treat it like any other table in your data layer.
You could accomplish the same end result using includes or joins or even writing out the expression in a cross-table query but in my opinion the view is the cleanest and most efficient method.

Entity Framework fluent api how to conditionally map multiple properties to a single table

Using fluent api, how do I map multiple properties of the same data type to a single table conditionally.
Database Model:
ListType would include the Grouping names i.e. Allergens
ListItem would include all possible values for the given type
ProductListItem contains all "selected" values for a given product.
The goal is to use the ProductListItem table and apply it across multiple properties of a model (of the same ProductListItem type) based on the ListType (WHERE ProductListItem.ListTypeID = 1).
public class Product
{
public int ProductID { get; set; }
public List<ProductListItem> Allergens { get; set; }
public List<ProductListItem> DoesNotContain { get; set; }
}
I really don't think that you can achieve this with conditional mapping, but you can cheat.
public List<ProductListItem> Allergens
{
get { return this.ProductListItems.Where(i => i.ListType.Name=="Allergens").ToList=();}
}
Or can you can optionally create a single class for different ListItems with the same baseclass and use the TPH mapping: http://weblogs.asp.net/manavi/inheritance-mapping-strategies-with-entity-framework-code-first-ctp5-part-1-table-per-hierarchy-tph
The code would be something like this:
class Product
{
public List<AllergenProductListItem> Allergens { get; set; }
public List<DoesNotContainListItem> DoesNotContain { get; set; }
}
Its obviously not dynamic regarding the number of the item types (hard to add a new one), but neither is you desired solution, since if you want to have a new type you should modify the code.

EF relationship to slow changing dimension

I'm using C# with .net 4.5.1 and EF 6 code first.
Is there a way to mark relationships with a time range of validity? In terms of SQL DDL I'd define something like this:
create table a_b_relationship (
table_a_id int,
table_b_id int,
valid_from datetime,
valid_to datetime
)
I'm wondering if I should define the relationship itself as a class so that EF translate it in a relationship-table or there is a data annotation or fluent api.
Thanks in advance
If I understand your question correctly, you want to add properties in the table created to support a many-to-many relationship between to tables.
I would create a separate class to support this.
public class a_b_relationship
{
public a_b_relationship_id(table_a a, table_b b)
{
table_a = a;
table_b = b;
ValidTime(20);
}
public int a_b_relationship_id { get; set; }
public int table_a_id { get; set; }
public int table_b_id { get; set; }
public DateTime valid_from { get; set; }
public DateTime valid_to { get; set; }
public virtual IEnumerable<table_a> table_as { get; set; }
public virtual IEnumerable<table_b> table_bs { get; set; }
private void ValidTime(int validMinutes)
{
valid_from = DateTime.Now();
valid_to = DateTime.Now().AddMinutes(validMinutes);
}
}
Then map this in your context.
This way you can add the extra properties for the validity DateTimes instead of letting EF generate the joining table without them.
If you are trying to set the relationship time range in EF (e.g. When I link a record in table_a to a record in table_b, then this relationship is valid for 2 hours), then no. That is a business rule that you set in code.
Edit
I added a method for adding the time range of validity that's accessed when creating the relationship. You still need to save the changes & all that, but this is just an example of how I would do it. You could set this up to work from either or both a & b classes.

NHibernate and DTOs to return specific data from hierarchical data

I have several tables that I need to pull data from but I do not need all of the data in all of the tables. So for example I have the following Order object that contains several child objects and object collections.
public class Order
{
public virtual int ID { get; set; }
public virtual Coupon CouponID { get; set; }
public virtual Status StatusID { get; set; }
public virtual Address ShippingAddressID { get; set; }
public virtual Address BillingAddressID { get; set; }
public virtual ICollection<OrderShipmentHistory> OrdertHistories { get; set; }
public virtual ICollection<OrderShipmentNote> OrderNotes { get; set; }
public virtual ShippingDetails ShippingDetail { get; set; }
public virtual ICollection<OrderProduct> OrderProducts { get; set; }
}
Also some of these child objects in turn have child objects and at the most extreme we have a 4 tier object hierarchy.
So my problem is that I need to retrieve a list of objects that contain only specific information from most of these child objects.
Currently when I retrieve the list of orders I am pulling everything back. I have used lazy loading so that I don't do this but I will eventually need to pull this information back as I am accessing at least one piece of data in each of these child objects.
So I was thinking that instead of populating a list of orders I could create DTO's for each of the data collections that I need. My problem is I am not sure where to start. I have seen examples of people using DTO's but are only populating them once they have retrieved all of the data. I don't want to do this. I want to only retrieve the data I need and then populate the DTO's with the result sets.
I would really appreciate any guidance on where I should start and what I should be using.
regards
Noel.
What you are talking about is called projection.
To project your object graph to a flattened structure use for example linq select.
Now within the select you can either directly create the data strongly typed with your Dto or just return an IEnumerable<T> where T is dynamic or some other Poco and pass this around...
Simple example of projection: Lets say foo is a Queryable coming from nhibernate...
// Creates anonymous type with one property 'bar'
var list = foo.Select(p => new { p.bar }).ToList();
// Creates a Dto for each element and set property Bar of the Dto.
var list = foo.Select(p => new Dto{ Bar = p.bar }).ToList();

Categories