Is it possible to fetch a link table without fetching all links? - c#

Ok, so first of I would like to say that I'm using NHibernate for my project, and in this project we have (among other things) a sync function (to sync from a central MSSQL database to a local SQLite). Now I know that NHibernate was not made to sync databases, but I would like to do this anyways.
I have a medium large database model so I can't add it here, but the problem is that I have two datatables, and one link table to link them both.
Database model:
| Product | | ProductLinkProducer | | Producer |
|--------------------| |---------------------| |---------------------|
| Id | | LinkId | | Id |
| Name | | Product | | Name |
| ProductLinkProducer| | Producer | | ProductLinkProducer |
Database:
| Product | | ProductLinkProducer | | Producer |
|---------| |---------------------| |----------|
| Id | | LinkId | | Id |
| Name | | ProductId | | Name |
| | | ProducerId | | |
So during the sync, I first copy all data from the Product table, and then from the Producer table (basically var products = session.Query<Products>().ToList()). This is done by NHibernate in a single statement each:
select
product0_.id as id2_,
product0_.name as name2_
from
Product product0_
Now I have to evict all items from the first session (products.ForEach(x => session.Evict(x));)
And then the save (products.ForEach(x => syncSession.save(x));) is one insert per row (as expected).
So when saving the data in the link table I would have wished that there also would be just a single select. However that is not the case. Because first it makes a select ... as above. But now before every row to insert it does even more select for the Product and for the Producer.
So it will look something like:
Products:
select
insert (id 1)
insert (id 2)
Producer:
select
insert (id 101)
insert (id 102)
ProdLinkProducer:
select
select id 1 from Products
select id 1 from Products
select id 101 from Producer
select id 2 from Products
select id 2 from Products
select id 102 from Producer
select id 102 from Producer
insert
insert
So is there anyway avoiding this behavior?
EDIT
To better explain what I have done, I have created a small test project. It can be found here: https://github.com/tb2johm/NHibernateSync
(I would have preferred to add only a ghist, but I think that it might have left out to much data, sorry...)
EDIT2
I have found out one way to make it work, but I don't like it.
The way this solution works is to in the database model create a ProductLinkProducerSync table, that doesn't contain any links, but just the values, and avoid synchronizing the ordinary link tables, but just the "sync" tables. But as I said I don't like this idea, since if I change anything in the database, I have kind of the same data in two places that I need to update.

I was unable to find NHibernate out of the box way of doing what you are asking.
However I was able to get the desired behavior (I guess something is better than nothing:) by manually rebinding the FK references (proxy classes) to the new session:
var links = session.Query<ProductLinkProducer>().ToList();
links.ForEach(x => session.Evict(x));
foreach (var link in links)
{
link.Product = syncSession.Get<Product>(link.Product.Id);
link.Producer = syncSession.Get<Producer>(link.Producer.Id);
syncSession.Save(link);
}
syncSession.Flush();
Here is the generalized version using NHibernate metadata services:
static IEnumerable<Action<ISession, T>> GetRefBindActions<T>(ISessionFactory sessionFactory)
{
var classMeta = sessionFactory.GetClassMetadata(typeof(T));
var propertyNames = classMeta.PropertyNames;
var propertyTypes = classMeta.PropertyTypes;
for (int i = 0; i < propertyTypes.Length; i++)
{
var propertyType = propertyTypes[i];
if (propertyType.IsAssociationType && !propertyType.IsCollectionType)
{
var propertyName = propertyNames[i];
var propertyClass = propertyType.ReturnedClass;
var propertyClassMeta = sessionFactory.GetClassMetadata(propertyClass);
yield return (session, target) =>
{
var oldValue = classMeta.GetPropertyValue(target, propertyName, EntityMode.Poco);
var id = propertyClassMeta.GetIdentifier(oldValue, EntityMode.Poco);
var newValue = session.Get(propertyClass, id);
classMeta.SetPropertyValue(target, propertyName, newValue, EntityMode.Poco);
};
}
}
}
and applying it to your Sync method:
private static void Sync<T>(string tableName, ISession session, ISession syncSession)
{
Console.WriteLine("Fetching data for ####{0}####...", tableName);
var sqlLinks = session.Query<T>();
var links = sqlLinks.ToList();
Console.WriteLine("...Done");
Console.WriteLine("Evicting data...");
links.ForEach(x => session.Evict(x));
Console.WriteLine("...Done");
Console.WriteLine("Saving data...");
var bindRefs = GetRefBindActions<T>(syncSession.SessionFactory).ToList();
foreach (var link in links)
{
foreach (var action in bindRefs) action(syncSession, link);
syncSession.Save(link);
}
Console.WriteLine("...Flushing data...");
syncSession.Flush();
Console.WriteLine("...Done");
Console.WriteLine("\n\n\n");
}

