Retrieving a List<T> from another List - c#

Not too sure I have the title correct but I'll explain. I have a couple of tables that have relationships between them:
As you can see, Issues can have many IssueActivities and Issues can also have LinkedIssues. A LinkedIssue is one or more Issues that are tied to another Issue. What I'm trying to do is given an IssueID, I want to pull back all LinkedIssues AND the IssueActivities for each. I'll be using this to display in a DevExpress grid using master/detail.
Here is what the .edmx looks like:
Note -- The Navigation Properties for Issue are IssueActivities, IssueCategory, and LinkedIssues
I am fairly new to using linq with EF so I'm not sure how to approach this. I know I can get a list of LinkedIssues by using
UnitOfWork unitOfWork = new UnitOfWork();
var childIssues = (from li in unitOfWork.Context.LinkedIssues
where li.IssueId == issueId
select li).ToList();
return childIssues;
but I'm not sure if it's correct or how to get the IssueActivities for each and display them in a grid.
FK relationship between Issue and IssueActivities

Related

Dapper and n-tier application (navigation properties)

I'm struggling to get my head around how I should implement dapper in my application. I have a n-tier mvc application and have some experience with EF. Even that I think EF is good, I have not passed the learning curve to make it flow easy and not struggle with performance. In the new project we decided to give dapper a go, mostly to get control over the sql and hopefully get good performance.
Background
I created a layered aplication (core) with these layer
Web - mvc
Service - Business layer to handle the business logic
Data - datalayer to access the ms sql server
I went ahead and started implementing a UnitOfWork and generic Repositories in the datalayer.
A normal structure in the Database would be
Order
ref to User
ref to Address
OrderLine
ref to Product
And in many cases I want to retrieve multiple orders with all lines and products.
So what I did was to have navigation properties on the entity models as you would in EF and populate them with dapper using either multiquery or split the result into different entities and mapping them to the graph.
The problem
The problem I run into is when I do an insert. I have an sqlextension that maps the properties to table columns. But the navigation would also be mapped by default. I realize that I can decorate with attributes and read them on the mapping, but as I google I'm getting aware that maybe I should drop the UnitOfWork pattern and also repository, making the data-layer "super thin" and just expose the connection.
Then the service-layer would call the Dapper with correct sql, kind of what I would do today but with repositories.
I would also drop the navigation properties and fetch each entity on it's own, and combine them in the ViewModel.
My problem with this is if we take the order table above I would have to do something like this to get a full list (normally paged, also I removed the User/address)
var listModel = new OrderListViewModel();
var orders = orderService.GetAll();
foreach(var order in orders) {
var orderModel = new OrderViewModel(); // also map fields
var orderLines = orderService.GetOrderLinesForOrder(order.OrderId);
foreach(var orderline in orderLines) {
var orderLineModel = new OrderLineViewModel(); // also map fields
var product = productService.GetProduct(orderline.ProductId);
orderLineModel.Product = new ProductViewModel(); // also map fields
orderModel.OrderLines(orderLineModel);
}
listModel.Orders.Add(orderModel);
}
This will generate ALOT of queries (almost like EF lazy loading). So I could do a mapping thing
var orders = orderService.GetAll();
var orderLines = orderService.GetOrderLinesForOrders(orders.Select(o => o.OrderId).ToArray() ); // get all orderlines for all orders
var products = productService.GetProductsForOrderLines(orderLines.Select(p => p.OrderLineId).ToArray() ); // get all products for all orderlines
foreach(var order in orders) {
var orderModel = new OrderViewModel(); // also map fields
var orderLines = orderLines.Where( o => o.OrderId == order.OrderId );
foreach(var orderline in orderLines) {
var orderLineModel = new OrderLineViewModel(); // also map fields
var product = products.First(p => p.ProductId == orderline.ProductId);
orderLineModel.Product = new ProductViewModel(); // also map fields
orderModel.OrderLines(orderLineModel);
}
listModel.Orders.Add(orderModel);
}
This will generate alot less sql queries and is optimal in performance I think. I know there can be a problem with more than 2100 (?) parameters, but I think that will not be a problem in my case.
The problem is that many of out tables have different status, and many relations to other tables. I would have to do alot of these queries all the time.
When I first did repository and navigation I would do it like
repo.Get<Order, OrderLines, Product, Order>(sqlThatWouldJoinAllTables);
// split and map the structure into order Entity and just return that
That way I could just call orderService.GetAll() and retrieve a graph of order, orderlines and products.
I don't know which of the solutions is "best practice". I've tried to find a good open source project using layers and dapper to get some real world usage, but without success.
The approach of removing navigation properties also remove some of the purpose of the service layer, since i'm in kind of a way moving some of the business logic to the mvc controller.
I can't find a good practice how I would go forward, please advice.
If the RDBMS you're using supports JSON, I would suggest to wrap everything you need to insert into a JSON and send it to stored procedure with just one call. Same technique can be used to return a graph of related object with just one call. The Unit-Of-Work, a transaction really, will be taken care in the stored procedure itself, which is also the right place where to deal with transactions that operation on data IMHO.
This helps enormously to reduce round-trips at the expense of more CPU used on the database. This is usually not a problem unless you expect a really huge number (= more then several thousands of concurrent queries per second.
I have wrote extensively about this here:
https://medium.com/dapper-net/one-to-many-mapping-with-dapper-55ae6a65cfd4
and more specifically the "Complex Custom Handling" sample shows exactly what I mentioned.

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

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

How do you query several tables at once given a list of table names as strings?

So I work at a company where there are several tables with different names, but the exact same structure (date, time, value). What I would like to be able to do is have my program (C# or LINQ/LINQPad) run through these tables and query out a specific row so I can make sure they have all been updated properly. Is this doable?
So far my train of thought has been this. Knowing that each table name will give it a different class, I'm trying to generically get the values out of the table. I am able to get the table by using the code here
DataContext dc = new DataContext();
var myTable = (ITable)dc.GetType().GetProperty("Table").GetValue(dc, null);
My problem is I'm trying to get the columns out of this table using a similar method. However, the below is throwing an error:
foreach(var e in myTable)
{
double myValue = (double)e.GetType().GetProperty("Value").GetValue(e, null);
}
So when I took a look all the properties of e.GetType(), I noticed it had 2, one of which was Context, so I went exploring there. However, the next layer down seems to be a dead end of metadata with no actual data coming out of my loop. Am I even heading in the right direction? Once I get this hurdle, it would be easy enough for me to make a list and get my results out that way. Any help would be appreciated. Thanks.
If you need to query many tables by primary key (EntityKey in db context), then
ObjectContext has a method called GetObjectByKey that allows you to get object by EntityKey
So, you need to construct just a EntityKey. According to the MSDN article, it is pretty simple,
DataContext dc = new DataContext();
EntityKey key = new EntityKey("DataContext.Table","Id", 123);
dynamic entity = dc.GetObjectByKey(key);
You can create an interface that is common for all tables (entites) that have the same structure and cast your object to the interface, or just use dynamic.

EF6 mapping similar tables into one entity

I'm making an application that uses legacy database, using EF6 database first, .Net C#.
The database has two versions: the old and the new one. In the new one some tables were modified and renamed. E.g. old one has tables like: work, order, item etc. and new one work_t, order_t and item_t.
The content of corresponding tables is very similar, in the new ones some new columns were added and some were removed. So my application is supposed to work with both kind of databases as I use only the columns that are presented in both versions.
I was wondering if there is any decent way to hide those table pairs behind some interface or something to avoid doing 2 implementations of LINQ coding.
This is not exactly creating one entity out of 2 tables, because only one table is presented in the database at a time. I want to have single piece of code to address either one of the similar tables.
Here's some pseudo code for what I'm after:
public workDTO GetWork(int workId)
{
MyEntities db = new MyEntities();
// for old version it will go like
var work = db.work.Where(a => a.id == workId);
// for new version it will go like
var work = db.work_t.Where(a => a.id == workId);
return Mapper.Map(work, workDTO);
}
So the idea is to have just one method and one LINQ implementation for both tables.
Yes , You can do it by giving a column attribute in entity framework:
Read here
Update :
You can use the .ToTable() method:
modelBuilder.Entity().ToTable("t_Department");
Source: MSDN: http://msdn.microsoft.com/en-us/data/jj591617.aspx

Entity framework Binding to DataGrid wpf trouble

I connected to DataBase with Entity:
Entity db = new Entity();
...
Then i add DataGrid to Form and try to recive table from my DataBase
var pp = from asd in db.ABONENT select asd;
MyDataGrid.ItemsSource = pp.ToList();
The result is here:
Screenshot
it's display other fields from other linked tables, why ?
How to display data only from ABONENT table?
My guess is that you are using a DataGrid to display the content of all your tables.
Are you columns definition static or dynamically loaded?
If it's dynamically, I suggest to Remove all columns between every Data Binding.
If it's static, hide the columns you don't wanna display (Visible = false).
In Entity Framework, you have Entities, not Tables. Entity Framework abstracts the relational concept of tables into objects you use within your application. That's what an ORM does.
Because of this, relations between tables are expressed as what is called a Navigation Property in your entities, which is basically a property inside the entity class that represents the associated entity.
My point is.. why do you use an ORM if you intend to expose the tables directly into the UI?. Use plain old ADO.Net for that, or otherwise define your UI in such a way that you don't expose the entire table directly to the user. The user knows nothing about tables. The user understands their business. Therefore your application should know nothing about tables.
I see this as a bad practice from a UX perspective, for example, why should the user see the Id columns such as abonentID and RegionID into their UI?? they don't care about that, nor do they understand that. row IDs are a RDBMS concept, not a business concept.
My suggestion: Rethink your approach: either fallback to using plain old ADO.Net, or set the AutoGenerateColumns to false in the DataGrid and expose only the columns the user cares about.
You can select the exposed properties of the entities by using the following syntax:
var pp = from asd in db.Products
select new
{
asd.Id,
asd.Name,
ProductCategory = asd.ProductCategory.Name,
};
MyDataGrid.ItemsSource = pp.ToList();

Categories

Resources