LINQ InsertOnSubmit Required Fields needed for debugging - linq-to-sql

I've been using the ADO.NET Strogly-Typed DataSet model for about 2 years now for handling CRUD and stored procedure executions. This past year I built my first MVC app and I really enjoyed the ease and flexibility of LINQ. Perhaps the biggest selling point for me was that with LINQ I didn't have to create "Insert" stored procedures that would return the SCOPE_IDENTITY anymore (The auto-generated insert statements in the DataSet model were not capable of this without modification).
Currently, I'm using LINQ with ASP.NET 3.5 WebForms. My inserts are looking like this:
ProductsDataContext dc = new ProductsDataContext();
product p = new product
{
Title = "New Product",
Price = 59.99,
Archived = false
};
dc.products.InsertOnSubmit(p);
dc.SubmitChanges();
int productId = p.Id;
So, this product example is pretty basic, right, and in the future, I'll probably be adding more fields to the database such as "InStock", "Quantity", etc... The way I understand it, I will need to add those fields to the database table and then delete and re-add the tables to the LINQ to SQL Class design view in order to refresh the DataContext. Does that sound right?
The problem is that any new fields that are non-null are NOT caught by the ASP.NET build processes. For example, if I added a non-null field of "Quantity" to the database, the code above would still build. In the DataSet model, the stored procedure method would accept a certain amount of parameters and would warn me that my Insert would fail if I didn't include a quantity value. The same goes for LINQ stored procedure methods, however, to my knowledge, LINQ doesn't offer a way to auto generate the insert statements and that means I'm back to where I started.
The bottom line is if I used insert statements like the one above and I add a non-null field to my database, it would break my app in about 10-20 places and there would be no way for me to detect it. Is my only option to do a solution-side search for the keyword "products.InsertOnSubmit" and make sure the new field is getting assigned?
Is there a better way?
Thanks!

Create a NON-Null field, with a default value of whatever. This way the old rows will have a default value you know of, letting you go back and add values to the table.

Related

INSERT with Linq omitting some "fake" columns

