I have a dot net 4.0 DAL library project that contains three edmx files. I chose to split my data model in three files because there are many tables in my db, and from what I've read here at Stackoverflow it seemed best practice to do so.
But now I have three connection string entries in my App.config that basically uses the same database connection. Can I somehow get back to the old ado.net style with only one connection string in my config files?
Thanks,
Jens
In your code you could use the EntityConnectionStringBuilder to build the three separate connection strings from a 'regular' connection string that's stored in your config file.
You would get something like:
string providerString = <load your connection string>;
// Initialize the EntityConnectionStringBuilder.
EntityConnectionStringBuilder entityBuilder =
new EntityConnectionStringBuilder();
//Set the provider name.
entityBuilder.Provider = providerName;
// Set the provider-specific connection string.
entityBuilder.ProviderConnectionString = providerString;
// Set the Metadata location.
entityBuilder.Metadata = #"res://*/AdventureWorksModel.csdl|
res://*/AdventureWorksModel.ssdl|
res://*/AdventureWorksModel.msl";
You can create three EntityConnectionStrings and change the MetaData property on each one to point to your Model.
But be aware that this would lead to a hardcoded part of your connection string in code.
Related
In the following microsoft documentation: -
https://learn.microsoft.com/en-us/sql/ado/guide/appendixes/microsoft-data-shaping-service-for-ole-db-ado-service-provider?view=sql-server-2017
this feature is being removed and the suggestion is to use XML. Has anyone done this? I'm wondering what they mean, in terms of loading the structure of what MSDataShape is by using XML, or just to use XML objects?
TIA
I believe this is referring to the FOR XML clause of T-SQL, which performs much the same job as MSDataShape in that it returns hierarchically nested data.
Port your MSDataShape queries to FOR XML queries and change the client to parse the results instead of using the MSDataShape OLEDB provider.
At the client side, SAX or pull parsing would be the best fit to port code that previously used MSDataShape (which also had a move-through-the-records cursor based model).
This is my bit of code that is helpful. My MSDataShape code still works, therefore I propose using that to generate your XML as a template, then use that going forward to load them: -
Dim objShapeMaker As clsShapeMaker
Dim rsoTemp As ADODB.Recordset
Dim strXMLTemplate As String
' Template file
strXMLTemplate = "C:\Temp\Template_GI.xml"
' Create the MSDataShape and save it to XML
Set rsoTemp = objShapeMaker.CreateGI()
rsoTemp.Save strXMLTemplate, adPersistXML
' Now we have the XML in a file going forward, load it in my recordset
Set rsoTemp = New ADODB.Recordset
rsoTemp.Open strXMLTemplate, , , , adCmdFile
' Cleanup
Set rsoTemp = Nothing
Set objShapeMaker = Nothing
If you don't like the idea of generating XML template files to maintain, you could do this via .NET and expose it to COM to use in your VB6/VBA application as mentioned here.
I have made a .NET application that can generate these XML files from simple code lines should anyone want going forward that is similar to the blog listed, however it handles child recordsets with relationships.
EDIT 1: This works great if you have schema set ups without returning data. As far as I can tell, to populate these effectively, it's better to write code to load the structure first, and populate it after from seperate recordsets (which is slower!)
EDIT 2: This is the approach we are taking with a replacement in a .NET Interop. Initially looking at bringing XML from SQL and parsing that back as required. This could be bought back into a DataSet and that's parsed into the target recordset as well, but then the relationship between the tables in the result dataset needs to be set in code rather than the one place in T-SQL with XML output.
I'm trying to write a Casacading(v1.2) casade (http://docs.cascading.org/cascading/1.2/userguide/htmlsingle/#N20844) consisting of two flows:
1) The first flow outputs urls to a db table, (in which they are automatically assigned id's via an auto-incrementing id value).
This flow also outputs pairs of urls into a SequenceFile with field names "urlTo", "urlFrom".
2) The second flow reads from both these sources and tries to do a CoGroup on "urlTo" (from the SequenceFile) and "url" (from the db source) to get the db record "id" for each "urlTo".
It then does a CoGroup on "urlFrom" and "url" to get the db record "id" for each "urlFrom".
The two flows work individually - if I call flow.complete() on the first before running the second flow. But if I put the two flows in a cascade object I get the error
cascading.cascade.CascadeException: no loops allowed in cascade, flow: urlLink*url*url, source: JDBCTap{connectionUrl='jdbc:mysql://localhost:3306/mydb', driverClassName='com.mysql.jdbc.Driver', tableDesc=TableDesc{tableName='urls', columnNames=null, columnDefs=null, primaryKeys=null}}, sink: JDBCTap{connectionUrl='jdbc:mysql://localhost:3306/mydb', driverClassName='com.mysql.jdbc.Driver', tableDesc=TableDesc{tableName='url_link', columnNames=[urlLinkFrom, urlLinkTo], columnDefs=[bigint(20), bigint(20)], primaryKeys=[urlLinkFrom, urlLinkTo]}}
on trying to configure the cascade.
I can see it's coming from the addEdgeFor function of the CascadeConnector but I'm not clear on how to resolve this problem.
I've never used Cascade / CascadeConnector before. Is there something I'm missing?
It seems like your some paths for source and sinks are the same.
A Cascade uses the concept of Direct Graphs to build the Cascade itself so if you have a flow source and a sink source pointing to the same location that in essence creates a loop and is disallowed in the concept of Directed Graphs since
it does not go from:
Source Location A to Sink Location B
but instead goes from:
Source Location A to Sink Location A.
"A Tap is not given an explicit name by design. This is so a given Tap instance can be re-used in different {#link Flow}s that may expect a source or sink by a different logical name, but are the same physical resource."
"In general, two instances of the same Tap class must have differing Identifiers (and different #equals)."
It turns out that JDBCTaps generate their identifier from the connection url alone (and do not include the table name). So as I was reading from one table and writing to a different table in the same database it seemed like I was reading from and writing to the same Tap and causing a loop.
As a work-around, I'm going to subclass the JDBCTap and override the getIdentifier() method to include the table name.
I came across with a big problem yesterday. In my current project I use ojdbc6 implementation of Oracle's JDBC for a connection, but also I would need to handle for example oracle 8 databases, which is totally impossible with this JAR.
You would say that I should use ojdbc14 for example which was true for some tests, but lets assume that later on I will need to handle 2 kind of databases from the same vendor, but we know that there is no existing implementation for BOTH and I need to have those simultaneously loaded. Same interface (and well, not just same interface, same class-structure, just different implementation inside!), same URL connection prefix -> JDBC connection will use one driver, but I cannot load multiple of them. So what now?
My first idea was to load the JARs with different classloaders, maybe I could load the same package structure with the same classes separated from each other? I don't really think so, maybe that was a silly idea of mine. This could be also a general problem later not with just JDBC drivers, so even if you cannot answer to my question but you know what is lacking here please tell me
Even if I could do a separate loading of class implementations of the same class names, how I can tell to the DriverManager when creating a connection to use the EXACT driver instead of finding one based on the connection url's prefix? (where I mean jdbc:oracle:thin for example).
I feel like a total dumb now because I think this is not an entirely extraordinary idea to handle in the Java world, BUT I totally don't know how to handle.
Thanks for y'all in advance
You actually have a couple of options:
You can try to load the drivers from different class loaders. That will work if you need only pure JDBC in your application. I doubt that you will get Hibernate to work with such a setup.
Eventually, you will have to run code where you will need to see instances from both classloaders and here, you will get ClassCastExceptions (two classes with the same full qualified name are different when they were loaded from different class loaders).
You can split your application into two. The second one would a small server which takes commands from your original app and translates those into JDBC for the database. The small server talks to Oracle 8 while your app only talks to one database.
This approach would allow you to keep the two concerns completely separate but you won't be able to run joins on the two databases.
You can link the old Oracle 8 database in your new database using CREATE DATABASE LINK. That makes the old tables visible as if they were part of the new database. You app only talks to one DB and Oracle handles the details internally.
Maybe Oracle 8 is too old for this to work but I'd definitely give it a try.
Oracle JDBC drivers are more compatible that you might expect. When you say "which is totally impossible with this JAR", did you try it? I used an Oracle 10 driver to connect to Oracle 7 in the past. Not every feature was supported but I could run the standard queries and updates.
#jdbc.properties
oracle.driver=oracle.jdbc.OracleDriver
oracle.url=jdbc:oracle:thin:#//localhost/xe
oracle.user=scott
oracle.password=tiger
mysql.driver=com.mysql.jdbc.Driver
mysql.url=jdbc:mysql://localhost/sales
mysql.user=root
mssql.driver=com.microsoft.sqlserver.jdbc.SQLServerDriver
mssql.url=jdbc:sqlserver://192.168.1.175;databaseName=sales
mssql.user=dbviewer
mssql.password=dbviewer
And then read the properties file:
class QueryTest2 {
public static void main(String[] args) throws Exception{
Properties settings = new Properties();
FileInputStream fin = new FileInputStream("jdbc.properties");
settings.load(fin);
fin.close();
String dvr = settings.getProperty(args[0] + ".driver");
String url = settings.getProperty(args[0] + ".url");
String usr = settings.getProperty(args[0] + ".user");
String pwd = settings.getProperty(args[0] + ".password");
Class.forName(dvr);
Connection con = DriverManager.getConnection(url, usr, pwd);
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("select pno,price,stock from products");
while(rs.next()){
System.out.printf("%d\t%.2f\t%d%n", rs.getInt(1), rs.getDouble(2), rs.getInt("stock"));
}
rs.close();
stmt.close();
con.close();
}
}
For the project I'm working on, I can't use the [dbo] schema. From looking at the EventStore source, it doesn't look trivial to use a non-dbo schema.
So far, the best I've come up with is to use a custom dialect like this:
Sub-class CommonSqlDialect
Add a private instance of MsSqlDialect
Then override all virtual properties of CommonSqlDialect to do something like
Example:
public override string AppendSnapshotToCommit
{
get { return customizeSchema(_msSqlDialect.AppendSnapshotToCommit); }
}
private string customizeSchema(string dboStatement)
{
// replace "[dbo]" with "[notdbo]",
// replace " Commits" with " [notdbo].Commits",
// replace " Snapshots" with " [notdbo].Snapshots"
}
I also have to customize the InitializeStorage property to replace "sysobjects" with "sys.objects" so I can add an additional constraint on the schema name.
This works, but it seems like there should be wireup options for customizing the schema and table names.
UsingSqlPersistence(...)
.WithSchema(...)
.WithCommitsTable(...)
.WithSnapshotsTable(...)
Is there a clearly better way to handle this that I've missed?
While I can see a potential need to customize the table names, the existing version does not support that. All you need to do is to subclass the MsSqlDialect and then provide your custom version to the wireup like so:
UsingSqlPersistence(...)
.WithDialect(new MsSqlDialectWithCustomTableNames());
A solution that doesn't require any code changes, and is good for security:
Create a new user in database and give him read/write only access to the new schema.
Set the new schema as a default schema to the user.
Add a new 'EventStore' connection string to the config file.
Pass this new connection to UsingSqlPersistence constructor.
All the queries in the event store are not prefixed with schema name, so changing a default schema for the user effectively 'redirects' all the calls to the new schema.
What is more, having a specific user for event store with limited permissions is a good thing anyway. Make sure that other db users don't have access to your new schema.
I see articles on using SPMetal to generate the .cs file that allows LINQ to work properly. The file I'm talking about inherits from the Microsoft.SharePoint.Linq.DataContext class. How can I use LINQ without recompiling on my production environment, since I would need to regenerate this file using SPMetal on my production environment? I suspect the answer is going to be "can't do it".
I guess I'll use a CAML query instead unless there is some easier way to use LINQ that I am missing.
If the objective is just to query lists using LINQ and you want to avoid such recompilations, do not use SPMetal.
LINQ can be directly used on SPListItemCollection
e.g.
var FindCustomer = from SPListItem Item in Customers.Items
where Item["Orders"] as int == 5
select Item;
//or select new{Title = Item["Title"]}
This does not have hard coded entities but is more flexible. And as long as your list column names remain same, code can be deployed on any environment even if other lists are changing.
Also you can choose to retrieve few chosen field's data instead of retrieving data of all the fields every time.
There is no problem I guess. Personally I have been using Linq for good amount of time. I never generated the cs specifically for production. Is your site different across environments?
Im not sure if I'm missing the point or not, but the DataContext object takes the URL as apart of the constructor, so you should retrieve the URL from config somewhere E.g. database
DataContext teamSite = new DataContext("http://MarketingServer/SalesTeam");
OR use the SPContext object, if your code has a SharePoint context. E.g. in a web part
DataContext teamSite = new DataContext(SPContext.Current.Web.Url);