c# sqlite inner join - c#

I am creating a Xamarin Cross Platform app in VS2017 that uses an SQLite database and I am trying to work out how to perform a query using an inner join. I am using the sqlite-net-pcl NuGet package.
I created the databse using DB Browser for SQLite. I have 2 simple tables.
class Crime
{
[PrimaryKey, AutoIncrement]
public int Id {get; set; }
[NotNull]
public string Name { get; set; }
[NotNull]
public int Legislation { get; set; }
}
class Legislation
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
[NotNull]
public string Title { get; set; }
}
Using DB Browser I have a foreign key set up on Crime.Legislation linked to Legislation.Id.
I am easily able to query a single table and populate a list using the code below.
public partial class LegislationListPage : ContentPage
{
private SQLiteAsyncConnection _connetion;
private ObservableCollection<Legislation> _legislations;
public LegislationListPage ()
{
InitializeComponent ();
_connetion = DependencyService.Get<ISQLiteDb>().GetConnection();
}
protected override async void OnAppearing()
{
await _connetion.CreateTableAsync<Legislation>();
var legislations = await _connetion.Table<Legislation>().OrderBy(x => x.Title).ToListAsync();
_legislations = new ObservableCollection<Legislation>(legislations);
legislationList.ItemsSource = _legislations;
}
}
What I want to do on another page is run a query that returns the Name column from Crime and the Title column from Legislation.
I can achieve this in SQL in DB Browser using
select c.Name, l.Title from Legislation as l inner join Crimes as c on l.Id = c.Legislation
However I don't know how to do this using c# as I have been doing with previous queries. I can't find any .join methods or anything similar. I'm hoping to store the results in a list based on a class that is created on the fly, but I'm not sure if this is possible so I'm happy to create a new class to hold the result of the two strings that will be returned. The end goal is just to populate a list with the results.
If someone could point me in the right direction that would be great.
Thanks in advance.

Ok I managed to find a solution. Quite simple really. I couldn't find a .join method cos i hadn't been 'using' System.Linq.
There are two ways of doing it. Using query syntax and lambda. Lambda is what I was hoping to find so I'm pleased I got it working.
For anyone else looking for something similar here is the two ways I
// First I put both tables into separate lists ( var crimes and var legislation) using the
// way shown in my question. Code for that is omitted here
//Query style
var crimesListItems = from c in crimes
join l in legislations on c.Legislation equals l.Id
select new { c.Name, l.Title };
// Lambda style
var crimesListItems = crimes.Join(
legislations,
c => c.Legislation,
l => l.Id,
(c, l) => new {c.Name, l.Title} );
Credit to Jeremy Clark and his tutorial at https://www.youtube.com/watch?v=tzR2qY6S4yw

Related

Joining Entity Framework tables using Linq when IDs do not exist in both tables