Related

Associate values of one list with the ones of another list with linq

I have two lists, one holding some information about customers and one other table with some more information about the financial status of a customer (1-to-1 relationship).
Customer table:
Id
Name
Address
Fincance table:
Id
CustomerId
Salary
My goal is to link each salary to the list of customers (not all customers have an associated recored in the finance table).
I am receiving all my customers and finances like this:
IEnumerable<Customer> customers= await _customerService.GetCustomers();
IEnumerable<CustomerDTO> customerDTOs = _mapper.Map<IEnumerable<Customer>, IEnumerable<CustomerDTO>>(kunden); // Map to CustomerDTO which contains a property for salary
IEnumerable<Finance> finances= await _financeService.GetFinances();
Example:
+----+-----------+---------+ +----+------------+--------+
| Id | Name | Address | | Id | CustomerId | Salary |
+----+-----------+---------+ +----+------------+--------+
| 1 | Microsoft | foo 1 | | 1 | 1 | 8,650$ |
| 2 | Apple | foo 2 | | 3 | 2 | 7,880$ |
| 3 | Testla | foo 3 | +----+------------+--------+
+----+-----------+---------+
Note: I am mapping Customer to a CustomerDTO which has a field Salary. That field should be filled with the ones of the Finance table. I cannot change the design of my tables and there is a reason behind the table design which I am not going to talk about due to simplicity. I am just interested if it is possible to use linq to do this kind of operation.
Yes, it's possible - you have the list of customers and the list of salaries.
To join them manually, you can just do
customers.Select(x=> new {
x.Id,
x.Name,
x.Address,
Salary = salaries.SingleOrDefault(y=>y.CustomerId==x.Id)?.Salary
}).ToList();
This will create a new list with the info joined.
You can also do a straight join as well:
https://learn.microsoft.com/en-us/dotnet/csharp/linq/perform-inner-joins
Another way of doing it besides the way that Matt explained is like this:
finances.ToList().ForEach(x => customerDTOs.Where(y => y.Id == x.CustomerId).First().Salary = x.Salary);
You can also do with Left outer join as well:
https://learn.microsoft.com/en-us/dotnet/csharp/linq/perform-left-outer-joins
From above link:
You have the list of customers and the list of Finance so Can you please try this:
var abc = from c in customers
join fa in Fincance on c.Id equals fa.CustomerId into fin
from fa in fin.DefaultIfEmpty()
select new
{
c.Id,
c.Name,
c.Address,
salary = fa != null ? fa.Salary : null
};

Is there an easy way to INNER join , OUTER join , LEFT OUTER join, RIGHT OUTER join, or UNION two (or more) DataTables in C#?

