Cross Database Join with Linq - Updating T4 template to support DB name? - linq

I'm currently running in a multi-DB SQL Server environment and using linq to sql to perform queries.
I'm using the approach documented here to achieve cross DB joins:
http://www.enderminh.com/blog/archive/2009/04/25/2654.aspx
so basically:
2 data contexts - Users and Payments
Users.dbo.UserDetails {PK: UserId }
Payments.dbo.CurrentPaymentMethod { PK: UserId }
I drag the tables onto the DBML, and in the properties window, change the Source from dbo.UserDetails to Users.dbo.UserDetails to fully qualify the DB name.
I can then issue a single data context cross DB join by doing something like:
var results = (from user in datacontext.Table<UserDetail>()
join paymentmethod in dataContext.Table<CurrentPaymentMethod>() on user.UserId equals paymentmethod.UserId
... rest of query here ...);
Now this is tickety boo and works as I want it to. The only problem I'm currently having is when schema updates etc. happen (which is relatively frequent as we're in a significant dev phase).
(and finally, the question!)
What I want to achieve (and I've marked the question up as T4 as a guess, as I know that the DBML files are T4 guided) is an automated way when I drag any table onto a data context that the Source automatically picks up the DB name (so will have Users.dbo.UserDetails instead of just dbo.UserDetails)?
Thanks for any pointers :)
Terry

Have a look at the T4 Toolbox and the LinqToSql code generator it provides (Courtesy of Oleg Sych) - You can customize the templates to generate references however you'd like, but I think the problem you're going to run into is that the database name isn't stored in the dbml file.
What you could probably do is add a filter to the generator, perhaps using a dictionary or similar, such that in your .tt file, you maintain a list of tables and the databases they belong to. That way, if your maintenance task is to delete the class from the designer and drop it on again, it will get the right database name.

Related

Visual Studio 2013 Dataset Designer refresh relations