Below is a class I have used to generate a table in my database using Entity Framework. I'd like to be able to link this table to another table, Property. However, the way my code is set up there is not an Id column in the Instruction table, there is a Property property within the class, which then generates a PropertyId column in the actual database, but since the Property property is not an Id I am unable to using Linq to join these tables.
Instruction table
[Table("Instruction")]
public class Instruction
{
[Key]
public int Id { get; set; }
public InstructionTypes InstructionType { get; set; }
public Property Property { get; set; } //Generates the EF property FK, but is not an ID so therefore cannot be used in linq.
}
Property table
[Table("Property")]
public partial class Property
{
[Key]
public int Id { get; set; }
public Address Correspondence { get; set; }
}
Join Query
var instruction =
from instructions in _context.Instructions
join properties in _context.Properties on instructions.Property equals properties.Id
where ...
The above query gives a compiler error of: `The type of one of the expressions in the join clause is incorrect.
This error is being generated as I'm attempting to use a property object to join with a propertyId.
How can I alter this query so that I am able to join these two tables?
In 99% of all cases, you do not want to use the join operator. Entity Framework automatically generates SQL JOINS for you when you are using Navigation Properties.
var instruction = await _context.Instructions.Where(i => i.Property...).FirstOrDefaultAsync().ConfigureAwait(false);
Note, that depending on whether you are using EF6 or EF Core or with different configuration, Lazy Loading may be disabled (if not, I strongly encourage you to disable it as it is a massive performance bottleneck).
So you have to use the Include Method to eagerly load the related entity.
var instruction = await _context.Instructions.Include(i => i.Property).Where(i => i.Property...).FirstOrDefaultAsync().ConfigureAwait(false);
But before doing this, think if you really need the Instruction. If not, your code could become:
var property = await _context.Properties.Where(p => p.Instructions.Any(i => ...)).FirstOrDefaultAsync().ConfigureAwait(false);
Please note that you have to extend your Property class for this to work to have a back-reference
public partial class Property
{
// No need for the Key attribute, as this is convention
public int Id { get; set; }
public Address Correspondence { get; set; }
public int CorrespondenceId { get; set; } // Not needed in this scenario, but good practice
public ICollection<Instruction> Instructions { get; } = new HashSet<Instruction>();
}
You seems to be a newcomer to linq. As such you are still thinking as if you still are in an sql world.
With linq to entities, the use of join is the exception. SQL join are generated silently by EF using the navigation properties.
So your query can be:
var instruction =
from instruction in _context.Instructions
where instruction.Porperty.Correspondence.Contains("abc");
then you can access
instruction.First().Property.Correspondence
As a good practice you can delclare the foreign keys as class members and use the fluent API to bind them.
To test you can use the following code,
//assuming that Instructions is a DbSet<Instruction>
using (var context = new MyContext() ) {
context.Instructions.Add(
new instruction {
Property = new Property {
Correspondence = new Address {}
}
});
}
using (var context = new MyContext() ) {
var c = context.Instructions.First();
console.WriteLine($"{c.Id}, {c?.Property.Id}, {c?.Property?.Correspondence.Id}");
});

Showing User Names (stored in ASP.Net Core Identity system), instead of IDs When loading a table

I've having a lot of trouble doing what you would assume a simple task.
Changing user IDs to username when showing a table at my web application.
This is the data retrieved form the table:(unnecessary information removed)
var data = (from o in _mainDbContext.blog
select new blogViewModel
{
Id = o.id,
title = o.title,
Editor= o.editorID,
} );
editorID is the user ID created by ASP.Net Identity system. I need to load the corresponding user name and place it inside Editor fields for all the entries fetched.
What I have tried so far:
Something like:
var data = (from o in _mainDbContext.blog
join u in _userManager.Users on o.editorID equals u.UserName
select new blogViewModel
{
Id = o.id,
title = o.title,
Editor= o.editorID,
} );
Doesn't work because EF Core doesn't support joining tables from different contexts.
Using foreach like:
foreach (var row in data)
{
var user = await _userManager.FindByIdAsync(row.editorID);
row.Editor= user.UserName;
}
Doesn't work. It doesn't change the information inside data.
Trying to use raw SQL did not help either. Because FromSql works only on one table and ExecuteSqlCommand does not work with SELECT.
Currently, EF Core don't support query multiple DbContext with one query. You could trace this behavior Query: Throw if second context instance is used in single query #11101.
For a workaround, you may consider convert _userManager.Users to _userManager.Users.ToList() which is a list object.
var data = from o in _mainDbContext.Blogs
join u in _userManager.Users.ToList() on o.EditorID equals u.Id
select new BlogViewModel
{
Id = o.Id,
Title = o.Title,
Editor = u.UserName
};
var result = data2.ToList();
Why are your contexts separated ? Why not merge them and create a relation with a navigation property between Users and Blogs.
public class MainDbContext: IdentityDbContext<AppUser>
{
public DbSet<Blog> Blogs { get; set; }
}
public class Blog
{
//Some properties
public int EditorId { get; set; }
public AppUser Editor { get; set; }
}
With this you can easily access user's info via the navigation property Editor.

Performing join using linq query in Entity Framework

I'm trying to select some data from a table in my database using a join in a linq query, but I can't seem to grasp how to save it to the list of DTO's that I would like to return.
I've been looking at this post for directions at using the lambda expression: C# Joins/Where with Linq and Lambda but it seems like that guy is trying to accomplish something slightly different than me; I want to compare the value CPR (from the table Coworkers) and the value CPR (From the table Duties) and select all of those where the Projektname (from the table Duties) are equal to the string projektname.
What I've written so far of the method is this:
public List<CoworkerDTO> GetCoworkers(string projektname)
{
_coworkerlist = new List<CoworkerDTO>();
using (var context = new F17ST2ITS2201608275Entities())
{
var dataset =
from co in context.Coworkers
join du in context.Duties on co.CPR equals du.CPR
where du.Projektname == projektname
select new {Coworkers = co};
foreach (var element in dataset.ToList())
{
_coworkerlist.Add(element);
}
}
return _coworkerlist;
}
The CoworkerDTO looks like this:
class CoWorkerDTO
{
public string Fornavn { get; set; }
public string Efternavn { get; set; }
public int Alder { get; set; }
public string CPR { get; set; }
public decimal AntalTimer { get; set; }
}
The table Coworkers has a column that corresponds to each of the properties above, so I guess my question is how to somehow convert the selection that I get into a list of the CoworkerDTOs.
Sorry for the long post, and if my english is a bit confusing, as it's not my first language.
Thanks in advance :)
You should convert Coworkers entity into CoWorkerDTO. You can do it manually (assume properties have same names and types):
var dtos =
from co in context.Coworkers
join du in context.Duties on co.CPR equals du.CPR
where du.Projektname == projektname
select new CoWorkerDTO {
Fornavn = co.Fornavn,
Efternavn = co.Efternavn,
Alder = co.Alder,
CPR = co.CPR,
AntalTimer = co.AntalTimer
};
return dtos.ToList();
Or you can use something like AutoMapper Queryable Extensions to do that projection automatically:
Mapper.Initialize(cfg =>
cfg.CreateMap<Coworkers, CoWorkerDTO>());
And query with projection will look like
var entities =
from co in context.Coworkers
join du in context.Duties on co.CPR equals du.CPR
where du.Projektname == projektname
select co;
return entities.ProjectTo<CoWorkerDTO>().ToList();

Return data from 2 tables with Entity Framework

I'm working with MVC3 and Entity Framework but i came to a point where i need more data from different tables. Usually i'd do something like this to get data from a table:
Table: Users
id
username
In code i would do something like this to get all the users:
public static IEnumerable<Users> GetUsers( int userId )
{
MyEntities ent = new MyEntities();
return from g in ent.Users
where g.OwnerUserId == userId
select g;
}
So this would give me all my users back.
But a user can join a group, and i have to get all the usernames from a specific group.
Table: userGroups
id
fk_user_id
fk_group_id
Now if i'd use this code:
public static IEnumerable<userGroups> GetUsersFromGroup( int groupId )
{
MyEntities ent = new MyEntities();
return from g in ent.userGroups
where g.OwnerUserId == userId
select g;
}
Now obviously this only returns me the data from the "userGroups" table. But somehow i also need the username from the Users table. How can i get that data too and still return my "userGroups" as an IEnumerable?
In SQL i'd simply do a LEFT JOIN, but i can't really figure out how that works here.
Something like this maybe:
var query = from g in ent.userGroups
join u in ent.Users on g.fk_user_id equals u.userID
select new { g, u, });
Or with a LEFT JOIN
var query = (from g in ent.userGroups
from u in ent.Users.Where(a => a.fk_user_id == u.userID).DefaultIfEmpty()
select new { g, u, });
var query = from ug in ent.userGroups
join u in ent.Users on ug.OwnerUserId = ug.userID
select new
{
Name = u.UserName,
Id = u.userID
Group = ug.GroupName
};
If you need left join then you would require DefaultIfEmpty.
Please check the following articles:
http://codingsense.wordpress.com/2009/03/08/left-join-right-join-using-linq
http://www.dotnetperls.com/join
The above queries are going to require you to change your method signature, which could be a lot of really painful work depending on where you have this set up. Arion's in particular pretty much totally mimics the left join behavior you're talking about (which is great, because you know what Entity is doing), but you will need to change your return type to a Tuple<userGroups, Users> or something of that nature.
What you may try instead is to update the userGroups poco to include a nav property to the Users table. If I'm understanding your posted question properly, you have a one-to-many relationship that exists here. In this case, you would change the poco as follows:
public class userGroups
{
public int ID { get; set; }
public string GroupName { get; set; }
public virtual ICollection<Users> Users { get; set; }
}
public class Users
{
public int ID { get; set; }
public string Name { get; set; }
public virtual userGroups UserGroup { get; set; }
}
However, the names you've posted in your original question are not what Entity considers normalized naming, so you may need to use data annotations as described here. Ctrl-F "ForeignKey" if you're having some trouble finding it, it's kind of a big infodump on data annotations as a whole.
The benefit is, if you link in like this you will never have to worry about joining again. You can simply access the Users collection on userGroups and it will be accessed, joined, and worked out all for you.

Pass Linq Expression to a function

I want to pass a property list of a class to a function. with in the function based on property list I'm going to generate a query. As exactly same functionality in Linq Select method.
Here I'm gonna implement this for Ingress Database.
As an example,
in front end I wanna run a select as this,
My Entity Class is like this
public class Customer
{
[System.Data.Linq.Mapping.ColumnAttribute(Name="Id",IsPrimaryKey=true)]
public string Id { get; set; }
[System.Data.Linq.Mapping.ColumnAttribute(Name = "Name")]
public string Name { get; set; }
[System.Data.Linq.Mapping.ColumnAttribute(Name = "Address")]
public string Address { get; set; }
[System.Data.Linq.Mapping.ColumnAttribute(Name = "Email")]
public string Email { get; set; }
[System.Data.Linq.Mapping.ColumnAttribute(Name = "Mobile")]
public string Mobile { get; set; }
}
I wanna call a Select function like this,
var result = dataAccessService.Select<Customer>(C=>C.Name,C.Address);
then,using result I can get the Name and Address properties' values.
I think my Select function should looks like this,
( *I think this should done using Linq Expression. But im not sure what are the input parameter and return type. * )
Class DataAccessService
{
// I'm not sure about this return type and input types, generic types.
public TResult Select<TSource,TResult>(Expression<Func<TSource,TResult>> selector)
{
// Here I wanna Iterate through the property list, which is passed from the caller.
// Here using the property list,
// I can get the ColumnAttribute name value and I can generate a select query.
}
}
This is a attempt to create a functionality like in Linq. But im not an expert in Linq Expressions.
There is a project call DbLinq from MIT, but its a big project and still i couldn't grab anything helpful from that.
Can someone please help me to start this, or can someone link me some useful resources to read about this.
What you're trying to do is creating a new anonymous type that consists of Name and Address. This is easily achievable via long form linq (I made that term up, for lack of a better explanation.) Here's a sample from Microsoft, link provided below:
public void Linq11()
{
List<Product> products = GetProductList();
var productInfos =
from p in products
select new { p.ProductName, p.Category, Price = p.UnitPrice };
Console.WriteLine("Product Info:");
foreach (var productInfo in productInfos)
{
Console.WriteLine("{0} is in the category {1} and costs {2} per unit.", productInfo.ProductName, productInfo.Category, productInfo.Price);
}
}
Details: Linq Select Samples
Update:
So are you trying to do something like this then?
var result = dataAccessService.Select<Customer>(c => c.Name, c => c.Address);
public object[] Select<TSource>(params Expression<Func<TSource, object>>[] selectors)
{
var toReturn = new object[selectors.Count()];
foreach (var s in selectors)
{
var func = s.Compile();
//TODO: If you implement Select a proper extension method, you can easily get the source
toReturn[i] = func(TSource);
}
return toReturn;
}
I don't understand why you're trying to implement Select as a function of DataAccessService? Are trying to create this as an extension method rather?
If this is not what you mean though, you need to rephrase you're question big time and as one commenter suggested, tell us what you need not how you want us to design it.

Categories

Resources