Foreign key to values using Linq - c#

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.

Related

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

Retrieving a List<T> from another List

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

Prevent INSERTing duplicated references to category table with Entity Framework

I'm working with a Database First model with Entity Framework 5.
In particular, we have one table with a list of predefined categories that are used as reference to a second table of items.
TABLE ITEM
(lots of fields)
integer category_id
TABLE CATEGORY
integer category_id
string name
string description
The table with the categories is populated initially and will not be modified (unless there is a change in the requirements)
The category for each object is calculated inside the code and returns a Category object for each Item object passed to a class written for that purpouse. This class does not need to use the objects in the CATEGORY TABLE, because they are known in advance. It is basically something like this:
var calculatedCategory = Categorizer.calculateCategoryFor(itemToCategorize);
itemToCategorize.category = calculatedCategory;
QUESTION
I want to find the most transparent way to work with the entities when I am calculating the Category for an Item whatever I want to use the category inside or outside a DbContext. The method to returns a Category object instead of just a String to make a proper use of the entities even in a dettached scenario, but this category can just be created on the fly inside the method because it knows the name, wich is the important field.
The key point here is how to make it transparent to whoever uses this Categorizer to avoid possible duplicated insertions (besides during runtime enforcing some constrain in the DB).
The solution I am using right now is passing a collection of categories to the constructor of the Categorizer, so it can use them when they are actual references in the database. When I just want to calculate it whithout any database operation involved, I just create a collection of this Categories dettached. However this is an additional duplicity and cumbersome, and feels very wrong.
I would like something that could be used like this
using (var ctx = new context())
{
var itemToCategorize = getItemSomeHow();
var calculatedCategory = Categorizer.calculateCategoryFor(itemToCategorize);
itemToCategorize.category = calculatedCategory;
ctx.SaveChanges()
}
Instead of something like that
using (var ctx = new context())
{
var itemToCategorize = getItemSomeHow();
var calculatedCategory = Categorizer.calculateCategoryFor(itemToCategorize);
//Code that searches for the matching category in the DB
...
itemToCategorize.category = matchingCategory;
ctx.SaveChanges()
}
and avoid the returned Category to be inserted as a new one in the database, and just update the reference of the Item to the right preexisting Category
I'm thinking in two approaches:
Override some methods in the model to do this verification and avoid inserts in the database in the Category table.
Modify the database to remove the category_id and use the name field as primary key (the problem here is that I'm not an expert in databases and I don't know the effects in the preformance of this, as the ITEMS TABLE can be huge)
You can't avoid having to do some sort of name checking before storing a Category. I think the cheapest way to do this is:
var categoryId = ctc.Categories.Where(c => c.Name == category.Name)
.Select(c => c.Id).FirstOrDefault();
if (categoryId.HasValue)
{
itemToCategorize.CategoryId = categoryId.Value;
}
else
{
ctc.Categories.Add(category);
itemToCategorize.Category = category;
}
So you only fetch an Id value from the database to do the check. And by setting CategoryId only (not the Category object reference) you avoid ever duplicating an existing Category.
To prevent concurrency issues due to latency between the check and the actual insert, of course as a final guard there should be a unique index on the name column (needless to say, I guess).

oop asp.net classes for sql server queries

In sql server, I have Students and Departments tables.
I want to get Student records from Student table; with their Department data from Departments table(so i joined them) and show them in an asp.net page.
In asp.net I created a Student class type to use in list in asp.net codebehind.
The list, takes Student class type data like this:
List<Student> lst = new List<Student>();
But I need to get Students with their Department datas. So I created a custom class in asp.net side called StudentsAndDepartments.
Now the list takes this class:
List<StudentsAndDepartments> lst = new List<StudentsAndDepartments>();
My question; do I have to create, for every query I make, an another new custom class?
If I create a custom class for every query I use, there are being too many classes inside the solution.
I think I'm doing wrong..
What should I do?
Thanks for helping..
You do not create a "StudentsAndDepartments" class.
What you are doing here is trying to "flatten out" the domain model, and use one class to represent something that is better defined as a relationship.
You create your domain model. And relate them.
public class Student
{
public string LastName
/* the above is an example 'scalar' property on the Student. you'll have others like FirstName, StudentIdenficationNumber, etc, etc. */
/* below is the 'relationshiop' property, use one of the two below but not both */
public ICollection<Department> Departments;
/* or */
public Department ParentDepartment;
}
public class Department
{
public string DepartmentName
public ICollection<Student> Students;
}
Then you "hydrate" the domain model.
Now this is where things can really vary.
Entity Framework with POCO can do this.
ADO.NET can do this. You write "Serializers" which convert IDataReaders into your object model. This is manual-mapping.
With ADO.NET, you may write different stored procedures...
dbo.uspStudentsGetAllWithParentDepartments
dbo.uspDepartmentsGetAllSimple
dbo.uspDepartmentsGetAllWithChildrenStudents
something like that.
Now, where I digress from others usually.
I do NOT write JOIN SQL statements.
I write 2 separate SQL statements (in ONE stored procedures) to get my data.
dbo.uspStudentsGetAllWithParentDepartments
would like like this
Select st.EmpKey, st.LastName, st.FirstName from dbo.Student st
Select dept.DepartmentKey, dept.DepartmentName from dbo.Department dept where exists (Select null from dbo.Student innerStud where innerStud.ParentDepartmentKey = dept.DepartmentKey )
Now, Entity Framework can do this sql-writing for you, but it is a start-up cost if you've never seen it before.
What my Microsoft-only friends won't mention is that EF does not support the .Merge() function like NHibernate does (another ORM tool). Which is a deal breaker to me. But that's a deeper discussion.
Define your domain-objects, their relationships, and then ask questions about "what's the best way to hydrate my domain-model based on my current skill-sets" (or without the skillset part if you're open to new ways)
Here is a link to another answer I posted...which is the serializer code for the ado.net way of hydrating your objects.
Why is DataTable faster than DataReader

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