Как перейти с Linq 2 SQL на Linq 2 Entities?

Я хотел бы начать справочник для людей, которые хотят перейти от linq2sql к linq2entities и ADO.net Entity Framework (здесь называется L2E). Я не хочу обсуждать, кто из этих двух лучше. Я просто хочу создать список различий между этими двумя для людей, которые хотят перейти от одного к другому.

Суть проста: удалить классы данных linq2sql, добавить модель ado.net (созданную из базы данных). Переименуйте Entities на имя бывшего контекста данных.


Теперь о различиях. Например, чтобы сохранить (сохранить) изменения в L2S, я бы использовал:

using (MyDataClassesDataContext mydc = new MyDataClassesDataContext())
{
  // change data
  mydc.SubmitChanges();
}

В L2E это должно быть изменено на:

using (MyDataClassesDataContext mydc = new MyDataClassesDataContext())
{
  // change data
  mydc.SaveChanges();
}


2-й пример, чтобы вставить новую запись в L2S, вы должны использовать:

using (MyDataClassesDataContext mydc = new MyDataClassesDataContext())
{
  MyTable myRow = new MyTable();
  mydc.MyTable.InsertOnSubmit(myRow);
  mydc.SubmitChanges();
}

В L2E это должно быть изменено на:

using (MyDataClassesDataContext mydc = new MyDataClassesDataContext())
{
  MyTable myRow = new MyTable(); // or = MyTable.CreateMyTable(...);
  mydc.AddToMyTable(myRow);
  mydc.SaveChanges();
}    


Для других фрагментов кода я пропущу часть using (...) и SubmitChanges / SaveChanges, поскольку она всегда одинакова.
Чтобы прикрепить измененный объект к тексту / модели данных в L2S (используя временную метку):

mydc.MyTable.Attach(myRow);

В L2E:

// you can use either
mydc.Attach(myRow);
// or (have not tested this)
mydc.AttachTo("MyTable", myRow);


Чтобы прикрепить измененный объект к тексту / модели данных в L2S (используя исходный объект) :

mydc.MyTable.Attach(myRow, myOriginalRow);

В L2E ( MSDN - применение изменений, внесенных в отдельный объект ):

mydc.Attach(myOriginalRow);
mydc.ApplyPropertyChanges(myOriginalRow.EntityKey.EntitySetName, myRow);


Чтобы удалить запись в L2S :

mydc.MyTable.DeleteOnSubmit(myRow);

В L2E:

mydc.DeleteObject(myRow);


Чтобы показать созданные команды SQL для отладки в L2S :

mydc.Log = Console.Out;
// before mydc.SubmitChanges();

В L2E вы можете показать SQL для запроса (благодаря TFD):

using System.Data.Objects;
...
var sqlQuery = query as ObjectQuery;
var sqlTrace = sqlQuery.ToTraceString();

К сожалению, я не нашел способа вывести SQL, сгенерированный для вызова SaveChanges () - для этого вам понадобится профилировщик SQL .


Чтобы создать базу данных из схемы, если ее нет L2S :

if (!mydc.DatabaseExists())
  mydc.CreateDatabase();

В L2E:

// according to TFD there are no DDL commands in L2E


Чтобы выполнить команду SQL для базы данных в L2S :

mydc.ExecuteCommand("ALTER TABLE dbo.MyTable ADD CONSTRAINT DF_MyTable_ID DEFAULT (newid()) FOR MyTableID");

В L2E:

Чтобы выполнить команду eSQL для базы данных в EF (будьте осторожны, eSQL еще не поддерживает команды DDL или DML (alter, Insert, update, delete)):

using System.Data.EntityClient;
...
EntityConnection conn = this.Connection as EntityConnection;
using (EntityCommand cmd = conn.CreateCommand())
{
  conn.Open();
  cmd.CommandText = @"Select t.MyValue From MyEntities.MyTable As t";
  var result = cmd.ExecuteReader(System.Data.CommandBehavior.SequentialAccess);
  result.Read();
  var myValue = result.GetValue(0);
  ...
  conn.Close();
}

Текст команды находится в Entity SQL, который не на 100% совпадает с T-SQL.
(спасибо TFD)

Если вам нужны команды DDL / DML для одного и того же соединения, вам может потребоваться создать соединение с базой данных самостоятельно, подключить EF, используя собственное соединение с базой данных, и использовать это соединение для ваших команд DML. Не очень, посмотрите сами:

MetadataWorkspace workspace = new MetadataWorkspace(new string[] { "res://*/" }, new Assembly[] { Assembly.GetExecutingAssembly() });
using (SqlConnection sqlConnection = new SqlConnection("Data Source=salsa;Initial Catalog=SamAlyza;Integrated Security=True"))
using (EntityConnection econ = new EntityConnection(workspace, sqlConnection))
using (AlyzaDataClassesDataContext adc = new AlyzaDataClassesDataContext(econ))
{
   // now you can use the SqlConnection like always
}


Чтобы предоставить значения по умолчанию для вновь созданного L2S-класса, переопределите частичный метод OnCreated:

partial void OnCreated()
{
  Name = "";
}

В L2E вы можете просто создать конструктор по умолчанию для своего класса таблицы:

