How do I retrieve Insert Statements without using SubmitChanges in LINQPad - linq-to-sql

I have something similar to the code below in LINQPAD using C# Statements. My goal is to get the actual SQL Insert statments not actually update the database.
I can easily delete the data after it inserts with this small sample, but I will need this for a larger push of data. I hope that I have missed something simple either in L2S or LINQPad.
Is there an easier way retrieve the SQL Insert?
var e1 = new MyEntity(){ Text = "First" };
var e2 = new MyEntity(){ Text = "Second" };
MyEntities.InsertOnSubmit(e1);
MyEntities.InsertOnSubmit(e2);
SubmitChanges();

A quick-n-dirty way is to wrap everything in a transaction scope that is never commited:
using(TransactionScope ts = new TransactionScope())
{
var e1 = new MyEntity(){ Text = "First" };
var e2 = new MyEntity(){ Text = "Second" };
MyEntities.InsertOnSubmit(e1);
MyEntities.InsertOnSubmit(e2);
SubmitChanges();
// Deliberately not committing the transaction.
}
This works well for small volumes. If the data volume is large and you have full recovery model on the database the transaction log growth might become a problem.

When we did the samples for "LINQ in Action", we used the following method which gets the scheduled changes from the context:
public String GetChangeText(System.Data.Linq.DataContext context)
{
MethodInfo mi = typeof(DataContext).GetMethod("GetChangeText",
BindingFlags.NonPublic | BindingFlags.Instance);
return mi.Invoke(context, null).ToString();
}
If you want to see this in action, download the samples in LINQPad (see http://www.thinqlinq.com/Default/LINQ-In-Action-Samples-available-in-LINQPad.aspx) and check out chapter 6 example 6.29.

Related

Get Row Count for Data Transferred using EzAPI SSIS

I am transferring some data from one table to another using SSIS with EzAPI. How can I get the number of rows that were transferred?
My setup is as follows
EzPackage package = new EzPackage();
EzOleDbConnectionManager srcConn;
EzOleDbSource src;
EzOleDbConnectionManager destConn;
EzOleDbDestination dest;
EzDataFlow dataFlow;
destConn = new EzOleDbConnectionManager(package); //set connection string
srcConn = new EzOleDbConnectionManager(package);
dataFlow = new EzDataFlow(package);
src = Activator.CreateInstance(typeof(EzOleDbSource), new object[] { dataFlow }) as EzOleDbSource;
src.Connection = srcConn;
src.SqlCommand = odbcImport.Query;
dest = Activator.CreateInstance(typeof(EzOleDbDestination), new object[] { dataFlow }) as EzOleDbDestination;
dest.Connection = destConn;
dest.AttachTo(src, 0, 0);
dest.AccessMode = AccessMode.AM_OPENROWSET_FASTLOAD;
DTSExecResult result = package.Execute();
Where in this can I add something to get the number of rows? For all versions of SQL server 2008r2 and up
The quick answer is that the Row Count Transformation isn't included out of the box. I had a brief post about that: Row Count with EzAPI
I downloaded the source project from CodePlex and then edited EzComponents.cs (in EzAPI\src) and added the following code
[CompID("{150E6007-7C6A-4CC3-8FF3-FC73783A972E}")]
public class EzRowCountTransform : EzComponent
{
public EzRowCountTransform(EzDataFlow dataFlow) : base(dataFlow) { }
public EzRowCountTransform(EzDataFlow parent, IDTSComponentMetaData100 meta) : base(parent, meta) { }
public string VariableName
{
get { return (string)Meta.CustomPropertyCollection["VariableName"].Value; }
set { Comp.SetComponentProperty("VariableName", value); }
}
}
The component id above is only for 2008.
For 2012, it's going to be E26997D8C-70DA-42B2-8208-A19CE3A9FE41 I don't have a 2012 installation at the moment to confirm I didn't transpose a value there but drop a Row Count component onto a data flow, right click and look at the properties. The component/class id is what that value needs to be. Similar story if you're dealing with 2005.
So, once you have the ability to use EzRowCountTransform, you can simply patch it into your existing script.
// Create an instance of our transform
EzRowCountTransform newRC = null;
// Create a variable to use it
Variable newRCVariable = null;
newRCVariable = package.Variables.Add("RowCountNew", false, "User", 0);
// ...
src.SqlCommand = odbcImport.Query;
// New code here too
newRC = new EzRowCountTransform(dataFlow);
newRC.AttachTo(src);
newRC.Name = "RC New Rows";
newRC.VariableName = newRCVariable.QualifiedName;
// Continue old code
I have a presentation on various approaches I've used over time and what I like/don't like about them. Type more, click less: a programmatic approach to building SSIS. It contains sample code for creating the EzRowCountTransform and usage.

LINQ to SQL, in memory lists and anonymous types

I am joining a table to an in memory list and wanting to create an anonymous type from the results. I have joined the 2 data sources together ok, but I'm not sure of the syntax required to use a property from the in memory list in the anonymous type.
Here's my code
public DataKeys(IEnumerable<Element> elements)
{
var defsource = new DefinitionSource();
var items = from def in defsource.Definitions
where elements.Select(el=> el.Value).Contains(def.Name)
select new { def.Key };
...
}
(Obviously "elements" is an in-memory list of element and "DefinitionSource" is a wrapper around a table.) This works fine, but as you can see there is no property from the in-memory list of elements. I've tried this
var items = from def in defsource.Definitions
where elements.Select(el=> el.Value).Contains(def.Name)
from el in elements
where el.Value.Equals(def.Name)
select new { el.NodeType, def.Key };
but at run time it gens a "{"Local sequence cannot be used in LINQ to SQL implementations of query operators except the Contains operator."}"
So what is the syntax I need to do this?
Many thx IA
Simon
This might help:
var def=defsource.Definitions
.Where(a=>elements.Select(el=>el.Value).Contains(a.Name))
.ToList();
var items = from d in def
from el in elements
.Where(a=>a.Value==d.Name)
select new { el.NodeType, d.Key };
To query over two data sources both of them needs to be in memory are both of them should be in database. you can do the following to achieve the results you want
var query = (from def in defsource.Definitions
where elements.Select(el=> el.Value).Contains(def.Name)
).ToList();//bring filtered result in memory
var Items = from def in query //Definitions are already filtered no need to re-apply where
from el in elements.Where(a=>a.Value == def.Name)
select new {el.NodeType, def.Key};

AdvancedDataGrid total sum of branch nodes

Introduction:
I have an AdvancedDataGrid displaying hierarchical data illustrated by the image below:
The branch nodes "Prosjekt" and "Tiltak" display the sum of the leaf nodes below.
Problem: I want the root node "Tavle" to display the total sum of the branch nodes below. When i attempted to do this by adding the same SummaryRow the sum of the root node was not calculcated correctly(Every node's sum was calculated twice).
dg_Teknikktavles = new AutoSizingAdvancedDataGrid();
dg_Teknikktavles.sortExpertMode="true";
dg_Teknikktavles.headerHeight = 50;
dg_Teknikktavles.variableRowHeight = true;
dg_Teknikktavles.addEventListener(ListEvent.ITEM_CLICK,dg_TeknikktavlesItemClicked);
dg_Teknikktavles.editable="false";
dg_Teknikktavles.percentWidth=100;
dg_Teknikktavles.minColumnWidth =0.8;
dg_Teknikktavles.height = 1000;
var sumFieldArray:Array = new Array(context.brukerList.length);
for(var i:int = 0; i < context.brukerList.length; i++)
{
var sumField:SummaryField2 = new SummaryField2();
sumField.dataField = Ressurstavle.ressursKey + i;
sumField.summaryOperation = "SUM";
sumFieldArray[i] = sumField;
}
var summaryRow:SummaryRow = new SummaryRow();
summaryRow.summaryPlacement = "group";
summaryRow.fields = sumFieldArray;
var summaryRow2:SummaryRow = new SummaryRow();
summaryRow2.summaryPlacement = "group";
summaryRow2.fields = sumFieldArray;
var groupField1:GroupingField = new GroupingField();
groupField1.name = "tavle";
//groupField1.summaries = [summaryRow2];
var groupField2:GroupingField = new GroupingField();
groupField2.name = "kategori";
groupField2.summaries = [summaryRow];
var group:Grouping = new Grouping();
group.fields = [groupField1, groupField2];
var groupCol:GroupingCollection2 = new GroupingCollection2();
groupCol.source = ressursTavle;
groupCol.grouping = group;
groupCol.refresh();
Main Question: How do i get my AdvancedDataGrid's (dg_Teknikktavles) root node "Tavle" to correctly display the sum of the two branch nodes below?
Side Question: How do i add a red color to the numbers of the root node's summary row that exceed 5? E.g the column displaying 8 will exceed 5 in the root node's summary row, and should therefore be marked red
Thanks in advance!
This is a general answer, without code examples, but I had to do the same just couple of days ago, so my memory is still fresh :) Here's what I did:
Created a class A to represent an item renderer data, extended it from Proxy (I had field names defined at run time), and let it contain a collection of values as it's data member. Once accessed through flash_proxy::getPropery(fieldName) it would find a corresponding value in the data member containing the values and return it. Special note: implement IUID, just do it, it'll save you couple of days of frustration.
Extended A in B, added a children property containing ArrayCollection of A (don't try to experiment with other collection types, unless you want to find yourself examining tons of framework code, trust me, it's ugly and is impossible to identify as interesting). Let B override flash_proxy::getPropery - depending of your compiler this may, or may not be possible, if not possible - call some function from A.flash_proxy::getPropery() that you can override in B. Let this function query every instance of A, which is a child of B, asking the same thing, as DataGrid itself would, when building item renderers - this way you would get the total.
When creating a data provider. Create an ArrayCollection of B (again, don't try to experiment with other collections--unless you are ready for lots of frustration). Create Hierarchical data that uses this array collection as a source.
Colors - that's what you use item renderers for, just look up any tutorial on using item renderers, that must be pretty basic.
In case someone else has the same problem:
The initial problem that everything was summed twice, was the result of using the same Array of SummaryField2 (sumFieldArray in the code) for both grouping fields(GropingField2 tavle and kategori)
The Solution to the main question: was to create a new array of summaryfields for the root node(in my intial for loop):
//Summary fields for root node
var sumFieldRoot:SummaryField2 = new SummaryField2();
sumFieldRoot.dataField = Ressurstavle.ressursKey + i;
sumFieldRoot.summaryOperation = "SUM";
sumFieldArrayRoot[i] = sumFieldRoot;
Answer to the side question:
This was pretty much as easy as pointed out by wvxyw. Code for this solution below:
private function summary_styleFunction(data:Object, col:AdvancedDataGridColumn):Object
{
var output:Object;
var field:String = col.dataField;
if ( data.children != null )
{
if(data[field] >5){
output = {color:0xFF0000, fontWeight:"bold"}
}
else {
output = {color:0x006633, fontWeight:"bold"}
}
//output = {color:0x081EA6, fontWeight:"bold", fontSize:14}
}
return output;
}

How to write a expression for a linq to sql property?

My appologies upfront for the lengthy question. I made quite an effort to make my question as clear as possible in one go. Please bear with me. ;o) any help will be greatly appreciated!
I have the classes Branch and Text:
class Branch
int ID
Text WebDescription
and a bunch of other properties
class Text
int ID
string UK
string NL
string FR
string IT
and a bunch of other properties as well
I want to only display the ID of the branch and its description in the appropriate language. I want only one query (no extra round trips) which retrieves only two fields (not the whole object).
I found three solutions
Via the object model in the query
// good: no round trips
// good: clean sql
// bad: impossible to use the currentUserLanguage parameter
var lang = "NL";
var dbProject = new ProjectDataContext();
var query = from b in dbProject.GetTable<Branch>()
select new
{
b.ID,
WebDescription = b.WebDescriptionObject.NL // <-- problem
};
var text = query.First().WebDescription;
Via the object model after the query
// good: no round trips (eager loading of text object)
// good: possible to use the currentUserLanguage parameter
// bad: loads the *whole* branch and text object, not just two fields
var lang= "NL";
var dbProject = new ProjectDataContext();
var query = from b in dbProject.GetTable<Branch>()
select new
{
b.ID,
WebDescription = b.GetWebDescriptionAsString(lang)
};
var text = query.First().WebDescription;
Using an expression
// good: I have the feeling I am on the right track
// bad: This doesn't work :o( throws an exception
var lang= "NL";
var dbProject = new ProjectDataContext();
var query = from b in dbProject.GetTable<Branch>()
select new
{
b.ID,
WebDescription = b.GetWebDescriptionAsExpression(lang)
};
var text = query.First().WebDescription;
Here is code for the two methods GetWebDescriptionAsString and GetWebDescriptionAsExpression.
public string GetWebDescriptionAsString(string lang)
{
if (lang== "NL") return WebDescriptionObject.NL;
if (lang== "FR") return WebDescriptionObject.FR;
if (lang== "IT") return WebDescriptionObject.IT;
return WebDescriptionObject.UK;
}
public Expression<Func<Branch, string>> GetWebDescriptionAsExpression(string lang)
{
if (lang== "NL") return b => b.WebDescriptionObject.NL;
if (lang== "FR") return b => b.WebDescriptionObject.FR;
if (lang== "IT") return b => b.WebDescriptionObject.IT;
return b => b.WebDescriptionObject.UK;
}
Without really answering the question, the cleanest approach would be to change the Text structure into a more normalized form like:
Text
ID
TextTranslation
ID
TextID
Lang
TextValue
where each text has a number of translations, one for each language.
The query would become something like:
var q =
from branch in dbProject.Branches
join text in dbProject.Texts on branch.TextID = text.ID
join translation in dbProject.TextTranslations on text.ID = translation.TextID
where translation.Lang == lang
select new
{
branch.ID,
WebDescription = translation.TextValue
};
This approach has other advantages as well, for example adding a new language will not change the model structure.
This would be very easy to do if you used a stored procedure. Are you opposed to using SP's as a solution?
If a stored procedure works, then I am happy to use it.
Thank you for you prompt reply.
I made a quick attempt. The UDF was already there, I just didn't know how to use it. The performance dropped significantly. The first solution is 3 times faster. In my understanding, this approach would require extra round trips to the database. Is that correct?
var query = from b in dbProject.GetTable<Branch>()
select new
{
b.ID,
WebDescription = db.fGetText(b.WebDescriptionID, (currentUserLanguage))
};
Without understanding your whole problem
create a stored procedure like this:
CREATE PROCEDURE spGetTheTextINeed #Language char(2), #BranchID int
AS
/* I don't know how your database is structured so you need to write this */
SELECT MyText from MyTable WHERE Language=#Language and Branch=#BranchID
Then you need to add the sp to your DBML and then you can just call the sp you need with the appropriate parameters:
var query = myDataContext.spGetTheTextINeed("NL",[your branch number])
Dim str As String
str = query.MyText
The code above is not to be exact - I don't understand your full requirements but this should get you started.

In LINQ to SQL how can I make it so I can add items to a collection of an entity without saving the entity first?

var e1 = new E1();
e1.e2s.Add(new e2()); //e2s is null until e1 is saved, i want to save them all at the same time
context.e1s.imsertonsubmit(e1);
context.submitchanges();
The sub items will be saved along with the main item, and even identities will be set properly, if you give your DataClasses an association between these classes.
You do this by adding LoadOptions to your O/R-Designer DataClasses like this:
MyDataContext mydc = new MyDataContext();
System.Data.Linq.DataLoadOptions lo = new System.Data.Linq.DataLoadOptions();
lo.LoadWith<E1>(p => p.e2s);
mydc.LoadOptions = lo;
This way LINQ will take care of adding the sub-items, you don't need to InsertOnSubmit every one by itself.
A side effect: upon loading the item, the subitems will be retrieved, too.
Well - I don't know if your initial code block would work, but I'm guessing you have to mark your new e2 as insert on submit. Thus:
var e1 = new E1();
var e2 = new e2();
e1.e2s.Add(e2); //e2s is null until e1 is saved, i want to save them all at the same time
context.e1s.insertonsubmit(e1);
context.e2s.insertonsubmit(e2);
context.submitchanges();
there we go, apparently when you create another ctor, you have to actually call the no arg ctor in order for the stuff in the ctor to happen