Read-only LINQ to SQL - linq-to-sql

I'm using LINQ to SQL to access my database but I'm only reading, I never insert, update or delete anything. Are there ways to optimize LINQ2SQL for this?

Yes there is. Linq 2 SQL will by default cache all data that you read from the DB. It needs to do this to track any changes you apply to your objects, so it can generate the necessary insert/update/delete statements when you call SubmitChanges()
If you're only reading data, this is unnessecary. You can turn off object tracking by setting the ObjectTrackingEnabled property to false on your DataContext.

One thing I've been told, is to avoid using the generated record class.
That is, if you have a Users table, L2S will create for you a User class, which is what it returns from the database. Instead of using that directly, you should create a "shadow" class --- all the same Properties, but nothing else, and immedaitely copy the data into those records for your use. In fact, if it's going to be exclusively read-only, you can assign them in the ctor, and only have public getters:
class myUser
{
public string FName {get; private set}
public string LName {get; private set}
public myUser(User user)
{
this.FName = user.FName;
this.LName = user.LName;
}
}
var users = from u in db.Users
where .....
select new myUsers(u);
This avoids a lot of overhead needed to deal with the possibility of writing the object out again.

Related

In LinqPad how to discard/clear all pending changes to database, LinqToSql DataContext does not have a method

Using the amazing LinqPad. Is there a way to clear all pending database changes to the built-in LinqToSql DataContext (the UserQuery object which is this)?
Example LinqPad file, connection to a basic mssql database with tb_person table.
void Main()
{
var newRow = new tb_person() {FirstName = "Bob"};
// do lots of db updates, deletes, inserts....
tb_persons.InsertOnSubmit(newRow);
// how to do this, revert all pending Db changes?
//this.SomeMethodToDiscardChanges()
}
I found 2 option, neither seems to be ideal, maybe there is a LinqPad specific way outside of LinqToSql?
Create a new DataContext, how to do this in LinqPad?
DataContext.Refresh() method passing in every added or changed entity, cumbersome!
References
How to clear the DataContext cache on Linq to Sql
No guarantees, but you can try
this.Uncapsulate().ClearCache();
I've used this and it seems to work, but I've never used it on a production database.
Alternatively, you can create a new DataContext using
var dc = new TypedDataContext(this.Connection.ConnectionString);
but of course you can't assign to this which means that you need to be careful to prefix all your queries and references to use dc.

Linq to SQL - Error handling

I have a linq to sql statement that inserts records in the database. If there is a duplicate, it throws Primary key violation .
after this happens, whenever i try to execute any other statement, it repeatedly shows this error. Is this a problem of transaction not getting closed. Also how to handle transactions in a more reliable way in LINQ to SQL
Each DataContext instance tracks the instances of the mapping classes that it has seen. When you say: myDataContext.InsertOnSubmit(myCustomer); you are registering a customer instance with this DataContext. Later, when you say myDataContext.SubmitChanges();, the DataContext attempts to carry out all changes that it is tracking.
If these changes fail - the DataContext does not stop tracking them and will attempt to make the change each time SubmitChanges is called.
In order to have a DataContext that is not tracking a record that it can't insert, you should abandon this instance and new-up another one.
If your primary key field is an identity field, DO NOT populate it when you insert it into the database. For example, given a Customer table with the following structure:
Customer
========
Customer_ID (PK, identity)
Last_Name (varchar)
First_Name (varchar)
Middle_Initial (char)
this is possible:
public int CreateCustomer(string lastName, string firstName, string middleInitial)
{
using (DataContext dc = new DataContext())
{
Customer customer = new Customer();
customer.Last_Name = lastName;
customer.First_Name = firstName;
customer.Middle_Initial = middleInitial;
dc.Customers.InsertOnSubmit(customer);
dc.SubmitChanges();
return customer.Customer_ID;
}
}
The most likely reason for your error is that you are trying to access the entity object after the DataContext in which it was created has been destroyed. Using the above example, this will produce an error similar to the one you are probably receiving:
public int CreateCustomer(string lastName, string firstName, string middleInitial)
{
using (DataContext dc = new DataContext())
{
Customer customer = new Customer();
customer.Last_Name = lastName;
customer.First_Name = firstName;
customer.Middle_Initial = middleInitial;
dc.Customers.InsertOnSubmit(customer);
dc.SubmitChanges();
}
return customer.Customer_ID; // <<-- Error occurs here
}
The reason the error occurs is due to the built-in change tracking of the LINQ engine. When the DataContext is created, any entity objects created within that DataContext are tied to that DataContext via a reference within the entity object. Once the DataContext falls out of scope, the reference is no longer valid and the contents of the entity object are no longer considered reliable to the LINQ engine.
As far as transaction handling is concerned, a transaction is created when the DataContext is created. When SubmitChanges is called, all changes are executed within the context of the DataContext's transaction, no matter how many entities/tables are involved.
Are you re-using the same DataContext? If so, don't do that - create a new DataContext for each logical operation and use TransactionScope to manage transactions
Why not check for a duplicate record before trying to save the record?
Throwing exceptions are very costly.

