C#, EF: Map [NotMapped] value from Sql Query - c#

I'm using Entity Framework. I currently have an attribute that is labeled [NotMapped] in my model class. I'm also using a query to bring back that value (from a view, which is why it's not mapped).
var list = context.Database
.SqlQuery<SomeModel>("SELECT NonMappedField, anotherfield FROM SomeView")
.ToList()
Is there a way I can hint to C# that for this instance, it should map the column from the raw query to my models?
I would have more things showing what I've tried, but I don't have the slightest clue of what to do next, other than build my own mapper and that seems like a very brittle solution.
Possible X/Y Problem:
My a data model has a Parent/Child relationship. The Child can be a child of multiple models,necessitating the use of a join table ParentChildJoin. When I do
context.Database.Parent.Where...Include( n => n.Children ).ToList();
I run into query timeouts for a pathetically small number of rows. So I had the bright idea of joining the ParentChildJoin table with the Child table in a View and retrieving the children that way. This works, but I need some way to map the retrieved Child objects to their Parent.
This is where the NotMapped field comes in. I can create a NonMapped field on my model, and then when I retrieve from my View, I can store the ParentId there. From there, I can associate the Child objects with the correct parent.
So that's how I go here.

Either the field is mapped or it isn't, you can't have it both ways!
Take a look at AutoMapper and the AutoMapperEF extension. The EF extension is clever enough to realise that a model that only has a selection of fields from a query will only have those fields in the generated SELECT. You could have several different models for the same query, each only returning the fields required for that model.

What you can do (without changing model) is to concatenate NonMappedField+"_"+anotherfield as anotherfield before load.
After FromSql load, you can split NonMappedField and anotherfield back.
var query = "SELECT [Id], CONVERT(VARCHAR(1024),[NonMappedField]+'|'+[anotherfield]) AS [anotherfield] FROM myView";
var list = _context.SomeModels.FromSql(query)
.Select(i => new SomeModel {
Id = i.Id,
NonMappedField = i.anotherfield.Substring(0, i.anotherfield.IndexOf('|')),
anotherfield = i.anotherfield.Substring(i.anotherfield.IndexOf('|')+1)
}).ToList();

Related

Entity Framework Navigation Property with Composite Foreign Key

EF 6.1.3.
I have a domain which contains many instances of a "Header/ Item" type pattern, where the a Header can have many Items (1 to many), and also has a "current" or "latest" item.
This is represented as follows:
Header
Guid Id
Guid CurrentItemId
Item CurrentItem
ICollection<Item> AllItems
Item
HeaderId
Id
The PK of the Items is always the HeaderID + ItemID. The reason being that, by far, the most common access pattern for items is to list all items related to a given header, and having HeaderID be the first part of the PK/clustered index means we get that data with clustered index seeks.
Our problem is that when we use the CurrentItem navigation property, it only ever uses the ItemID to do the lookup, which results in not so great query plans.
I assume this is because the conventions for EF us to use the CurrentItemId to look up the CurrentItem. My question is, is there a way for my to tell EF to always perform its joins for CurrentItem by mapping the Header.Id,Header.CurrentItemId -> Item.HeaderId,Item.Id?
I believe this is a slight different scenario than the one described here: composite key as foreign key
In my case, I have a one to one mapping not one top many, and there doesn't seem to be a WithforeignKey method available for that scenario.
We ended up not being able to get EF to generate the SQL the way we wanted - so we wrote a db command interceptor to dynamically find instances of this join and re-write the join to match our designed composite key.
We configure this as the DbContext level like so:
this.ModifyJoin<Item, Header>(
(i) => new Header() { CurrentItemId = i.Id }, //What to find
(i) => new Header() { CurerntItemId = i.Id, Id = i.HeaderId }); //What to replace with
This information is attached to the context instance itself, so when the command interceptor sees the overrides, it uses them to re-write the SQL.
This ends up working well for most scenarios, but there are some - such as when additional filtering is doing on the Item table as part of the LINQ statement, that the aliasing rules used by EF become too complex to follow without writing a full SQL parser.
For our use, this results in the ideal join about 90% of the time, which is good enough for us.
The code to do all this isn't difficult, but it's too big to put here. Add a comment if you want a copy and I'll put it up on GitHub.

Entity Framework Linq query unrelated child tables

I am using Entity Framework 6.1.3 to generated the entities and data model.
If I have two tables: Orders -> OrderDetails, with a relation between them (OrderId), then I can get all the orders and related OrderDetails with the following query
dbContext.Order().Include(a => a.OrderDetails);
But if I created a view (vOrder) for Orders, then there is no direct relation between vOrder and OrderDetails in the model, though I can link them together with joins on OrderId. How could I still get all the data from vOrder and related OrderDetails. The following query doesn't work unless I add all the navigation properties manually.
dbContext.vOrder().Include(a => a.OrderDetails);
Is there a simple LINQ query to accomplish the intended query?
Thanks for your help.
Do a manual join and return an anonymous object that contains both.
Something like:
dbContext.vOrder
.GroupJoin(
dbContext.OrderDetails,
v=>v.orderid,
od=>o.orderid,
(v,od)=>new {v=v,od=od});
Of course, you could just add the appropriate naviation properties on to vOrder and do exactly what you said.
Why not just include more columns in the view (or create another view that has all the required data, if you don't want to modify the first one)?

Foreign key to values using Linq

I have a webpage and site using Linq. The page contains a gridview which shows customers and bound by a List (Of Customer).
I use the below code to get a single customer
Public Function GetCustById(ByVal id As Integer) As Customer Implements ICustomerService.GetCustById
Return DataContext.Customers.SingleOrDefault(Function(c) c.Id = id)
End Function
I then bind it to my gridview by passing all the customer IDs into the method. This displays all the rows as i need but i have one issue.
Some columns which are Foreign keys show up as 1,2 etc, Usually the way i have overcome this in the past ON DIFFERENT PROJECTS is by adding a sub query to my SQL query in the data layer but as you've guessed this is Linq so im not sure what is /not possible in order to get the Foreign Keys to display as values using Linq?
Im a little new to Linq so appreciate any help or articles that would help me with this.
Thanks
It is recommended to separate the view from the entity classes (or domain classes if you like). So you will define a view model class, say CustomerViewModel, and project your Customer into it.
I'm not sure how you get a list of Customers (getting each single customer by Id is highly inefficient) but somewhere there will be an IEnumerable<Customer>. Let's call it customers. Now you can do
From c in customers_
Select New CustomerViewModel With { Name = c.Name, ... }
But now you can add properties to CustomerViewModel that are not in Customer! For instance
Select New CustomerViewModel
With { Name = c.Name, TypeName = c.CustomerType.Name }
This CustomerType could be one of those foreign keys you want to show.
The result of the projection is an IEnumerable<CustomerViewModel> that you can show in the grid.

Nhibernate and indexed view

I have an Album entity which has IList<Photo> in it. I want to select all my albums with count of photos with one fast query using NHibernate. Before migrating to NH I had a query using an indexed view:
SELECT a.*, t.PhotoCount
FROM dbo.Album a
LEFT JOIN dbo.vw_AlbumPhotoCount t ON t.AlbumID = a.AlbumID
How can I use this view (vw_AlbumPhotoCount) in NHibernate to speed up the query?
UPDATE:
I mapped a simple <one-to-one/> entity as Alex advised and it worked for me. One downside of this solution - as one-to-one properties cannot be loaded lazily I always get LEFT JOIN when fetching album by id. I couldn't map this entity as <many-to-one/> property, I'm not sure why maybe because the view doesn't have its own ID property. Anyway ofter mapping it as <many-to-one/> I got null in it after fetching an album
UPDATE 2:
I've reviewed my architecture and decided to remove the view. The best way in this particular situation is using NH caching system instead of indexed view
Your best bet may be to map a simple entity to your PhotoCount view, and then set up a relationship to that entity through ordinary means. Just make sure you don't cache these objects, because they will never actually get updated.
a Property on the Album with a formula
// Fluent Mapping
Map(x => x.PhotoCount).Formula("SELECT t.PhotoCount FROM dbo.vw_AlbumPhotoCount t ON t.AlbumID = AlbumID");
// xml mapping
<property name="PhotoCount" formula="SELECT t.PhotoCount FROM dbo.vw_AlbumPhotoCount t ON t.AlbumID = AlbumID">
var albums = session.QueryOver<Album>().List();

Accessing a Common Code Value Table in Generated EF Models

I have a database that uses a custom model for recording lookup valus (i.e. states, types, etc.). So across all of the database tables, there are various columns called something like state_cdv_id which would store an integer and reference the code_value table to get that state's value (i.e. "CA", "AK", etc.).
I would like to map my EF model so that I can access the code values for all of these fields, and I don't want to have to do it manually in partial classes for EVERY entity... that's a lot of repetition. So I want to be able to access my code values like: MyPerson.State and get back the string "CA" for example.
Here's what a single getter would be that I would have to repeat many times if I were to do it manually:
public string State
{
get
{
MyEntityContext c = new MyEntityContext();
return c.CodeValues.Single(cv => cv.Id == RecordStatusCdvId).Value;
}
}
I don't know what the best approach would be: change the T4 templates, add property attributes to certain fields and then programmatically add a getting to those, or something else.
Any help?
If there is a 1:1 relationship between the entity and the code_value table the entity should already have a State property, which by default which will be null by default, you could then fill it in by using an Include on your DB queries:
var foo = context.MyEntities.Include( x => x.State);
Your sample code is terribly wrong because it makes your entity dependent on the context (moreover you don't dispose it). Whole POCO approach just to avoid this (POCO T4 generator and DbContext T4 generator).
If you have relation to lookup table in your database EF will crate for you navigation property. If you don't have such relation in the database and you are using EDMX file you can still create such relation in your model and you will again get navigation property to lookup table. Once you have navigation property you can simply do:
string code = myEntity.State.Code;
But the navigation property must be loaded either by eager loading (as #BrokenGlass described) or by lazy loading.
If you don't like the idea of navigation property and you still want State property to show just code of the state you must understand what does it mean: If you map the entity that way it will be read-only because EF will not be able to transform compound entity back to real tables which must be updated. It is possible to map the entity the way you want but it is considered as advanced (and mostly not needed) scenario which works only if you have EDMX file (not with code first approach). The choices are:
Create database view and map the view to a new entity
Create DefiningQuery manually in EDMX (opened as XML) file and map it to a new entity (once you do that you cannot update your model from database or generate database from model any more)
Create QueryView manually in EDMX (opened as XML) file and map it to a new entity (this requires original entities to be already mapped)
You will have to do that for every table where you want such mapping. Anyway whole that complexity with manually changing EDMX is not needed because you can simply create custom classes like:
public class SomeViewModel // I suppose your main target is to have codes in presentation layer
{
public string SomeData { get; set; }
public string State { get; set; }
}
and use projection query
If you have navigation property:
var data = from x in context.SomeEntities
select new SomeViewModel
{
SomeData = x.SomeData,
State = x.State.Code
};
If you don't have navigation property
var data = from x in context.SomeEntities
join y in context.LookupValues on x.LookupId equals y.Id
select new SomeViewModel
{
SomeData = x.SomeData,
State = y.Code
};

Categories

Resources