понедельник, 24 июня 2013 г.

Database scan with Entity Framework (part 1 monothread)

При работе над одним проектом возникла задача асинхронного сканирования базы данных на предмет появления новых записей с последующей их обработкой. В качестве поставщика данных решено было использовать ORM Entity Framework 5.0, который активно продвигается в Microsoft. Далее рассмотрим основные подходы к использованию EF.
В EF для решения тех или иных задач используется три подхода:
  • Database first;
  • Model first;
  • Code first;
Так как у нас в проекте уже была база данных, написанная на SQL Server, был использован подход Database first. В кратце напомню его суть: по уже готовой базе данных EF строит модель классов, которые содержат ту же логику привязки данных, что и таблицы в базе данных, также создается контекст доступа к ним (класс-наследник от DbSet). Например, у нас есть таблица Engines:

CREATE TABLE Spacecrafts.Engine
 (
 ID_Engine int NULL CONSTRAINT UQ_ID_ENG UNIQUE,
 NameEngine nvarchar(50) NULL,
 Trust real NOT NULL,
 SpecificImpulse real NOT NULL,
 FuelAmount int NULL,
 MaxTimeOfWorking real NULL,
 TypeOfEngine nvarchar(50) NULL,
 Comment nvarchar(150) NULL
 )  ON [PRIMARY]
GO
для которой EF создает класс Engine с соответствующими свойствами:
public class SomeClass 
 public partial class Engine
    {
        public Engine()
        {
            this.SpacecraftCharacteristics = new HashSet();
        }
    
        public int ID_Engine { get; set; }
        public string NameEngine { get; set; }
        public float Trust { get; set; }
        public float SpecificImpulse { get; set; }
        public Nullable FuelAmount { get; set; }
        public Nullable MaxTimeOfWorking { get; set; }
        public string TypeOfEngine { get; set; }
        public string Comment { get; set; }
    
        public virtual ICollection SpacecraftCharacteristics { get; set; }
    }
Далее перейдем к непосредственной работе с этим классом:
 Напомню задачу: нам необходимо сканировать базу данных на предмет появления новых записей для последующей их обработки. Для решения этой задачи я нашел два решения: первое заключалось в использовании глобального контекста(что сразу не очень хорошо), но тем не менее у него есть право на жизнь. Сначала мы создаем глобальный контекст для нашей базы данных, а затем с некой периодичностью применяем его метод Load(), который будет записывать в текущий контекст все данные из таблицы базы данных. Затем с помощью метода Local с ними можно работать.

var context = new CCYCEntities()); //создаем контекст базы данных CCYC
var nNu = new Engine               //создаем новые сущности
{
    Comment = "From Wrapper",
    ID_Engine = 1,
    NameEngine = "My Engine",
    SpecificImpulse = 114,
    FuelAmount = 123,
    MaxTimeOfWorking = 123,
    Trust = 1,
    TypeOfEngine = "www"
};
var nNut = new Engine
{
    Comment = "From Wrapper",
    ID_Engine = 2,
    NameEngine = "My Engine",
    SpecificImpulse = 114,
    FuelAmount = 123,
    MaxTimeOfWorking = 123,
    Trust = 1,
    TypeOfEngine = "www"
};
try
{
    context.Engines.Load();  // загружает cущности из БД
    context.Engines.Add(nNu); //добавляем новые сущности в контекст
    context.Engines.Add(nNut);
    var res1 = context.Engines.Local.Count(); //сколько сущностей сейчас в контексте
    context.SaveChanges(); сохраняем несохраненные сущности в БД
}
 catch (DataException exception)
 {
    MessageBox.Show(exception.Message);
 }
Таким образом циклично повторяя Load() можно добиться постоянного сканирования БД.
Теперь способ №2.
Суть его в том, что мы используем локальный контекст данных, который после использования сразу освобождается, а в качестве критерия отбора новых элементов можно использовать последний считанный ID:

using (var context = new CCYCEntities())
            {
                var nNu = new Engine
                {
                    Comment = "From Wrapper",
                    ID_Engine = 1,
                    NameEngine = "My Engine",
                    SpecificImpulse = 114,
                    FuelAmount = 123,
                    MaxTimeOfWorking = 123,
                    Trust = 1,
                    TypeOfEngine = "www"
                };
                var nNut = new Engine
                {
                    Comment = "From Wrapper",
                    ID_Engine = 2,
                    NameEngine = "My Engine",
                    SpecificImpulse = 114,
                    FuelAmount = 123,
                    MaxTimeOfWorking = 123,
                    Trust = 1,
                    TypeOfEngine = "www"
                };
                try
                {
                     NewEngines = (from d in context.Engines where d.ID_Engine > OldId select d);
                     var ctn = NewEngines.Count(); //число новых записей
                }
                catch (DataException exception)
                {
                     MessageBox.Show(exception.Message);
                }
            }
На этом все, в следующей части я расскажу о том как применить это для асинхронной проверки БД.
Полезные ссылки:  
1.DbContex (MSDN Magazine)
2.Local(MSDN)
3.More information (MSDN Magazine)

Комментариев нет:

Отправить комментарий