I am writing a c# application that connects separate database systems. These systems could be flat-file db's, Oracle, Sql, Excel Files, ext. The job of the C# application is to provide an outlet for making all of these sources available in one spot. So basically, the application accepts a list of queries and connection settings for the respective database systems and collects a bunch of results.
The goal is to output a singe DataTable with the result of all these queries all joined/unioned together(depending on settings). Does C# provide an easy way to perform any join/union operations on a list of DataTables?
For example:
Table1:
__________________________________________________________
|tb1_pk_id| tb1_name | tb1_data1 | tb1_data2 |
|---------|---------------|---------------|---------------|
| 1 | tb1name_blah1 | tb1dat1_blah1 | tb1dat2blah1 |
| 2 | tb1name_blah2 | tb1dat1_blah2 | tb1dat2blah2 |
| 3 | tb1name_blah3 | tb1dat1_blah3 | tb1dat2blah3 |
-----------------------------------------------------------
Table2:
__________________________________________________________
|tb2_pk_id| tb2_name | tb2_data1 | tb2_data2 |
|---------|---------------|---------------|---------------|
| 1 | tb2name_blah1 | tb2dat1_blah1 | tb2dat2blah1 |
| 2 | tb2name_blah2 | tb2dat1_blah2 | tb2dat2blah2 |
| 3 | tb2name_blah3 | tb2dat1_blah3 | tb2dat2blah3 |
-----------------------------------------------------------
Join Results:
__________________________________________________________ _______________________________________________
|tb1_pk_id| tb1_name | tb1_data1 | tb1_data2 | tb2_name | tb2_data1 | tb2_data2 |
|---------|---------------|---------------|---------------|---------------|---------------|---------------|
| 1 | tb1name_blah1 | tb1dat1_blah1 | tb1dat2blah1 | tb2name_blah1 | tb2dat1_blah1 | tb2dat2blah1 |
| 2 | tb1name_blah2 | tb1dat1_blah2 | tb1dat2blah2 | tb2name_blah2 | tb2dat1_blah2 | tb2dat2blah2 |
| 3 | tb1name_blah3 | tb1dat1_blah3 | tb1dat2blah3 | tb2name_blah3 | tb2dat1_blah3 | tb2dat2blah3 |
-----------------------------------------------------------------------------------------------------------
So far I have found the following code online (here) to do a merge on all the data:
private DataTable MergeAll(IList<DataTable> tables, String primaryKeyColumn)
{
if (!tables.Any())
throw new ArgumentException("Tables must not be empty", "tables");
if (primaryKeyColumn != null)
foreach (DataTable t in tables)
if (!t.Columns.Contains(primaryKeyColumn))
throw new ArgumentException("All tables must have the specified primarykey column " + primaryKeyColumn, "primaryKeyColumn");
if (tables.Count == 1)
return tables[0];
DataTable table = new DataTable("TblUnion");
table.BeginLoadData(); // Turns off notifications, index maintenance, and constraints while loading data
foreach (DataTable t in tables)
{
table.Merge(t); // same as table.Merge(t, false, MissingSchemaAction.Add);
}
table.EndLoadData();
if (primaryKeyColumn != null)
{
// since we might have no real primary keys defined, the rows now might have repeating fields
// so now we're going to "join" these rows ...
var pkGroups = table.AsEnumerable()
.GroupBy(r => r[primaryKeyColumn]);
var dupGroups = pkGroups.Where(g => g.Count() > 1);
foreach (var grpDup in dupGroups)
{
// use first row and modify it
DataRow firstRow = grpDup.First();
foreach (DataColumn c in table.Columns)
{
if (firstRow.IsNull(c))
{
DataRow firstNotNullRow = grpDup.Skip(1).FirstOrDefault(r => !r.IsNull(c));
if (firstNotNullRow != null)
firstRow[c] = firstNotNullRow[c];
}
}
// remove all but first row
var rowsToRemove = grpDup.Skip(1);
foreach (DataRow rowToRemove in rowsToRemove)
table.Rows.Remove(rowToRemove);
}
}
return table;
}
This works fine for doing a union, but I don't know if an easier way to do that already exists in .NET that will let me do ANY kind of join or union on a group of seprate DataTables (not just the union as in the code above) or do I have to custom code each type of join/union?
No, there is not a simple .Net way of doing this....
LINQ can come close... you can create table joins in LINQ, but they are typically "inner joins". Doing a "left join" is a bit more complicated and requires the GroupJoin keyword.
https://msdn.microsoft.com/en-us/library/bb386969(v=vs.110).aspx
If you'd like "do it yourself" with ADO.Net DataRelations, you might take a look at this old VB.Net article:
http://www.emmet-gray.com/Articles/DataRelations.html

Filter a BindingSource based on the rows of another DataGridView

I have two DataGridViews in Winforms. DataGrid1 is connected to a table that contains a list of jobs that need to be completed. Once someone completes a job, it's entered into a separate table as completed, which is connected to DataGrid2.
I need to filter the binding source for DataGrid1 so that when a job shows up as completed in DataGrid2 it's filtered out of DataGrid1. The current code I'm using only filters the binding source by the last entry in DataGrid2 and I need it to filter by all of the entries.
How do I filter the BindingSource for DataGrid1 based on all values of the a column of DataGrid2?
foreach (DataGridViewRow row in dataGrid2.Rows)
{
DataGrid1BindingSource.Filter =
string.Format("ColumnName <> '{0}'", row.Cells[1].Value);
}
Here is an example of all jobs in a data table, then the first grid which contains incomplete jobs and the second grid which contains completed jobs. The jobs which should be shown in Incomplete grid, are those jobs which are not in Completed jobs grid:
__________ ____________ ___________
| All Jobs | | Incomplete | | Completed |
|――――――――――| |――――――――――――| |―――――――――――|
| JobTitle | | JobTitle | | JobTitle |
|――――――――――| |――――――――――――| |―――――――――――|
| Job 1 | | Job 1 | | Job 3 |
| Job 2 | | Job 2 | | Job 4 |
| Job 3 | | | | |
| Job 4 | | | | |
‾‾‾‾‾‾‾‾‾‾ ‾‾‾‾‾‾‾‾‾‾‾‾ ‾‾‾‾‾‾‾‾‾‾‾
Before reading the answer, you should know if you don't have a bool field or something to detect which job is completed it's not a good design. You should have a single list of jobs. Then based on a bool field you should show incomplete jobs in first grid and completed jobs in second grid. Then the filter would be simply Completed = true and Completed = false.
Anyway, you can use IN in filter expression. It's enough to create a list of values which you want to use in filter, then create the filter this way:
var ids = this.dataGridView2.Rows.Cast<DataGridViewRow>()
.Where(r => !r.IsNewRow)
.Select(r => r.Cells[0].Value.ToString());
bs1.Filter = string.Format("Column1 NOT IN ({0})", string.Join(",", ids));
In the above example I supposed ids are int so for example "Column1 NOT IN (1,2,3)" will be the filter. For string ids, the filter would be "Column1 NOT IN ('a','b','c')". So you can change the select statement like below:
.Select(r => string.Format("'{0}'",r.Cells[0].Value.ToString()));
This snippet is pretty ugly, but it should give you a hint of what to do:
var colname = YOURGRIDTOFILTER.Columns[INDEXOFCOLUMNTOFILTER].HeaderText;
var filterString = colname+" <> ";
foreach (DataGridViewRow row in dataGrid2.Rows)
{
filterString += "'" + row.Cells[1].Value + "' OR "+colname+" <> ";
}
filterString = filterString.Substring(0, filterString.LastIndexOf("OR"));