How can I control the creation of database indexes when using DataContext.CreateDatabase()

I am new to LINQ to SQL, but have done a lot of database development in the past.
The software I just started working on uses:
// MyDataContext is a sub class of DataContext, that is generated with SqlMetal
MyDataContext db = new MyDataContext (connectionString);
db.CreateDatabase();
to create the database when it is first run.
I need to add some indexes to the tables....
How can I tell the DataContext what indexes I want?
Otherwise how do I control this?
(I could use a sql script, but I like the ideal that db.CreateDatabase will always create a database that matches the data access code)
(For better, or worse the software has full access to the database server and our software often create databases on the fly to store result of model runs etc, so please don’t tell me we should not be creating databases from code)
I seem not to be the only person hitting limts on DataContext.CreateDatabase() see also http://csainty.blogspot.com/2008/02/linq-to-sql-be-careful-of.html
As far as I know the DataContext.CreateDatabase method can only create primary keys.
When you look at the DBML directly, you will see that there are no elements for defining an index. Therefore it is, IMHO, save to assume that CreateDatabase cannot do it.
So the only way I can think of for creating indexes "automatically" is by first calling DataContext.CreateDatabase and then calling DataContext.ExecuteCommand to add the indexes to the tables that were just created.
You can execute SQL Command on DatabaseCreated method.
public partial class DatabaseModelsDataContext : System.Data.Linq.DataContext
{
partial void OnCreated ()
{
var cmdText = #"
IF EXISTS (SELECT name FROM sys.indexes WHERE name = N'IX_LeafKey')
DROP INDEX IX_MyTableColumn
ON [mydb].[dbo].[Leaf];
CREATE INDEX IX_MyTableColumn
ON [mydb].[dbo].[MyTable] ([column]) ;";
ExecuteCommand(cmdText);
}
}

inserting extra data in linq to sql partial class

I have a L2S generated class called Accounts, I have a L2S class called UsersInAccounts I need to add a function call AddUserToAccount(accountid, userid) should/could this function be added to the partial Accounts class I have created or are partial classes used for getting data rather than editing data
public partial class Account
{
public void addUser(Guid userid)
{
// code
}
}
I don't think that what you are doing is a problem. In your code, you'd probably have an Account instance that you want to do things with so being able to do this:
Account theAccountIWant = GetTheAccount();
theAccountIWant.addUser(myUsersGUID);
...seems pretty intuitive. It might be an idea to do some error trapping inside your addUser method and pass back some sort of success status but that's another discussion.
edit: As advised, if you then retrieve a User object and want to attach it to the Account using the AccountUsers property then this is no use unless you pass the DataContext in.

LINQ-to-SQL and SQL Compact - database file sharing problem

I'm learing LINQ-to-SQL right now and i have wrote a simple application that define SQL data:
[Table( Name = "items" )]
public class Item
{
[ Column( IsPrimaryKey = true, IsDbGenerated = true ) ]
public int Id;
[ Column ]
public string Name;
}
I have launched 2 copy of application connected to the same .sdf file and tested if all database modifications in one application affects another application. But strange thing arise. If i use InsertOnSubmit() and DeleteOnSubmit() in one application, added/removed items are instantly visible in other application via 'select' LINQ queue. But if i try to modify 'Name' field in one application, it is NOT visible in other applicaton until it reconnects the database :(. The test code i use:
var Items = from c in db.Items
where Id == c.Id
select c;
foreach( var Item in Items )
{
Item.Name = "new name";
break;
}
db.SubmitChanges();
Can anyone suggest what i'm doing wrong and why InsertOnSubmit()/DeleteOnSubmit works and SubmitChanges() don't?
This is related to how Linq-to-Sql manages Object Identity. If you query for the same entity more than once, you will always receive the same object representing the row in the database.
When you insert or delete entities, you are able to see the changes from another client because Linq-to-Sql will need to create/delete the objects associated with those entities and, if there are no conflicts, this presents no problem.
Updating has a different behavior that's explained in the Object Identity article.
LINQ to SQL uses this approach to
manage the integrity of local objects
in order to support optimistic
updates. Because the only changes that
occur after the object is at first
created are those made by the
application, the intent of the
application is clear.
If you need the most updated data from the database, use the DataContext.Refresh with RefreshMode.OverwriteCurrentValues.