I have a table in the database with the following columns: ID, Name, Txt. We are using Linq To Sql to implement our DAL. In there another collegue added two extra columns so in the code the same table results: ID, Name, Txt, NameTemp, TxtTemp.
These two "fake" tables are used in different parts of the code in LINQ joins and analyzing with SQL Profiler the parsed SQL query takes the "real" columns and everything works properly.
Now I need to make an INSERT using that table, but I get an exception since also the fake columns are used in the statement.
Since I cannot add the two fake columns in the DB(since unuseful there), is there a way in which I could make an insert with Linq omitting these two columns?
I think i know where you're getting at. You should be able to add properties to a partial linq class no problem, only thing is that if you try and use a linq query against these "fake" columns, you'll get an exception when linqtosql tries to reference a column that doesn't exist in the database. I've been through this before - i wanted to be able to select columns that don't exist in the database (but do in the linq2sql dbml class) and have linq2sql translate the columns into what they really are in the database. Only problem is that there's no real easy way to do this - you can add attributes to the "fake" properties so that linq2sql thinks that NameTmp and TxtTmp are in fact Name and Txt in the sql world, only problem is that when it comes to inserting a record, the translated sql specifies the same column twice (which SQL doesn't like and throws an exception).
You can mark the column with IsDbGenerated = true - that'll let you insert records without getting the double column problem, but you can't update a record without linqtosql complaining that you can't update a computed column. I guess you can use a sproc to get around this perhaps?
I logged a bug with Microsoft a while back, which they'll never fix. The info here might help you get what you need -
http://social.msdn.microsoft.com/Forums/eu/linqtosql/thread/5691e0ad-ad67-47ea-ae2c-9432e4e4bd46
https://connect.microsoft.com/VisualStudio/feedback/details/526402/linq2sql-doesnt-like-it-when-you-wrap-column-properties-with-properties-in-an-interface
LINQ is not for inserting data, but for querying only - Language INtegrated Query. Use ADO.NET for inserting the data.
(Leaving the first part to remind my stupidity)
Check ScottGu. The classes generated are partial (mentioned here), so you can put your 2 properties into the editable part and since they won't have any mapping attribute defined, they won't be mapped nor persisted.

Entity Framework 4 (CTP 5) unwisely queries in comparison with LINQ-to-SQL

I have an issue about Entity Framework 4 CTP 5, which I realize LINQ to SQL handle it better than this, but I insist on using EF 4 because of its Code-First feature.
So here is my problem:
Imagine products with its Tags (or whatever with one to many relation). And tblTags will be changing by deleting some and inserting some tags at a time by user (and user might change them completely different). So in my opinion we can simply delete all old tags and insert new ones (simplest thing to do), although it is a simple idea, LINQ to SQL manage it perfectly amazing unlike EF 4. The codes for comparison with LINQ are:
LINQ to SQL:
Dim oldTags = ctx.Tags.Where(Function(m) m.ProductID = pID)
Dim newTags = 'from user input, like from the model which was posted, like in MVC'
ctx.Tags.DeleteAllOnSubmit(oldTags)
ctx.Tags.InsertAllOnSubmit(newTags)
ctx.SubmitChanges()
EF 4 CTP5:
Dim oldTags = ctx.Tags.Where(Function(m) m.ProductID = pID)
Dim newTags = 'from user input, like from the model which was posted, like in MVC'
For Each item In oldTags
ctx.Tags.Remove(item)
Next
For Each item In newTags
ctx.Product.Add(item)
Next
ctx.SaveChanges()
The point that I'm trying to make is:
In LINQ to SQL, it will first try to update
old ones (instead of deleting them from DB). Then insert extra tags.
But in Entity Framework, it create and send one query per each "remove" and one
query per each "add".
EF doesn't calculate the updates, but does as it is told to do(no more). it will send lots of queries through SQL so it will rise my auto number column for no reason.
I know that i can do some logic for the second problem, which then I prefer LINQ to SQL in that way.
But I cant do anything about queries pass to SQL. It sound it is not optimized.
I want Code-First combine with these features that LINQ to SQL currently have(calculating stuff before commit). Any suggestion? Have I done anything wrong in EF code? Or is it gonna be like LINQ to SQL in commit changes in RTM version?
UPDATE
If you don't agree with this behavior please say so and leave a comment.
And If you need more information, please tell me to provide some.
I update this post cause I'm still waiting for an answer. thanks for your time.
You should use batch operation for this:
SaveChanges(SaveChangesOptions.Batch);
Read more at msdn.

Linq to SQL and Gridview Datasource

I have a question related to this one. I don't want to do a calculation (aggregation), but I need to get display values from an association. In my C# code, I can directly reference the value, because the foreign key constraint made Linq generate all the necessary wiring.
When I specify the IQueryable as the Gridview datasource property, and reference something that is not a column of the primary entity in the result set, I get an error that the column does not exist.
As a newbie to Linq, I am guessing the assignment implicitely converts the IQueryable to a list, and the associations are lost.
My question is, what is a good way to do this?
I assume that I can work around this by writing a parallel query returning an anonymous type that contains all the columns that I need for the gridview. It seems that by doing that I would hold data in memory redundantly that I already have. Can I query the in-memory data structures on the fly when assigning the data source? Or is there a more direct solution?
The gridview is supposed to display the physician's medical group associations, and the name of the association is in a lookup table.
IQueryable<Physician> ph =
from phys in db.Physicians
//from name in phys.PhysicianNames.DefaultIfEmpty()
//from lic in phys.PhysicianLicenseNums.DefaultIfEmpty()
//from addr in phys.PhysicianAddresses.DefaultIfEmpty()
//from npi in phys.PhysicianNPIs.DefaultIfEmpty()
//from assoc in phys.PhysicianMedGroups.DefaultIfEmpty()
where phys.BQID == bqid
select phys;
(source: heeroz.com)
So, based on Denis' answer, I removed all the unneeded stuff from my query. I figured that I may not be asking the right question to begin with.
Anyways, the page shows a physician's data. I want to display all medical group affiliations in a grid (and let the user insert, edit, and update affiliations). I now realize that I don't need to explicitly join in these other tables - Linq does that for me. I can access the license number, which is in a separate table, by referencing it through the chain of child associations.
I cannot reference the medical group name in the gridview, which brings me back to my question:
AffiliationGrid.DataSource = ph.First().PhysicianMedGroups;
This does not work, because med_group_print_name is not accessible for the GridView:
A field or property with the name 'med_group_print_name' was not found on the
selected data source.
Again, bear with me, if it is all too obvious that I don't understand Linq ... because I don't.
Your query seems strange. You should try to simply display
ph = from phys in db.Physicians
where phys.BQID == bqid
select phys;
in your grid. That should work.
Also, why the calls to Load()? If the DataContext is not disposed when the grid is binding, you should not need it.
If you still have issues, can you please post the error message you get, that would help...
Part 2
The problem is that you have the name is effectively not in the PhysMedGroup. You need to navigate one level down to the MedGroupLookup to access the name, since it is a property of that class.
Depending on the technology you are using (it seems to be either WinForms or Web Forms), you will need to configure your data-binding to access MedGroupLookup.med_group_print_name.

Linq to SQL Updating through BLL issues - Best Practices

The Setup:
I have a large form with many fields that are collected to update a Product object. So in the ASPX page the user changes the fields that need updating and they hit submit. In the code behind I do something like this;
Dim p as New MyCompany.Product()
p = p.GetProductById(ProductID)
I extend the Product partial class of Linq to SQL to add this method (GetProductById) to the object
p.Name = txtName.Text
p.SKU = txtSKU.Text
p.Price = txtPrice.Text
...
p.Update()
This is an Update method in the extended Product partial class. I update the database, send emails and update history tables so i want this method to do all those things.
There are 50 more fields for the project so obviously it would be ridiculous to have a method that collects all 50 fields (and I don't want to go that route anyway bc it's harder to debug IMO)
The Problem:
If I get the Product via Linq to SQL using a DataContext then I can never update it again because it errors about not being able to attach and entity that's already attached to another DataContext.
The Question:
SO if I get an object through a method in my BLL, update it in the ASPX page and then try to send the updates through the BLL again to update the database, how should I go about doing this?
Regardless of LINQ-to-SQL or not, here's what I do. Upon submission, I search for the item (it should be quick if it is a single item using the PK), my DAL returns a data object, and I use reflection to map each element in the page with corresponding properties in the data object. My DAL only updates items that changed.
I think what you have to do is the same, gathering all values and submitting them. If LinqToSql is not smart enough to determine what changed then it may not be the best alternative.

Limiting results of System.Data.Linq.Table<T>

I am trying to inherit from my generated datacontext in LinqToSQL - something like this
public class myContext : dbDataContext {
public System.Data.Linq.Table<User>() Users {
return (from x in base.Users() where x.DeletedOn.HasValue == false select x);
}
}
But my Linq statement returns IQueryable which cannot cast to Table - does anyone know a way to limit the contents of a Linq.Table - I am trying to be certain that anywhere my Users table is accessed, it doesn't return those marked deleted. Perhaps I am going about this all wrong - any suggestions would be greatly appreciated.
Hal
Another approach would to be use views..
CREATE VIEW ActiveUsers as SELECT * FROM Users WHERE IsDeleted = 0
As far as linq to sql is concerned, that is just the same as a table. For any table that you needed the DeletedOn filtering, just create a view that uses the filter and use that in place of the table in your data context.
You could use discriminator column inheritance on the table, ie. a DeletedUsers table and ActiveUsers table where the discriminator column says which goes to which. Then in your code, just reference the Users.OfType ActiveUsers, which will never include anything deleted.
As a side note, how the heck do you do this with markdown?
Users.OfType<ActiveUsers>
I can get it in code, but not inline
Encapsulate your DataContext so that developers don't use Table in their queries. I have an 'All' property on my repositories that does a similar filtering to what you need. So then queries are like:
from item in All
where ...
select item
and all might be:
public IQueryable<T> All
{
get { return MyDataContext.GetTable<T>.Where(entity => !entity.DeletedOn.HasValue); }
}
You can use a stored procedure that returns all the mapped columns in the table for all the records that are not marked deleted, then map the LINQ to SQL class to the stored procedure's results. I think you just drag-drop the stored proc in Server Explorer on to the class in the LINQ to SQL designer.
What I did in this circumstance is I created a repository class that passes back IQueryable but basically is just
from t in _db.Table
select t;
this is usually referenced by tableRepository.GetAllXXX(); but you could have a tableRepository.GetAllNonDeletedXXX(); that puts in that preliminary where clause to take out the deleted rows. This would allow you to get back the deleted ones, the undeleted ones and all rows using different methods.
Perhaps my comment to Keven sheffield's response may shed some light on what I am trying to accomplish:
I have a similar repository for most
of my data access, but I am trying to
be able to traverse my relationships
and maintain the DeletedOn logic,
without actually calling any
additional methods. The objects are
interrogated (spelling fixed) by a StringTemplate
processor which can't call methods
(just props/fields).
I will ultimately need this DeletedOn filtering for all of the tables in my application. The inherited class solution from Scott Nichols should work (although I will need to derive a class and relationships for around 30 tables - ouch), although I need to figure out how to check for a null value in my Derived Class Discriminator Value property.
I may just end up extended all my classes specifically for the StringTemplate processing, explicitly adding properties for the relationships I need, I would just love to be able to throw StringTemplate a [user] and have it walk through everything.
There are a couple of views we use in associations and they still appear just like any other relationship. We did need to add the associations manually. The only thing I can think to suggest is to take a look at the properties and decorated attributes generated for those classes and associations.
Add a couple tables that have the same relationship and compare those to the view that isn't showing up.
Also, sometimes the refresh on the server explorer connection doesn't seem to work correctly and the entities aren't created correctly initially, unless we remove them from the designer, close the project, then reopen the project and add them again from the server explorer. This is assuming you are using Visual Studio 2008 with the linq to sql .dbml designer.
I found the problem that I had with the relationships/associations not showing in the views. It seems that you have to go through each class in the dbml and set a primary key for views as it is unable to extract that information from the schema. I am in the process of setting the primary keys now and am planning to go the view route to isolate only non-deleted items.
Thanks and I will update more later.