SQL Theory Multiple References

i am designing a database and have theory problem, about which solution works better to run queries, to be faster on microsoft sql server or simply more relational.
GIVEN
Lets say, we have the following Tables:
Congress, Person, Session, Room, and much more.
Don't mind about the given names. These are just some basic standalone entities.
-----------------------------------------------------------------
| Congress | Person | Session | Room |
-----------------------------------------------------------------
| CongressID | PersonID | SessionID | RoomID |
| Name | Name | Name | Name |
| ... | ... | ... | ... |
-----------------------------------------------------------------
Additionally we have a table called "Right". Rights have a name and can define access to something like one or many of the basic entities. Each person can have those rights assigned.
So there are 2 more tables:
Right and PersonRight
---------------------------------
| Right | PersonRight |
---------------------------------
| RightID | PersonRightID |
| Name | PersonID |
| ... | RightID |
| ... | ... |
---------------------------------
SOUGHT-AFTER
Now there is only one thing missing. The way or table that represents the relations to the other entities. I know three different ways that all will work, but i don't have the deep experience to decide which one will be the best.
1. The relational way?
Upgrade: For every new entity, add a new table
Relation: Right 1 : N Entities
Pros: Adding new entities doesn't affect the others in any way, foreign keys to entities
Cons: Many tables with maybe redundant columns like CreatedDate or rowguid.
SQL Example::
select *
from Right r
left join RightCongress rc on r.RightID = rc.RightID
left join RightSession rs on r.RightID = rs.RightID
left join RightRoom ro on r.RightID = ro.RightID
left join Congress ec on rc.CongressID = ec.CongressID
left join Session es on rs.SessionID = es.SessionID
left join Room er on ro.RoomID = er.RoomID
-------------------------------------------------------
| RightCongress | RightSession | RightRoom |
-------------------------------------------------------
| RightCongressID | RightSessionID | RightRoomID |
| RightID | RightID | RightID |
| CongressID | SessionID | RoomID |
| ... | ... | ... |
-------------------------------------------------------
2. The column way?
2.1 The column way 1
Upgrade: For every new entity, add a new column to table "Right"
Relation: Right 1 : 1 Entities
Pros: No new table required, small statement, foreign keys to entities
Cons: Every new entity affect all other rows, only 1:1 relation possible, column count maybe confusing
SQL Example::
select *
from Right r
left join Congress ec on r.CongressID = ec.CongressID
left join Session es on r.SessionID = es.SessionID
left join Room er on r.RoomID = er.RoomID
-----------------
| Right |
-----------------
| RightID |
| Name |
| CongressID |
| SessionID |
| RoomID |
-----------------
2.2 The column way 2
Upgrade: For every new entity, add a new column to table "RightReference"
Relation: Right 1 : N Entities
Pros: 1:N relation, only one new table, small statement, foreign keys to entities
Cons: Every new entity affect all other rows, column count maybe confusing
SQL Example::
select *
from Right r
inner join RightReference rr on r.RightID on rr.RightID
left join Congress ec on rr.CongressID = ec.CongressID
left join Session es on rr.SessionID = es.SessionID
left join Room er on rr.RoomID = er.RoomID
---------------------------------------
| Right | RightReference |
---------------------------------------
| RightID | RightReferenceID |
| Name | RightID |
| ... | CongressID |
| ... | SessionID |
| ... | RoomID |
| ... | ... |
---------------------------------------
3. The reference way
Upgrade: For every new entity, add a new row to RightReference with the new ReferenceTypeID
Relation: Right 1 : N Entities
Pros: Only one new table and dynamic references
Cons: Anonymous references and have always to remember the indexes to build queries, no foreign keys to entities
Explanation: ReferenceID is the primary ID of the referenced entity/row, like of table Congress, Session and so on. So you can't suggest to which table it references. For that reason there is ReferenceTypeID. It points to a translation table called ReferenceType, where every table is stored with an unique id. Maybe it is possible to use the system method OBJECT_ID instead.
SQL Example::
select *
from Right r
inner join RightReference rr on r.RightID = rr.RightID
left join Congress ec on rr.ReferenceID = CongressID and rr.ReferenceType = 1
left join Session es on rr.ReferenceID = SessionID and rr.ReferenceType = 2
left join Room er on rr.ReferenceID = RoomID and rr.ReferenceType = 3
----------------------------------------------------------
| Right | RightReference | ReferenceType |
----------------------------------------------------------
| RightID | RightReferenceID | ReferenceTypeID |
| Name | RightID | Name |
| ... | ReferenceID | ... |
| ... | ReferenceTypeID | ... |
| ... | ... | ... |
----------------------------------------------------------
And now to all the sql experts.
What is the best or better lets say state of the art solution/way/approach to handle this task?
If you have other ways, please let me know.
What i am Looking for is: General Advantages and Disadavantages, SQL-Performance, implementation difficulties with EntityFramework and everything else you know or think about it.
Thanks!
Usually when dealing with relational databases, anything that requires a schema change is a no-no because you have to do potentially dangerous operations on your SQL server, update EF as well as whatever models you may be using and probably redeploy whatever application serves as the frontend for your database.
The SQL Solution
If you're OK with committing a no-no every time a new entity is added or are for some other reason tied to an RDBMS, you have two options:
If you care about your entity(Congress, Session, Room) table schema
Column way #2 is probably the best idea because it separates relational data from actual table data. Make a separate table for the relationships between entities and rights and put an index on every possible entityId. In your example you'd need indices on CongressId, SessionId and RoomId columns.
If you don't care about your entity table schema
Combine all entity tables into one large Entities table with an Id column and an XML column that contains all your actual entity info such as the type. Single relationship between Entities and rights and you're good.
The NoSQL Solution
If you can go this route, it would probably suit the flexible structure you're looking for much better. You will still need to update the code that accesses the document store but judging from your proposal that seems unavoidable unless you have some extraordinarily-flexible-but-error-prone code in place.
You don't need to do schema/EF updates every time a new entity type is added, and you don't need to worry about relationships. Your Person objects will have all their rights nested right inside and will be stored in the document store exactly that way.