partial class MyTable
{
  public MyTable()
  {
    Name = "";
  }
}


Следующие примеры относятся к соотношению 1: n между двумя таблицами. Я определяю таблицу здесь в SQL, чтобы вы знали, о чем я пишу:

CREATE TABLE dbo.[MyTable]
(
 [MyTableID] uniqueidentifier NOT NULL ROWGUIDCOL CONSTRAINT [PK_MyTable] PRIMARY KEY,
 [Name] nvarchar(100) NOT NULL,
)  ON [PRIMARY]

ALTER TABLE dbo.[MyTable] ADD CONSTRAINT [DF_MyTable_ID] DEFAULT (newid()) FOR [MyTableID]


CREATE TABLE dbo.[MySubTable]
(
 [MySubTableID] uniqueidentifier NOT NULL ROWGUIDCOL CONSTRAINT [PK_MySubTable] PRIMARY KEY,
 [MyTableID] uniqueidentifier NULL,
 [Subname] decimal(18,2) NOT NULL,
)  ON [PRIMARY]

ALTER TABLE dbo.[MySubTable] ADD CONSTRAINT [DF_MySubTable_ID] DEFAULT (newid()) FOR [MySubTableID]

ALTER TABLE dbo.[MySubTable] ADD CONSTRAINT [FK_MySubTable_MyTable] FOREIGN KEY
(
 [MyTableID]
) REFERENCES dbo.[MyTable]
(
 [MyTableID]
) ON DELETE CASCADE


Вставка записи в MyTable с соответствующей MySubTable в L2S :

  MyTable myRow = new MyTable();
  myRow.MySubTable.Add(new MySubTable());
  mydc.MyTable.InsertOnSubmit(myRow);

Очень похоже в L2E:

  MyTable myRow = new MyTable();
  myRow.MySubTable.Add(new MySubTable());
  mydc.AddToSaLyWebsites(test);


В L2S для поиска в подтаблице вы можете использовать:

from u in adc.MySubTable 
where u.MyTableID == _searchForTableID && u.Name == _searchForName 
select u

В L2E вы не можете получить доступ к столбцам отношений:

from u in adc.MySubTable 
where u.MyTable.MyTableID == _searchForTableID && u.Name == _searchForName 
select u

(конечно, вы также можете использовать)

from u in _searchForTable.MySubTable
where u.Name == _searchForName
select u

(странное примечание: _searchForTable не нужно прикреплять к EF, чтобы это работало.)


Разные примечания:

В L2S я могу использовать разные функции в LINQ. Если я использую пользовательские функции в L2E, я получаю NotSupportedException. Итак, вместо

from t in mydc.MyTable 
where t.Date >= _searchForDate && t.Date <= _searchForDate.AddHours(2) 
select t;

В L2E нужно было бы использовать

DateTime endDate = _searchForDate.AddHours(2);
from t in mydc.MyTable 
where t.Date >= _searchForDate && t.Date <= endDate 
select t;


Хотя L2S может считывать автоматически сгенерированные значения из базы данных, такие как, например, автоматически сгенерированный идентификатор, в L2E это, похоже, работает только с использованием идентификатора типа sql.

(Я буду собирать больше различий в этом посте, когда наткнусь на них или когда кто-то добавит их в ответы)

Некоторые ссылки, которые могут быть полезны:
- Разница между Transact-SQL и Entity-SQL
- NET - ADO.NET Entity Framework и LINQ to Entities
- Майк Тэлти об отключенном LINQ to Entities (для бета-версии 2 L2E)

Ответов (4)

Чтобы показать созданные команды SQL для отладки в EF

using System.Data.Objects;
...
var sqlQuery = query as ObjectQuery<T>;
var sqlTrace = sqlQuery.ToTraceString();

AFAIK нет команд для создания БД или выполнения какой-либо работы DDL. Это конструктивное ограничение языка Entity SQL.

Область проектирования EDMX отобразит вашу текущую схему базы данных, а не наоборот.

Чтобы выполнить команду SQL для базы данных в EF

using System.Data.EntityClient;
...
EntityConnection conn = new EntityConnection(myContext.Connection.ConnectionString);
conn.Open();
EntityCommand cmd = conn.CreateCommand();
cmd.CommandText = @"Select t.MyValue From MyEntities.MyTable As t";
var result = cmd.ExecuteReader(System.Data.CommandBehavior.SequentialAccess);
result.Read();
var myValue = result.GetValue(0);
...
conn.Close();

Текст команды находится в Entity SQL, который не на 100% совпадает с T-SQL.

Чтобы получить новое значение идентификатора из вставки в EF

Create Table dbo.MyItem (
    Id int indentity(1, 1) Primary Key,
    Value varchar(100)
)

var newItem = new MyItem() { Value = "Hello" };
context.AddToMyItem(newItem);
context.SaveChanges(true);
var theNewIdentityValue = newItem.Id;

Ребята из EF сделали это простой и приятной работой :-)

В L2S вы можете просто использовать хранимые процедуры, такие как вызовы функций. В EF SP должен возвращать объект. Это может вызвать проблемы, если ваш SP возвращает только подмножество полного объекта.