How to get the sql generated by LinqToSql for update method?
I use the following code to show the sql generated by LinqToSql in VS2008's debug output window, but it only gets the sql select method generated,
how can I find the sql update method that was generated by LinqToSql?
I know the Sql Server Profiler and LinqPad can get it(the sql-update generated), but I want to show them in VS2008 or Log them to a file.
public partial class Linq2 : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
DemoDataContext ctx = new DemoDataContext ();
// Then attach it to the Log property of your DataContext...
ctx.Log = new DebugTextWriter();
var product = ctx.Products.FirstOrDefault();
product.ProductName = "NewName1";
ctx.SubmitChanges();
}
}
// Add this class somewhere in your project...
public class DebugTextWriter : System.IO.TextWriter
{
public override void Write(char[] buffer, int index, int count)
{
System.Diagnostics.Debug.Write(new String(buffer, index, count));
}
public override void Write(string value)
{
System.Diagnostics.Debug.Write(value);
}
public override Encoding Encoding
{
get { return System.Text.Encoding.Default; }
}
}
And I get the sql-select query in the VS2008 debug output window:
SELECT TOP (1) [t0].[Id], [t0].[ProductName] ……
FROM [dbo].[Products] AS [t0]
-- Context: SqlProvider(Sql2005) Model: AttributedMetaModel Build: 3.5.30729.1
Your Database context object has a Log method that you can override. Your full Update statement, and every SQL command generated by Linq-To-SQL, can be captured via this Log method. I know this works because I use it to capture all our queries in our app. Just keep in mind that L2S can send a fair amount of output to the Log method, so make sure to capture it all. Your Update statement is in there somewhere.
Thank you for all answers.
I have found Linq To Sql Profiler to solve the problem.
Related
I'm looking for a way to record and write all those SQL statements to an output
file which get executed while running a Liquibase migration against an empty
target database.
The idea behind this is to speed up the initialization phase of integration tests
against a test database by simply reading and executing the SQL statements from the
generated file for subsequent tests.
I had no luck using updateSQL due to different handling of changesets with
pre-conditions (e.g. changeSetExecuted resolves to true for "update" but false for
"updateSQL").
Another approach was to run the Liquibase migration first, then writing a temporary
changelog file using GenerateChangeLogCommand which is finally used by another Liquibase
instance to produce an SQL update file.
While this approach works, it a) feels a bit hacky-ish, b) the end result is not the same
as running the migration directly.
Anyway, what I've come up with is a custom implementation of JdbcExecutor which incorporates
a LoggingExecutor. The implementation looks as follows:
#LiquibaseService(skip = true)
public class LoggingJdbcExecutor extends JdbcExecutor {
private LoggingExecutor loggingExecutor;
public LoggingJdbcExecutor(Database database, Writer writer) {
loggingExecutor = new LoggingExecutor(this, writer, database);
setDatabase(database);
}
#Override
public void execute(SqlStatement sql, List<SqlVisitor> sqlVisitors) throws DatabaseException {
super.execute(sql, sqlVisitors);
loggingExecutor.execute(sql, sqlVisitors);
}
#Override
public int update(SqlStatement sql, List<SqlVisitor> sqlVisitors) throws DatabaseException {
final int result = super.update(sql, sqlVisitors);
loggingExecutor.update(sql, sqlVisitors);
return result;
}
#Override
public void comment(String message) throws DatabaseException {
super.comment(message);
loggingExecutor.comment(message);
}
}
This executor gets injected into Liquibase before update() is invoked as follows:
final String path = configuration.getUpdateSqlExportFile();
ExecutorService.getInstance().setExecutor(liquibase.getDatabase(), new LoggingJdbcExecutor(
liquibase.getDatabase(), new FileWriter(path)
));
Is this approach reasonable and future proof ? While it seems to work I'm not sure if maybe I'm
missing something and there's a better way.
Thanks
I want to ask this question and I tried to search for a while without concrete answers.
I have made a database and used LINQ2SQL to auto-generate the classes needed.
I have set the serialization mode to unidirectional to make sure the classes are being serialized and making the datamembers.
Now, what I want to know is, how I can send the references to the other classes (which has been made through LINQ2SQL).
F.x. I have a Class called Scheduler which is referencing Reservation, and Seat, because Reservation and Seat have foreign keys.
You can see the dbml here:
http://imgur.com/rR6OxDi
The dbml file. This is the model of our database
Also you can see that when I run the WCF test client it does not return the objects of Seats and Reservation.
http://imgur.com/brxNBz7
Hopefully you can all help.
UPDATE
Here is the snippet of the code provided by LINQ2SQL.
This is the fields for the scheduler
[global::System.Data.Linq.Mapping.TableAttribute(Name="dbo.Scheduler")]
[global::System.Runtime.Serialization.DataContractAttribute()]
public partial class Scheduler : INotifyPropertyChanging, INotifyPropertyChanged
{
private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
private int _SchID;
private System.Nullable<System.DateTime> _Date;
private System.Nullable<System.TimeSpan> _Starttime;
private System.Nullable<int> _MovieID;
private System.Nullable<int> _HallID;
private EntitySet<Seat> _Seats;
private EntitySet<Reservation> _Reservations;
private EntityRef<Hall> _Hall;
private EntityRef<Movie> _Movie;
private bool serializing;
And here is the snippet part of the code where it references to Reservation and Seat:
[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Scheduler_Seat", Storage="_Seats", ThisKey="SchID", OtherKey="SchedulerID")]
[global::System.Runtime.Serialization.DataMemberAttribute(Order=6, EmitDefaultValue=false)]
public EntitySet<Seat> Seats
{
get
{
if ((this.serializing
&& (this._Seats.HasLoadedOrAssignedValues == false)))
{
return null;
}
return this._Seats;
}
set
{
this._Seats.Assign(value);
}
}
[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Scheduler_Reservation", Storage="_Reservations", ThisKey="SchID", OtherKey="SchedulerID")]
[global::System.Runtime.Serialization.DataMemberAttribute(Order=7, EmitDefaultValue=false)]
public EntitySet<Reservation> Reservations
{
get
{
if ((this.serializing
&& (this._Reservations.HasLoadedOrAssignedValues == false)))
{
return null;
}
return this._Reservations;
}
set
{
this._Reservations.Assign(value);
}
}
Update 2
Here is the Reservation class which LINQ2SQL made:
Here is the fields:
[global::System.Data.Linq.Mapping.TableAttribute(Name="dbo.Reservation")]
[global::System.Runtime.Serialization.DataContractAttribute()]
public partial class Reservation : INotifyPropertyChanging, INotifyPropertyChanged
{
private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty);
private int _ResID;
private System.Nullable<int> _CustomerID;
private System.Nullable<int> _SchedulerID;
private string _Row;
private string _Seat;
private EntityRef<Customer> _Customer;
private EntityRef<Scheduler> _Scheduler;
And here is the Scheduler reference part of the class
[global::System.Data.Linq.Mapping.AssociationAttribute(Name="Scheduler_Reservation", Storage="_Scheduler", ThisKey="SchedulerID", OtherKey="SchID", IsForeignKey=true, DeleteRule="SET DEFAULT")]
public Scheduler Scheduler
{
get
{
return this._Scheduler.Entity;
}
set
{
Scheduler previousValue = this._Scheduler.Entity;
if (((previousValue != value)
|| (this._Scheduler.HasLoadedOrAssignedValue == false)))
{
this.SendPropertyChanging();
if ((previousValue != null))
{
this._Scheduler.Entity = null;
previousValue.Reservations.Remove(this);
}
this._Scheduler.Entity = value;
if ((value != null))
{
value.Reservations.Add(this);
this._SchedulerID = value.SchID;
}
else
{
this._SchedulerID = default(Nullable<int>);
}
this.SendPropertyChanged("Scheduler");
}
}
}
All of these things should lead to where I could get the object like this:
Scheduler[] schedulers = client.GetAllSchedulers();
Reservation reservation = schedulers[0].Reservations.First();
But get this error due to WCF not sending the object, (which you could see in picture one).
Which is this error:
Description: An unhandled exception occurred during the execution of
the current web request. Please review the stack trace for more
information about the error and where it originated in the code.
Exception Details: System.InvalidOperationException: Sequence contains
no elements
UPDATE 3:
Ok so it appears that it works somehow.
I just had to make a join between the Scheduler and Reservation.
Also whenever I debug the code I can see the variables are there. (Due to my reputation I can not post links).
But some of you might recognize the following whenever you try to view a result in debug mode:
"expanding the results view will enumerate the ienumerable c#"
Whenever I do this, it works, but not if I run it in release mode.
Looks like only object types (Reservation,Seat) have null values.
I'm guessing either you are missing DataContract/DataMember attributes in your complex types or you might need to include KnownTypeAttribute
It'd be easier to tell if you could provide some code.
EDIT
What your are talking about later is deferred loading. See this blog for more information on deferred vs immediate loading.
When you expand the IEnumerable in debug mode, that makes the request to retrieve/load the objects.
What your probably want is to load your Reservation,Seat objects along with your Scheduler object. Something like the following:
YourDatabaseContext database = new YourDatabaseContext ())
{
DataLoadOptions options = new DataLoadOptions();
options.LoadWith<Scheduler>(sch=> sch.Reservation);
options.LoadWith<Scheduler>(sch=> sch.Seat);
database.LoadOptions = options;
}
See DataLoadOptions for more details.
If you want to understand deferred execution. See this article for more details.
Quote from the article:
By default LINQ uses deferred query execution. This means when you write a LINQ query it doesn't execute. LINQ queries execute when you 'touch' the query results. This means you can change the underlying collection and run the same query subsequent times in the same scope. Touching the data means accessing the results, for instance in a for loop or by using an aggregate operator like Average or AsParallel on the results.
So I have an application with a ton of migrations made by Entity framework.
We want to get a script for all the migrations at once and using the -Script tag does work fine.
However...it does not add GO statements in the SQL giving us problems like Alter view should be the first statement in a batch file...
I have been searching around and manually adding Sql("GO"); help with this problem but only for the entire script. When I use the package console manager again it returns an exception.
System.Data.SqlClient.SqlException (0x80131904): Could not find stored procedure 'GO'.
Is there a way to add these GO tags only when using the -Script tag?
If not, what is a good approach for this?
Note: we have also tried having multiple files but since we have so many migrations, this is near impossible to maintain every time.
If you are trying to alter your view using Sql("Alter View dbo.Foos As etc"), then you can avoid the should be the first statement in a batch file error without adding GO statements by putting the sql inside an EXEC command:
Sql("EXEC('Alter View dbo.Foos As etc')")
In order to change the SQL Generated by entity framework migrations you can create a new SqlServerMigrationSqlGenerator
We have done this to add a GO statement before and after the migration history:
public class MigrationScriptBuilder: SqlServerMigrationSqlGenerator
{
protected override void Generate(System.Data.Entity.Migrations.Model.InsertHistoryOperation insertHistoryOperation)
{
Statement("GO");
base.Generate(insertHistoryOperation);
Statement("GO");
}
}
then add in the Configuration constructor (in the Migrations folder of the project where you DbContext is) so that it uses this new sql generator:
[...]
internal sealed class Configuration : DbMigrationsConfiguration<PMA.Dal.PmaContext>
{
public Configuration()
{
SetSqlGenerator("System.Data.SqlClient", new MigrationScriptBuilder());
AutomaticMigrationsEnabled = false;
}
[...]
So now when you generate a script using the -Script tag, you can see that the insert into [__MigrationHistory] is surrounded by GO
Alternatively in your implementation of SqlServerMigrationSqlGenerator you can override any part of the script generation, the InsertHistoryOperation was suitable for us.
Turn out the concept exist deep in the SqlServerMigrationSqlGenerator as an optional argument for Statement(sql, batchTerminator). Here is something based on Skyp idea. It works both in -script mode or not. The GOs are for different operations than for Skyp only because our needs are a little different. You then need to register this class in the Configuration as per Skyp instructions.
public class MigrationScriptBuilder : SqlServerMigrationSqlGenerator
{
private string Marker = Guid.NewGuid().ToString(); //To cheat on the check null or empty of the base generator
protected override void Generate(AlterProcedureOperation alterProcedureOperation)
{
SqlGo();
base.Generate(alterProcedureOperation);
SqlGo();
}
protected override void Generate(CreateProcedureOperation createProcedureOperation)
{
SqlGo();
base.Generate(createProcedureOperation);
SqlGo();
}
protected override void Generate(SqlOperation sqlOperation)
{
SqlGo();
base.Generate(sqlOperation);
}
private void SqlGo()
{
Statement(Marker, batchTerminator: "GO");
}
public override IEnumerable<MigrationStatement> Generate(IEnumerable<MigrationOperation> migrationOperations, string providerManifestToken)
{
var result = new List<MigrationStatement>();
var statements = base.Generate(migrationOperations, providerManifestToken);
bool pendingBatchTerminator = false;
foreach (var item in statements)
{
if(item.Sql == Marker && item.BatchTerminator == "GO")
{
pendingBatchTerminator = true;
}
else
{
if(pendingBatchTerminator)
{
item.BatchTerminator = "GO";
pendingBatchTerminator = false;
}
result.Add(item);
}
}
return result;
}
}
The easiest way is to add /**/ before the GO statement.
Just replace the current statement with a .Replace("GO", "");
In HBase database I want to create a secondary index by using additional "linking" table. I have followed the example given in this answer: Create secondary index using coprocesor HBase
I am not very well familiar with the entire concept of HBase, and I had read some examples on the issue of creating secondary indexes. I am attaching the coprocessor to single table only, like this:
disable 'Entry2'
alter 'Entry2', METHOD => 'table_att', 'COPROCESSOR' => '/home/user/hbase/rootdir/hcoprocessors.jar|com.acme.hobservers.EntryParentIndex||'
enable 'Entry2'
The source code of it, is as follows:
public class EntryParentIndex extends BaseRegionObserver{
private static final Log LOG = LogFactory.getLog(CoprocessorHost.class);
private HTablePool pool = null;
private final static String INDEX_TABLE = "EntryParentIndex";
private final static String SOURCE_TABLE = "Entry2";
#Override
public void start(CoprocessorEnvironment env) throws IOException {
pool = new HTablePool(env.getConfiguration(), 10);
}
#Override
public void prePut(
final ObserverContext<RegionCoprocessorEnvironment> observerContext,
final Put put,
final WALEdit edit,
final boolean writeToWAL)
throws IOException {
try {
final List<KeyValue> filteredList = put.get(Bytes.toBytes ("data"),Bytes.toBytes("parentId"));
byte [] id = put.getRow(); //Get the Entry ID
KeyValue kv=filteredList.get( 0 ); //get Entry PARENT_ID
byte[] parentId=kv.getValue();
HTableInterface htbl = pool.getTable(Bytes.toBytes(INDEX_TABLE));
//create row key for the index table
byte[] p1=concatTwoByteArrays(parentId, ":".getBytes()); //Insert a semicolon between two UUIDs
byte[] rowkey=concatTwoByteArrays(p1, id);
Put indexput = new Put(rowkey);
//The following call is setting up a strange? recursion, resulting
//...in thesame prePut method invoken again and again. Interestingly
//...the recursion limits itself up to 6 times. The actual row does not
//...get inserted into the INDEX_TABLE
htbl.put(indexput);
htbl.close();
}
catch ( IllegalArgumentException ex) { }
}
#Override
public void stop(CoprocessorEnvironment env) throws IOException {
pool.close();
}
public static final byte[] concatTwoByteArrays(byte[] first, byte[] second) {
byte[] result = Arrays.copyOf(first, first.length + second.length);
System.arraycopy(second, 0, result, first.length, second.length);
return result;
}
}
This executes when I perform put on the SOURCE_TABLE.
There is a comment in the code (please seek it): "The following call is setting up a strange".
I set a debugging print in the log confirming that the prePut method is being executed only on the SOURCE_TABLE, and never on the INDEX_TABLE. Yet I don't understand why this strange recursion is happening despite in the coprocessor I only execute one put on the INDEX_TABLE.
I have also confirmed that the put action on the source table is again only one.
I have fixed my problem. It came out to be that I was adding multiple times the same observer mistakenly thinking that it is getting lost after Hbase restart.
Also the reason why the .put call to the INDEX_TABLE was not working is because I did not set a value to it, but only a rowkey, mistakenly thinking that this is possible. Yet HBase did not throw any excepiton whatsoever, just did not perform the PUT, no info given, which may be confusing for newcommers to this technology.
I'm starting to develop a new asp.net application based on subsonic3 (for queries) and log4net (for logs) and would like to know how to interface subsonic3 with log4net so that log4net logs the underlying sql used by subsonic.
This is what I have so far:
public static IEnumerable<arma_ocorrencium> ListArmasOcorrencia()
{
if (logger.IsInfoEnabled)
{
logger.Info("ListarArmasOcorrencia: start");
}
var db = new BdvdDB();
var select = from p in db.arma_ocorrencia
select p;
var results = select.ToList<arma_ocorrencium>(); //Execute the query here
if (logger.IsInfoEnabled)
{
// log sql here
}
if (logger.IsInfoEnabled)
{
logger.Info("ListarArmasOcorrencia: end");
}
return results;
}
You can use the Log property of the Provider class:
_db.Provider.Log = Console.Out;
will log your SQL statements to the console. If you want to use log4net or something similar you will have to write a small mediator class that implements TextWriter and redirects all received input to log4net.
You can get the generated sql like this:
string sql = select.GetQueryText();
Make sure you are using the version 3.0.0.4 or above.
Cheers