How to use Linq To Sql query to show the data in my case?

i have a trouble to write a query with Linq i explain better my case, i have a database with 2 tables as follow :
it is the first table ;
Hotel
HotelID (Nvarchar(10) - PK)
HotelName (Nvarchar (200))
and this one is the second table ;
Period
PeriodID (Int (inc) - PK)
_From (Datetime )
_To (Datetime)
HotelID(Nvarchar(10) - FK)
then in the second Table (Period) there is the FK (HotelID) to connect the 2 tables;
Happen sometime i have a HotelName that gets more periods(PeriodID) so my purpose is to show the data in an only one Row into a DataGrid, i show you an example as i want show the data in my DataGrid if there are more periods in the same HotelName:
| HotelName | From | To | From(2) | To(2) | From(3) | To(3) | From(4)| To(4) |
| Excelsior |12/5/10 |3/6/10 | 2/8/10 | 9/9/10 | 23/9/10 | 1/10/10| 2/11/11| 1/12/10|
so i ask do you have any idea/suggest about how to show the data in a DataGrid inside one Row using Linq To Sql ?
thanks so much for your attention .
Have a good time .
Cheers
This article explains working with hierarchical data binding: http://msdn.microsoft.com/en-us/library/aa478959.aspx
Then, create an object model which roughly maps to your database tables:
Hotel
- ID
- Name
- Bookings
- Booking 1 { From, To }
- Booking 2 { From, To }
- Booking n { From, To }
Your Linq should look something like this:
var hotels = _db.Hotel.Select();
foreach(var hotel in hotels)
hotel.Bookings = _db.Period.Where(x => x.HotelId == hotel.HotelId).Select();

Categories

Resources