Add custom field to linq to sql auto generated entity - c#

I have a table with a varbinary(max) column for an image. I have dropped the table on the LinqToSql designer and have set "Delay load" to true, since I don't want to load the actual image data.
Is it possible to just know if the column is null or not, without getting the actual data and still only doing one query from the database?
I would also like to use the automated entity created by Linq.
Something like a new bool HasImage {get;} property would be just what I'm looking for.

The only way for Linq to SQL to "automatically" know whether or not the column has a value is to actually ask the database for it. You can extend the partial class with fields/properties, but that's not going to eliminate the lookup.
One of the things you could do is created a computed column (assuming SQL 2005+ here, otherwise you'll have to try to adapt this to your DBMS). If your table looks like this, for example:
CREATE TABLE Foo
(
FooID int NOT NULL IDENTITY(1, 1) PRIMARY KEY,
FooName varchar(50) NOT NULL,
FooImage varbinary(max) NULL
)
You would add the computed column this way:
ALTER TABLE Foo
ADD FooHasImage AS CASE
WHEN FooImage IS NULL THEN 0
ELSE 1
END
Then you can add the FooHasImage column to your Linq to SQL entity, don't delay load it, and check that property instead of explicitly checking the FooImage property.
Also, I feel obligated to point out that storing images in a database this way is sub-optimal. It may be necessary, I don't know much about your environment, but if you're using SQL Server 2008 then consider using FILESTREAM instead, as it will use the file system for cheap "offline" BLOB storage instead of stuffing the entire thing in the database.

Create a partial class
public partial class MyTableObject
{
public bool HasImage { get { return MyColumn.HasValue; } }
}
this will probably trigger a database hit, though
I would suggest adding a new column to the database "HasImage" bit that you set when an image is uploaded or deleted

don't know the actual answer to your Q, but in case you don't get an answer: how about doing the change yourself in the DB. (that is of course if you have control over the DB design).
and put the HasImage (or HasContent) column straight in the table, with a default "false" and when you add the image you make it "true" and than you can consult that column to see if you have an image or not.

Related

How to calculate the (custom id) for a row which has not been inserted yet?

I am using the answer of this question How to automatically generate unique id in sql server to create a custom id for a table.It worked perfectly.Now I have a column which holds the values such as UID00000001 UID00000002 and so on. Suppose the last value in this column is UID00000003.Now I want to calculate the value for the row which hasn't been inserted yet via C# in one of my .aspx pages.In this case UID00000004. How can I achieve this value?
Any help would be appreciated.
Thank you.
If you are not required to generate these identifier at database level (e.g. some other processes insert records there), you can pre-generate them within your application. Something like above:
class Generator
{
public static int UniqueId = 0;
public static int GetNextId()
{
return Interlocked.Increment(ref UniqueId);
}
}
Then, your code can preallocate these identifiers and also format those strings. If multiple users access the same functionality, they will receive other identifiers. However, if one does not (successfully) performs a save operation, those identifiers will be lost.
You need to execute this query to get the next identity which will be generated for the table:
SELECT IDENT_CURRENT('table_name')+1;
For your case, it will have some other info concatenated with the next identity so the query will be like this:
SELECT 'UID' + RIGHT('00000000' + CAST(IDENT_CURRENT('table_name')+1 AS VARCHAR(8)), 8)
Of course you will need to write the C# code to send that query to the SQL Server.
Having said that, keep this in mind: When you get the value from that call and hold onto it, if during the time you are holding the value a record is inserted into that table, then the value is no longer the next value.
If you need the identiy value after a record is inserted in your application, please refer this answer.

Linq to SQL SubmitChanges only for specific table

I have been using linq to sql for a little while and often come up against this type of problem....
e.g
I have 2 db tables
-Table: Invoice ("Id" int auto-increment, "InvoiceDate" datetime)
-Table: InvoiceItems ("Id" int auto-increment, "InvoiceId" int (FK), "SomeReference" varchar(50))
The "SomeReference" field holds a value that is a combination of the Id from the parent Invoice record and some random characters. eg. "145AHTL"
Before i can set the value of SomeReference I need to know the value of the Invoice Id, but this only gets populated when it is saved to the DB. I have both parent and child records in the same Linq to SQl DB Context but I only want to perform "SubmitChanges" to the parent Invoice record only, so that i can then populate the SomeReference in the child record. I dont want to have the child InvoiceItem record saved to the DB before SomeReference is set.
How can I achieve this using Linq to Sql?
I understand that linq to sql uses the "Unit of Work" idea for saving to db, but I dont understand how I can avoid unnecessarily saving records to the db when they are not ready to be saved just yet. If there is no way around this, then why do developers bother with linq to sql, as this seems like such a huge drawback?
edit: should note that this example is just something i came up with to help describe my problem.
You can not. Not this way. And this is the only way (linq dues not support sequences). Brutally speaking - you have to fix your logic. The Id of an invoice is not a refernce field. It should not ever never be the number. This is a logical field and should be handled by your logic, outside the Id.
You example can be done, but you need to forget about the SQL and the database, but think in an ORM way.
Two issues need to be addressed in your example
First inserting the master and detail at the same time
Pseudo code for how it works:
using (var dc = new datacontext())
var master = new masterentity;
master.somedata = "data";
dc.tb_master.InsertOnSumbut(master)
var detail = new detailentity
detail.tb_master = master
dc.tb_detail.InsertOnSubmit(detail)
Submitchanges()
So you assign the entities to eachother, not the keys.
Second: the SomeReference
This first part however, does not give you the somereference field, only sets the the foreign key properly.
Your somereference field contains redundant data (not necessary) so that needs to be solved.
The somereference is a string + the ID.
So you store the string part in a column in the database (and only that) and you implement a custom property somereference by using a partial class.
public partial class tb_detail
{
public string somereference
{
get
{
return _id.ToString() + _somestring;
}}}

Copying a row from one table to another using LINQ and C#

I have two tables that are pretty much exact clones of one another (identical columns, just different columns set as primary keys). Basically the second table is just for keeping a history of the first table. What I need to do is, when a user updates a record in table 1 I need to insert the original copy of that record into table 2.
I am using a LinqDataSource object and utilizing the LinqDataSource_Updating(object sender, LinqDataSourceUpdateEventArgs e) event so I have access to e.OriginalObject and that will be perfect for inserting the original row in table 2. My problem is that I don't want to have to set every property manually because there are about 50 of them, so I want to use Reflection but am not sure how to properly go about it.
Consider the following code:
INSTRUMENT_DATA_SHEET _original = (INSTRUMENT_DATA_SHEET)e.OriginalObject;
INSTRUMENT_DATA_SHEET_HISTORY _history = new INSTRUMENT_DATA_SHEET_HISTORY();
How can I go about copying all of the _original's property values to _history's? I have tried using the solution from this question, however it isn't working for me. It throws the error:
Property DATE has an incompatible type in E_and_I.INSTRUMENT_DATA_SHEET_HISTORY
My guess is that it's because the DATE column is part of the primary key in table 2, but not table 1. As I said, the only difference between the two tables are the primary keys. Here they are for your reference:
The problem I see is that your History type Date field is DateTime and your Original one is DateTime? (same problem with REV in History, it can't be null). You'll have to decide what happens if there is a null DateTime of Date in your original version. Then you should be able to modify Skeets code (oh dear!) to handle specifically these fields differently then the rest of the fields.
Ok I've managed to figure it out :) Here's what I did:
INSTRUMENT_DATA_SHEET _original = (INSTRUMENT_DATA_SHEET)e.OriginalObject;
INSTRUMENT_DATA_SHEET_HISTORY _history = new INSTRUMENT_DATA_SHEET_HISTORY();
foreach (PropertyInfo pi in _original.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
_history.GetType().GetProperty(pi.Name).SetValue(_history, pi.GetValue(_original, null), null);
}
Not very elegant but it gets the job done!

Best Practice - Handling multiple fields, user roles, and one stored procedure

I have multiple fields both asp:DropDownList's and asp:TextBox's. I also have a number of user roles that change the Visible property of certain controls so the user cannot edit them. All of this data is saved with a stored procedure call on PostBack. The problem is when I send in the parameters and the control was not on the page obviously there wasn't a value for it, so in the stored procedure I have the parameters initialized to null. However, then the previous value that was in the database that I didn't want changed is overwritten with null.
This seems to be a pretty common problem, but I didn't have a good way of explaining it. So my question is, how should I go about keeping some fields from being on the page but also keeping the values in the database all with one stored procedure?
Apply the same logic when chosing what data to update as the logic you're actually using when chosing what data (and its associated UI) to render.
I think the problem is you want to do the update of all fields in a single SQL update, regardless of their value.
I think you should do some sanity check of your input before your update, even if that implies doing individual updates for certain parameters.
Without an example, it is a little difficult to know your exact circumstances, but here is a fictitious statement that will hopefully give you some ideas. It is using t-sql (MS SQL Server) since you did not mention a specific version of SQL:
UPDATE SomeImaginaryTable
SET FakeMoneyColumn = COALESCE(#FakeMoneyValue, FakeMoneyColumn)
WHERE FakeRowID = #FakeRowID
This basically updates a column to the parameter value, unless the parameter is null, in which case it uses the columns existing value.
Generally to overcome this in my update function
I would load the current values for the user
Replacing any loaded values with the newly changed values from the form
Update in db.
This way I have all the current plus everything that has been changed will get changed.
This logic will also work for an add form because all the fields would be null then get replaced with a new value before being sent to the db. You would of course just have to check whether to do an insert or update.

LINQ to SQL Insert Sequential GUID

I have a database that is part of a Merge Replication scheme that has a GUID as it's PK. Specifically the Data Type is uniqueidentifier, Default Value (newsequentialid()), RowGUID is set to Yes. When I do a InsertOnSubmit(CaseNote) I thought I would be able to leave CaseNoteID alone and the database would input the next Sequential GUID like it does if you manually enter a new row in MSSMS. Instead it sends 00000000-0000-0000-0000-000000000000. If I add CaseNoteID = Guid.NewGuid(), the I get a GUID but not a Sequential one (I'm pretty sure).
Is there a way to let SQL create the next sequential id on a LINQ InsertOnSubmit()?
For reference below is the code I am using to insert a new record into the database.
CaseNote caseNote = new CaseNote
{
CaseNoteID = Guid.NewGuid(),
TimeSpentUnits = Convert.ToDecimal(tbxTimeSpentUnits.Text),
IsCaseLog = chkIsCaseLog.Checked,
ContactDate = Convert.ToDateTime(datContactDate.Text),
ContactDetails = memContactDetails.Text
};
caseNotesDB.CaseNotes.InsertOnSubmit(caseNote);
caseNotesDB.SubmitChanges();
Based on one of the suggestions below I enabled the Autogenerated in LINQ for that column and now I get the following error --> The target table of the DML statement cannot have any enabled triggers if the statement contains an OUTPUT clause without INTO clause.
Ideas?
In the Linq to Sql designer, set the Auto Generated Value property to true for that column.
This is equivalent to the IsDbGenerated property for a column. The only limitation is that you can't update the value using Linq.
From the top of the "Related" box on the right:
Sequential GUID in Linq-to-Sql?
If you really want the "next" value, use an int64 instead of GUID. COMB guid will ensure that the GUIDs are ordered.
In regards to your "The target table of the DML statement cannot have any enabled triggers if the statement contains an OUTPUT clause without INTO clause", check out this MS KB article, it appears to be a bug in LINQ:
http://support.microsoft.com/kb/961073
You really needed to do a couple of things.
Remove any assignment to the GUID type property
Change the column to autogenerated
Create a constraint in the database to default the column to NEWSEQUENTIALID()
Do insert on submit just like you were before.
On the insert into the table the ID will be created and will be sequential. Performance comparison of NEWSEQUENTIALID() vs. other methods
There is a bug in Linq2Sql when using an auto-generated (guid/sequential guid) primary key and having a trigger on the table.. that is what is causing your error. There is a hotfix for the problem:
http://support.microsoft.com/default.aspx?scid=kb;en-us;961073&sd=rss&spid=2855
Masstransit uses a combguid :
https://github.com/MassTransit/MassTransit/blob/master/src/MassTransit/NewId/NewId.cs
is this what you're looking for?
from wikipedia:
Sequential algorithms
GUIDs are commonly used as the primary key of database tables, and
with that, often the table has a clustered index on that attribute.
This presents a performance issue when inserting records because a
fully random GUID means the record may need to be inserted anywhere
within the table rather than merely appended near the end of it. As a
way of mitigating this issue while still providing enough randomness
to effectively prevent duplicate number collisions, several algorithms
have been used to generate sequential GUIDs. The first technique,
described by Jimmy Nilsson in August 2002[7] and referred to as a
"COMB" ("combined guid/timestamp"), replaces the last 6 bytes of Data4
with the least-significant 6 bytes of the current system date/time.
While this can result in GUIDs that are generated out of order within
the same fraction of a second, his tests showed this had little
real-world impact on insertion. One side effect of this approach is
that the date and time of insertion can be easily extracted from the
value later, if desired. Starting with Microsoft SQL Server version
2005, Microsoft added a function to the Transact-SQL language called
NEWSEQUENTIALID(),[8] which generates GUIDs that are guaranteed to
increase in value, but may start with a lower number (still guaranteed
unique) when the server restarts. This reduces the number of database
table pages where insertions can occur, but does not guarantee that
the values will always increase in value. The values returned by this
function can be easily predicted, so this algorithm is not well-suited
for generating obscure numbers for security or hashing purposes. In
2006, a programmer found that the SYS_GUID function provided by Oracle
was returning sequential GUIDs on some platforms, but this appears to
be a bug rather than a feature.[9]
You must handle OnCreated() method
Partial Class CaseNote
Sub OnCreated()
id = Guid.NewGuid()
End Sub
End Class

Categories

Resources