I have an application with a dataset linked to an sql server database. I have updated some of the names or foreign keys and primary keys in the sql server. How do I make those changes translate to the data set. For example, I had a primary key called fk_temsempl_xxxxx. I changed it to fk_temsempl on the sql database. How do I get that change to show in the dataset designer in visual studio?
I have tried running custom tool by right clicking on the dataset and clicking run custom tool. That didnt work. I tried configuring the table adapter of one of the tables where a change occured, but the name of the relation didnt change.
You actually just right click the relation and choose Edit Relation... or double click on the line (when the mouse cursor changes from arrow to drag symbol) but I honestly wouldn't bother; you'll then have further refactoring to do in the code anywhere the relation is used, and it can be heavily used by visual designers.
You also get the problem that VS may not help you with the refactoring: in data binding scenarios most things that can be a source of data can also be a collection of multiple things that can be a valid DataSource. They then rely on a string DataMember to determine which of the collections of data in the data source should be used for the data.
For example, when a bindingsource is bound to list a DataTable, the bindingsource.DataSource property might be the DataSet object that contains the DataTable, and thebindingsource.DataMemberis a string of "YOUR_TABLE_NAME". the BindingSource might not be bound asmyBindignSource.DataSource = myDataSet.MyDataTable`. Refactoring inside strings involves a find and replace
DataRelations in a DataSet are created from foreign keys as they were discovered when the relevant table(s) were added to the dataset but it is important to note that, like DataTables and everything else, they are NOTHING to do with the database schema objects at all - they aren't permanently associated with them, the dataset entities are just set up looking something like the database objects when they (dataset entities) are first created. DataTables are created from only those columns selected, and whatever .NET datatypes closely resemble the types output by the query. For a table of:
Person
------
Name VARCHAR(50)
SSN INTEGER
Birthdate DATE
If you created the table with SELECT * FROM Person you'd get a datatable with Name (string), SSN (int), Birthdate (datetime) but if you made a new datatable in the dataset based on SELECT LEFT(Name, 1) as Initial, PADLEFT(SSN, 20) as PadSSN, DATEDIFF(day, Birthdate, NOW()) as AgeDays FROM Person then you'd get a datatable of Initial (string), PadSSN (string), AgeDays (int) - i.e. the datatable looks nothing like the db table. This concept of disconnection between dataset and db is pervasive, and really the only things that relate in any way to the database are the properties that specify which DB table/column a particular DataTable/DataColumn relates to for purposes of loading/saving data. Your Person.Name datacolumn can be renamed to Blahblah, but it will still have a .SourceColumn property that is set to "Name" - that's how the mapping between dataset and db works; dataset is predominantly completely independent of the db. Renaming a DB column would require a change to the SourceColumn property only
DataRelations don't even have this notion of linking to the parent relation in the database; there's no SourceRelation or SourceFK proeprty because there is no need to. They're set up with the same rules and a generated name all based on the rules of the FK, but then they function independently and only within the dataset. If you rename or even remove an FK from the db the dataset will carry on working in the same restricted way it always did; adding a datarow to a child table when no aprent row exists for it will throw an exception - none of it anything to do with the FK in the db, and the DataRelation can have different rules to the FK (e.g it can cascade deletes when the FK is NOACTION) or even different columns. You can have more or fewer DataRelations than the DB has FKs
Run Custom Tool is not a "contact the DB and see what changes have occurred there and replicate them into the dataset", it is a "turn the XSD that describes the dataset into a bunch of C# classes that implement strongly typed dataset/table/relation/column etc objects". Any time you change the XSD by making an edit in the visual designer and hit save, the custom tool is run. If you edit the XSD directly in a text editor you may need to run it manually to have your changes reflected in c# classes
Reconfiguring a tableadapter probably won't do anything to the relations either; its solely concerned with changing the datatable and tableadapter. If you really want to refresh the relations, delete the datatable from the set and recreate it. Be prepared for a potentially significant mop up/refactoring of code

How to join more than two tables using Lightswitch LINQ query

I am new to lightswitch as well as linq, i am using LS 2015. I want to know how to display the data of one table in other table's screen.
For example I have job vacancies, where I want to show candidate professional information who has applied for job. These two tables are related through Candidate personal ID, means a foreign key between candidate personal table and professional table.
The relation is as follows
JobVacancies->CandidatePersonalInfor->candidateProfessionalInfo. They are many to one related.
I have written a linq query to fetch data, where i have included some other tables also, my code is as follows
Any help is appreciated.
Have you defined the relationships you describe between the entities? If so, you'll have the ability to add and drop the details you require onto the form in the designer. The entities and their properties will be in the left pane of the screen designer.
If the join operation is more complex, you can use a WCF RIA Service to create a composite entity, as described in http://lightswitchhelpwebsite.com/Blog/tabid/61/EntryId/2226/Creating-a-WCF-RIA-Service-for-Visual-Studio-2013.aspx

How do I preserve my custom Linq to SQL class changes?

My MVC2 app use LINQ to SQL as the ORM. I just drag and drop the tables from the SQL Server Explorer connection to the LINQ design surface. Two of the tables (A and B) are related. Table A has 3 foreign keys referencing Table B. In the LINQ design surface, I manually change the name of the parent property for these associations to give them more distinguished names. However, whenever I modify the table structure in the SQL Server, and drag and drop the new structure to LINQ, I would lose the names. Is there a way to prevent this from happening? Thanks.
No. When you drag the tables back to the designer, Linq to SQL regenerates the code in the partial class, obliterating your custom changes.
The only way to preserve the name change is to rename the affected table fields in the database.
You might be able to achieve such customization preservation with the T4 Toolbox.
It is always a bad idea to make the associations at the LINQ level. Make the associations at the table level. This will ensure that when ever you are doing a drag and drop of the tables, the designer class reflects the correct associations.

"Injecting" a WHERE clause dynamically w/ PetaPoco

I'm building a multi-tenant app with a shared database using .NET MVC 3 and PetaPoco.
The tenant id (along with other info) is saved in a FormsAuth cookie on login and is available to all controllers via a BaseController property. Most tables, (i.e. apart from apart the main 'Tenants' table) include a TenantId column.
Instead of manually adding a 'WHERE TenantId = X' to all CRUD on the feature tables, is there a way I can dynamically add this to the query just before its executed? In other words, maybe maintain a list of tables, and if the query is for one of those tables, then dynamically add in the TenantId filter?
The benefit of course is that it removes the need to add in the filter manually thus reducing the chances its left out. I did find an example using NHibernate, which I doubt can be repurposed. I am using Ninject in case that makes a difference.
There is an OnCommandExecuting method on the Database class which you can override in your own sub class, and modify the sql as you wish just before it gets executed. We use this feature for converting isnull/nvl between Sql Server and Oracle.
You could just leave a marker in your sql and replace it here.

LINQ to SQL not recognizing new associations?

I have two projects using legacy databases with no associations between the tables. In one, if I create associations in the DBML file, I can reference the associations in LINQ like this:
From c In context.Cities Where c.city_name = "Portland" _
Select c.State.state_name
(assuming I added the link from City.state_abbr to State.state_abbr in the DBML file.)
In a different project that uses a different database, adding the association manually doesn't seem to give me that functionality, and I'm forced to write the LINQ query like this:
From c In context.Cities Where c.city_name = "Portland" _
Join s In context.States On c.state_abbr = s.state_abbr _
Select s.state_name
Any idea what I could be missing in the second project?
Note: These are completely contrived examples - the real source tables are nothing like each other, and are very cryptic.
Check your Error List page. You might have something like the following in there:
DBML1062: The Type attribute
'[ParentTable]' of the Association
element 'ParentTable_ChildTable' of
the Type element 'ChildTable' does not
have a primary key. No code will be
generated for the association.
In which case all you should need to do is make sure that both tables have a primary key set and re-save the dbml file. This will invoke the custom tool, which will in turn update the designer.cs file and create code for the association.
It looks like my problem was my tables didn't have primary keys in the second project. Like I stated, these are legacy tables, so I had to do the linking and primary key stuff in the Database Context instead of the database itself, and I just forgot to specify the primary keys the second time around. Frustrating when you don't spot it, but it makes sense now.
Sometimes, when everything is configured correctly but still not working, the solution can be as simple as restarting Visual Studio.
I don't know why it happens sometimes, but I thought I should add this answer because having done some searching for a solution myself, it seems nobody has suggested this yet...

Resources