Me again!
I have a question re. inserting into a sql table using LINQ. I am essentially creating a feedback form whereby a user enters there details, hits submit and their data is inserted into a sql table.
I haven't got very far!!!
protected void btnSubmitFeedback_Click(object sender, EventArgs e)
{
ORFeedDataClassesDataContext db = new ORFeedDataClassesDataContext();
tblOnlineReportingFeedback newtblonlineReportingFeedback = new tblOnlineReportingFeedback();
newtblonlineReportingFeedback.Name = "txtbxFdName";
db.tblOnlineReportingFeedbacks.InsertOnSubmit(newtblonlineReportingFeedback);
db.SubmitChanges();
}
I have set up the sql table so that an unique autonumber is inserted each time a row is inserted. However, when I run the above, I receive the following:
"SqlException was handled by user code - String or binary data would be truncated. The statement has been truncated"
Can anyone think of a work around for this? Chances ar emy code is a load of poo anyway so if someone can correct me, I would be most grateful.
The error you're getting is caused by your Name column in the tblOnlineReportFeedback table.
The length of the column is not long enough to hold the data.
try changing the column to at least VARCHAR(11)
Also, i'm not sure if you're meaning to put txtbxFdName as the string to insert. It sounds to be it should be getting it from a textbox named "txtbxFdName"
newtblonlineReportingFeedback.Name = txtbxFdName.Text;
The error message states that the field Name in the database is too small to contain the value you're trying to insert. Try redesigning the table to fit more characters
I would check the lengths of your column in your table ... looks like you are trying to put in something a wee bit too big and hence the truncation.
Kindness,
Dan
What a tool!!! I got that excited about LINQ I forgot to make sure the table was set up correctly.
Maybe its time to step away from the computer for a while!
Hang on a second, I have run it, the error reported above is sorted but I the name returned is "txtbxFdName" as opposed to the value within that particular textbox.
Just if anyone is looking in at this, the correct code should look like this:
protected void btnSubmitFeedback_Click(object sender, EventArgs e)
{
ORFeedDataClassesDataContext db = new ORFeedDataClassesDataContext();
tblOnlineReportingFeedback newtblonlineReportingFeedback = new tblOnlineReportingFeedback();
newtblonlineReportingFeedback.Name = txtbxFdName.Text;
db.tblOnlineReportingFeedbacks.InsertOnSubmit(newtblonlineReportingFeedback);
db.SubmitChanges();
}
Thanks for a fresh pair of eyes all.
You may want to take a look at not only your column sizes in the actual table, but in the properties of the objects in the .dbml view too, make sure all the datatypes in this pane match up to those in the actual table.
You should also consider putting this code into a using statement as its good practise to minimise memory problems =]
using(ORFeedDataClassesDataContext db = new ORFeedDataClassesDataContext())
{
tblOnlineReportingFeedback newtblonlineReportingFeedback = new tblOnlineReportingFeedback
newtblonlineReportingFeedback.Name = txtbxFdName.Text;
db.tblOnlineReportingFeedbacks.InsertOnSubmit(newtblonlineReportingFeedback);
db.SubmitChanges();
}
Related
I am using Linq-to-SQL for accessing SQL Server 2008.
In database I have UNIQUE index on some column.
When I enter duplicated data in that column, LINQ will throw SqlException with an error message. Is there a way to figure out that exception is related to UNIQUE index and not some other SQL error?
Solution I am aware of is to user RegEx to parse exception message, but I would like to know is there more elegant approach?
You could use the SQLException.Number property.
For more details you could iterate the Errors collection each of which have thier own number.
As an aside, in the past I have written my own custom serialiser for SqlException but it appears that in 4.5 ToString() has been overridden so this is not now necessary.
You could check the table having the unique constraint on whether it already has a row with some value in the constrained column:
if (linqDataContext.ConstrainedTable.Any(row => row.ConstrainedColumn == somevalue))
{
//show a message, saying you've already got this value
//and it is not applicable
}
else
{
//accept your changes with smth like this:
linqDataContext.SubmitChanges();
}
Another question, whether this approach is applicable for you, i.e. whether the cost of this query overwhelms the cost of catching an exception and processing it. If the constrained column is indexed, than a simple request on that column must not be that costy.
I have a block based on a table. If I enter "12345" in enter query mode, it creates a query with
WHERE my_field = '12345'
If I enter "12345A", it goes
WHERE (upper(my_field) = '12345A' AND my_field like '12%')
which is bad, because my_field is indexed normally (not on upper(my_field)). I have tried toggling "Case restriction" attribute between mixed and upper, and "Case insensitive query" between yes and no, nothing seems to help. I also have a block level PRE-QUERY trigger (trigger starts with a RETURN; statement) set on override, so nothing should mess with the formation of the query, yet it still messes up.
Any ideas on what else I could try?
EDIT:
There was an obscure function call within WHEN_NEW_FORM_INSTANCE trigger to some attached library that reset all trigger block's items to CASE_SENSITIVE_QUERY = TRUE. Never would have guessed.
Not sure how the query is getting changed to that form;
WHERE (upper(my_field) = '12345A' AND my_field like '12%'
First check that there are no enter query or prequery triggers in the form. Somebody might have attached a trigger at a higher level. Oracle is not that smart to rewrite the query.Check that you are tying to a table not a view or stored procedure,...
If all else fails, enable the query triggers in the data black and rewrite the where clause yourself. It is pretty straightforward.
Give version of oracle forms before you post.
The
my_field like '12%'
Uses the index. The subset is then filtered with
upper(my_field) = '12345A'
So it might not be as bad as you think....
The most naive question, Can you update the column so it's all uppercase? I mean would it cause some inconvenience to your app?
If you can, it could be handled with a database trigger to ensure it's allways uppercase.
If you can't, then I suggest you create another field that you keep updated to uppercase with a database trigger.
You can also create a function index so it's upper(my_field).
We have a database with some fields that are varchar(max) which could contain lots of text however I have a situation where I only want to select the first for example 300 characters from the field for a paginated table of results on a MVC web site for a "preview" of the field.
for a simplified example query where I want to get all locations to display in the table
(this would be paginated, so I don't just get everything - I get maybe 10 results at a time):
return db.locations;
However this gives me a location object with all the fields containing the massive amounts of text which is very time consuming to execute.
So what I resorted to before was using SQL stored procedures with the:
LEFT(field, 300)
to resolve this issue and then in the Linq to SQL .dbml file included the stored procedure to return a "location" object for the result.
However I have many queries and I don't want to have to do this for every query.
This maybe a simple solution, but I am not sure how I can phrase this on a search engine, I would appreciate anyone who can help me with this problem.
You can use functions that directly translate to those functions too, this is useful when you need to translate code that functionally works just fine in SQL at no risk in LINQ.
Have a look at System.Data.Objects.EntityFunctions
Locations.Select(loc=>System.Data.Objects.EntityFunctions.Left(loc.Field,300))
This will get directly translated into a LEFT on the server side.
EDIT: I misread LEFT for LTRIM. Here's all the String functions that can't be used in LINQ to SQL. Have you tried String.Substring()?
Your best option is to map the stored procedure and continue using it. Here is an excellent article with screen shots showing you how to do so.
If you're not using the designer tool you can also call ExecuteCommand against the DataContext. It isn't pretty, but it's what we have for now.
I found something like this worked for me:
return from locationPart in db.locations
select new LocationPart
{
Description = locationPart.description,
Text = locationPart.text.Substring(0,300)
};
Not ideal because I have to use "select new" to return a a different object, but it seems to work.
I have a LINQ query mapped with the Entity Framework that looks something like this:
image = this.Context.ImageSet
.Where(n => n.ImageId == imageId)
.Where(n => n.Albums.IsPublic == true)
.Single();
This returns a single image object and works as intended.
However, this query returns all the properties of my Image table in the DB.
Under normal circumstances, this would be fine but these images contain a lot of binary data that takes a very long time to return.
Basically, in it current state my linq query is doing:
Select ImageId, Name, Data
From Images
...
But I need a query that does this instread:
Select ImageId, Name
From Images
...
Notice i want to load everything except the Data. (I can get this data on a second async pass)
Unfortunately, if using LINQ to SQL, there is no optimal solution.
You have 3 options:
You return the Entity, with Context tracking and all, in this case Image, with all fields
You choose your fields and return an anonymous type
You choose your fields and return a strongly typed custom class, but you lose tracking, if thats what you want.
I love LINQ to SQL, but thats the way it is.
My only solution for you would be to restructure your DataBase, and move all the large Data into a separate table, and link to it from the Image table.
This way when returning Image you'd only return a key in the new DataID field, and then you could access that heavier Data when and if you needed it.
cheers
This will create a new image with only those fields set. When you go back to get the Data for the images you select, I'd suggest going ahead and getting the full dataset instead of trying to merge it with the existing id/name data. The id/name fields are presumably small relative to the data and the code will be much simpler than trying to do the merge. Also, it may not be necessary to actually construct an Image object, using an anonymous type might suit your purposes just as well.
image = this.Context.ImageSet
.Where(n => n.ImageId == imageId)
.Where(n => n.Albums.IsPublic == true)
.Select( n => new Image { ImageId = n.ImageId, Name = n.Name }
.Single();
[If using Linq 2 SQL] Within the DBML designer, there is an option to make individual table columns delay-loaded. Set this to true for your large binary field. Then, that data is not loaded until it is actually used.
[Question for you all: Does anyone know if the entity frameworks support delayed loaded varbinary/varchar's in MSVS 2010? ]
Solution #2 (for entity framework or linq 2 sql):
Create a view of the table that includes only the primary key and the varchar(max)/varbinary(max). Map that into EF.
Within your Entity Framework designer, delete the varbinary(max)/varchar(max) property from the table definition (leaving it defined only in the view). This should exclude the field from read/write operations to that table, though you might verify that with the logger.
Generally you'll access the data through the table that excludes the data blob. When you need the blob, you load a row from the view. I'm not sure if you'll be able to write to the view, I'm not sure how you would do writes. You may be able to write to the view, or you may need to write a stored procedure, or you can bust out a DBML file for the one table.
You cannot do it with LINQ at least for now...
The best approach I know is create View for the table you need without large fields and use LINQ with that View.
Alternatively you could use the select new in the query expression...
var image =
(
from i in db.ImageSet
where i.ImageId == imageId && i.Albums.IsPublic
select new
{
ImageId = i.ImageId,
Name = i.Name
}
).Single()
The LINQ query expressions actually get converted to the Lambda expression at compile time, but I prefair using the query expression generally because i find it more readable and understandable.
Thanks :)
I started playing around with Linq today and ran into a problem I couldn't find an answer to. I was querying a simple SQL Server database that had some employee records. One of the fields is the full name (cn). I thought it would be interesting to group by the first name by splitting the full name at the first space. I tried
group by person.cn.Split(separators)[0]
but ran into a lengthy runtime exception (looked a lot like a C++ template instantiation error).
Then I tried grouping by a few letters of the first name:
group by person.cn.Substring(0,5)
and that worked fine but is not what I want.
I'm wondering about two things:
Why does the first example not work when it looks so close to the second?
Knowing that behind the scenes it's SQL stuff going on, what's a good way to do this kind of thing efficiently
Thanks,
Andrew
Split has no translation into SQL.
So, how to do this string manipulation without split? Cheat like hell (untested):
string oneSpace = " ";
string fiftySpace = " ";
var query =
from person in db.Persons
let lastname = person.cn.Replace(oneSpace, fiftySpace).SubString(0, 50).Trim()
group person by lastname into g
select new { Key = g.Key, Count = g.Count };
The reason your first attempt didn't work is because LINQ to SQL uses Expression Trees to translate your query into SQL. As a result any code that isn't directly translatable into SQL is an exception - this includes the call to Split.
Thanks guys, I'll try the "Replace" trick to see if that runs. I'm very intrigued by LINQ but now it looks like there's some hidden mysteriousness where you have to know what your LINQ queries translate into before being able to use it effectively.
The core problem is of course that I don't know SQL very well so that's where I'll start.
Edit:
I finally tried the "Replace" today and it works. I even got to sort the grouped results by count so now I have a pareto of name in my company. It's horrendously slow, though. Much faster to select everything and do the bucketing in C# directly.
Thanks again,
Andrew