http://damienbod.com/2013/11/18/using-sqlite-with-entity-framework-6-and-the-repository-pattern/


USING SQLITE WITH ENTITY FRAMEWORK 6 AND THE REPOSITORY PATTERN


In this post, a Data access layer is implemented using the repository pattern together with entity Framework 6 and SQLite as a database. The post continues on from Getting started, using SQLite with .NET

UPDATE 02.03.2014: Using the new SQLite version 1.0.91.3 now with the SQLite provider. The xml app configuration needs to be changed due to this. Updated all other NuGet packages as well.

UPDATE 02.07.2014: Using the new SQLite version 1.0.93.0 now with the SQLite provider. The xml app configuration needs to be changed due to this. Updated all other NuGet packages as well.

Code: https://github.com/damienbod/AnimalMarketEnginsSqliteDb

A SQLite database can be created using the Firefox addon.

1
2
3
4
5
6
CREATE TABLE "AnimalType"
(
 "Id" INTEGER PRIMARY KEY  AUTOINCREMENT  NOT NULL  UNIQUE ,
 "Name" TEXT NOT NULL  DEFAULT Unknown,
 "MeanCost" DOUBLE NOT NULL  DEFAULT 100.0
)
1
2
3
4
5
6
7
8
9
10
CREATE TABLE "EventData"
(
 "EventDataId" INTEGER PRIMARY KEY  NOT NULL ,
 "Factor" int NOT NULL ,
 "StringTestId" NVARCHAR(160) NOT NULL ,
 "FixChange" DOUBLE NOT NULL  DEFAULT (0.0) ,
 "AnimalTypeId" INTEGER NOT NULL ,
 FOREIGN KEY ([AnimalTypeId]) REFERENCES [AnimalType] ([Id])
   ON DELETE NO ACTION ON UPDATE NO ACTION
)

Application data can then be added directly using this tool:
sqlite01

The .sqlite file is added to the project and ‘copied if newer’ when built.
sqlite02


OLD no longer required, but still works
The required NuGet packages need to be added. (At present the Sysem.Data.SQLite.Linq can only be got from https://www.myget.org/F/bricelam/ or you have to build the src itself.)
See http://brice-lambson.blogspot.ch/2013/06/systemdatasqlite-on-entity-framework-6.html

UPDATE: A new version of SQLite 1.0.91.3 has been released (available in NuGet, Version now works with the latest version of Entity Framework 6.0.2).
SqliteEF6_02

And you also require Entity Framework version 6.0.2.
SqliteEF6

A domain model can then be created. In this example it is a very simple 1:n model with just 2 tables.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
using System.Collections.Generic;
 
namespace AnimalMarketDal.DomainModel
{
    public class AnimalType
    {
        public AnimalType()
        {
            EventDataValues = new List<EventData>();
        }
  
        public long Id { get; set; }
        public string Name { get; set; }
        public double MeanCost { get; set; }
         
        public virtual ICollection<EventData> EventDataValues { get; set; }
    }
}
 
namespace AnimalMarketDal.DomainModel
{
    public class EventData
    {
        public long EventDataId { get; set; }
        public int Factor { get; set; }
        public string StringTestId { get; set; }
        public double FixChange { get; set; }
 
        public long AnimalTypeId { get; set; }
        public virtual AnimalType AnimalType { get; set; }
    }
}

Now create a DbContext. It is important to turn off the migrations as this is not code first.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;
using AnimalMarketDal.DomainModel;
 
namespace AnimalMarketDal.Dal
{
    public class AnimalContext  : DbContext
    {
        public AnimalContext()
        {
            // Turn off the Migrations, (NOT a code first Db)
            Database.SetInitializer<AnimalContext>(null);
        }
 
        public DbSet<AnimalType> AnimalTypes { get; set; }
        public DbSet<EventData> EventDataValues { get; set; }
 
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            // Database does not pluralize table names
            modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
        }
     
    }
}

The repository layer is then created. The context is added using Unity and construction injection with a transient lifecycle. This needs to be disposed, so the IDisposable is also implemented.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
using System;
using System.Collections.Generic;
using AnimalMarketDal.DomainModel;
 
namespace AnimalMarketDal.Repo
{
    public interface IEventDataRepository : IDisposable
    {
        AnimalType GetCowType();
 
        AnimalType GetPigType();
 
        AnimalType GetLambType();
 
        IEnumerable<EventData> GetPigData();
 
        IEnumerable<EventData> GetLambData();
 
        IEnumerable<EventData> GetCalfData();
    }
}

And the implementation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
using System;
using System.Collections.Generic;
using System.Linq;
using AnimalMarketCommon.Attributes;
using AnimalMarketDal.Dal;
using AnimalMarketDal.DomainModel;
 
namespace AnimalMarketDal.Repo
{
    [LifecycleTransient]
    public class EventDataRepository : IEventDataRepository
    {
        private const int PigId = 1;
        private const int CalfId = 2;
        private const int LambId = 0;
 
        private readonly AnimalContext _context;
 
        public EventDataRepository(AnimalContext context)
        {
            _context = context;
        }
 
        private IEnumerable<AnimalType> GetAnimalTypes()
        {
            return from a in _context.AnimalTypes select a;
        }
 
        public AnimalType GetCowType()
        {
            return GetAnimalTypes().FirstOrDefault(a => a.Id.Equals(CalfId));
        }
 
        public AnimalType GetPigType()
        {
            return GetAnimalTypes().FirstOrDefault(a => a.Id.Equals(PigId));
        }
 
        public AnimalType GetLambType()
        {
            return GetAnimalTypes().FirstOrDefault(a => a.Id.Equals(LambId));
        }
 
        public IEnumerable<EventData> GetPigData()
        {
            return from a in _context.EventDataValues
                   where a.AnimalTypeId.Equals(PigId)
                          orderby a.StringTestId
                          select a;
        }
 
        public IEnumerable<EventData> GetLambData()
        {
            return from a in _context.EventDataValues
                   where a.AnimalTypeId.Equals(LambId)
                   orderby a.StringTestId
                   select a;
        }
 
        public IEnumerable<EventData> GetCalfData()
        {
            return from a in _context.EventDataValues
                   where a.AnimalTypeId.Equals(CalfId)
                   orderby a.StringTestId
                   select a;
        }
 
        public void SaveChanges()
        {
            _context.SaveChanges();
        }
 
        private bool disposed = false;
 
        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    _context.Dispose();
                }
            }
            this.disposed = true;
        }
 
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    }
}

In this example attributes are used to define the different classes which are to be created and its defined lifecycle; singleton = LifecycleSingletonAttribute and transient = LifecycleTransientAttribute. The AnimalContext is registered as a transient type.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static void RegisterTypes(IUnityContainer container)
        {
            // Add your register logic here...
            var myAssemblies = AppDomain.CurrentDomain.GetAssemblies().Where(a => a.FullName.StartsWith("AnimalMarket")).ToArray();
 
            container.RegisterType(typeof(Startup));
 
            container.RegisterTypes(
                 UnityHelpers.GetTypesWithCustomAttribute<LifecycleSingletonAttribute>(myAssemblies),
                 WithMappings.FromMatchingInterface,
                 WithName.Default,
                 WithLifetime.ContainerControlled,
                 null
                ).RegisterTypes(
                        UnityHelpers.GetTypesWithCustomAttribute<LifecycleTransientAttribute>(myAssemblies),
                        WithMappings.FromMatchingInterface,
                        WithName.Default,
                        WithLifetime.Transient);
 
            container.RegisterType(typeof(AnimalContext));
        }

Reflection is used to search for the attributes.

1
2
3
4
5
6
7
8
9
10
11
12
13
public static IEnumerable<Type> GetTypesWithCustomAttribute<T>( Assembly[] assemblies)
{
    foreach (var assembly in assemblies)
    {
        foreach (Type type in assembly.GetTypes())
        {
            if (type.GetCustomAttributes(typeof(T), true).Length > 0)
            {
                yield return type;
            }
        }
    }
}

The Entity SQLite DbProviderFactories is defined in the application config file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
    <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
  </configSections>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
   
 <entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
    <providers>
      <provider invariantName="System.Data.SQLite" type="System.Data.SQLite.EF6.SQLiteProviderServices, System.Data.SQLite.EF6, Version=1.0.93.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139" />
      <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
      <provider invariantName="System.Data.SQLite.EF6" type="System.Data.SQLite.EF6.SQLiteProviderServices, System.Data.SQLite.EF6" />
    </providers>
  </entityFramework>
  <connectionStrings>
    <add name="AnimalContext" connectionString="Data Source=.\animals.sqlite" providerName="System.Data.SQLite" />
  </connectionStrings>
 
  <system.data>
    <DbProviderFactories>
      <remove invariant="System.Data.SQLite" />
      <add name="SQLite Data Provider" invariant="System.Data.SQLite" description=".Net Framework Data Provider for SQLite" type="System.Data.SQLite.SQLiteFactory, System.Data.SQLite" />
      <remove invariant="System.Data.SQLite.EF6" />
      <add name="SQLite Data Provider (Entity Framework 6)" invariant="System.Data.SQLite.EF6" description=".Net Framework Data Provider for SQLite (Entity Framework 6)" type="System.Data.SQLite.EF6.SQLiteProviderFactory, System.Data.SQLite.EF6" />
    </DbProviderFactories>
  </system.data>
 
   
</configuration>

The Web Api can then be tested using Fiddler. Here’s an example of the results:
sqlite04

As you can see it is very easy to create a data access layer with SQLite as the database.

Links:
http://system.data.sqlite.org

https://addons.mozilla.org/de/firefox/addon/sqlite-manager/

http://geekswithblogs.net/danielggarcia/archive/2013/12/22/portable-databases-ii-using-sqlite-with-entity-framework.aspx



Entity Framework - Model First 작성 샘플

https://msdn.microsoft.com/ko-kr/data/jj205424


이 비디오와 단계별 연습에서는 Entity Framework를 사용한 Model First 개발에 대해 소개합니다. Model First는 Entity Framework 디자이너를 사용하여 새 모델을 만든 다음 모델에서 데이터베이스 스키마를 생성할 수 있습니다. 모델은 EDMX 파일(.edmx 확장명)로 저장되며 Entity Framework 디자이너에서 확인하고 편집할 수 있습니다. EDMX 파일에서 응용 프로그램과 상호 작용할 클래스가 자동으로 생성됩니다.

추가 비디오 옵션 - 다운로드 포함)

 

필수 구성 요소

이 연습을 완료하려면 Visual Studio 2010 또는 Visual Studio 2012가 설치되어 있어야 합니다.

Visual Studio 2010을 사용하는 경우 NuGet도 설치해야 합니다.

 

1. 응용 프로그램 만들기

간단한 예제 프로그램으로 Model First를 사용하여 데이터 액세스를 수행하는 기본 콘솔 응용 프로그램을 빌드해 보겠습니다.

  • Visual Studio를 엽니다.
  • 파일 -> 새로 만들기 -> 프로젝트...
  • 왼쪽 메뉴에서 Windows를 선택하고 콘솔 응용 프로그램을 선택합니다.
  • 이름으로 ModelFirstSample을 입력합니다.
  • 확인을 선택합니다.

 

2. 모델 만들기

Visual Studio에 포함되어 있는 Entity Framework 디자이너를 사용하여 모델을 만들겠습니다.

  • 프로젝트 -> 새 항목 추가…
  • 왼쪽 메뉴에서 데이터 및 ADO.NET 엔터티 데이터 모델을 선택합니다.
  • 이름으로 BloggingModel을 입력한 다음 확인을 클릭하면 엔터티 데이터 모델 마법사가 시작됩니다.
  • 빈 모델을 선택하고 마침을 클릭합니다.
    빈 모델이 선택되어 있는 엔터티 데이터 모델 마법사

 

Entity Framework 디자이너가 실행되어 빈 모델을 표시합니다. 이제 모델에 엔터티와 속성, 연결을 추가할 수 있습니다.

  • 디자인 화면에서 마우스 오른쪽 단추를 클릭하고 속성을 선택합니다.
  • 속성 창에서 엔터티 컨테이너 이름을 BloggingContext로 변경합니다.
    생성될 파생 컨텍스트의 이름으로, 컨텍스트는 데이터를 쿼리하고 저장할 수 있는 데이터베이스의 세션을 나타냅니다.
  • 디자인 화면에서 마우스 오른쪽 단추를 클릭하고 새 항목 추가 -> 엔터티…를 선택합니다.
  • 엔터티 이름과 키 이름으로 각각 Blog 및 BlogId를 입력하고 확인을 클릭합니다.
    엔터티 이름과 키 속성 이름이 입력된 엔터티 추가 화면
  • 디자인 화면에서 새 엔터티를 마우스 오른쪽 단추로 클릭한 다음 새로 추가 -> 스칼라 속성을 선택하고 속성의 이름으로 Name을 입력합니다.
  • 이 과정을 반복하여 Url 속성도 추가합니다.
  • 디자인 화면의 Url 속성을 마우스 오른쪽 단추로 클릭한 다음 속성을 선택하고 속성 창에서 Null 허용 설정을 True로 변경합니다.
    그러면 Url을 지정하지 않고 데이터베이스에 Blog를 저장할 수 있습니다.
  • 같은 방법으로 PostId 키 속성을 가진 Post 엔터티도 추가합니다.
  • Post 엔터티에 Title 및 Content 스칼라 속성을 추가합니다.

 

이제 몇 가지 엔터티가 생성되었으므로 엔터티 간 연결(또는 관계)을 추가하겠습니다.

  • 디자인 화면에서 마우스 오른쪽 단추를 클릭하고 새 항목 추가 -> 연결…을 선택합니다.
  • 관계의 한쪽 End는 복합성이 1인 Blog를 가리키고, 다른 End는 복합성이 인 Post를 가리키게 합니다.
    이는 한 Blog는 여러 Post를 가지며, 각 Post는 한 Blog에 속함을 의미합니다.
  • 'Post' 엔터티에 외래 키 속성 추가 상자가 선택되어 있는지 확인하고 확인을 클릭합니다.
    Blog 및 Post 간 연결이 완료된 연결 추가 화면

 

이제 데이터베이스를 생성하여 데이터를 읽고 쓸 수 있는 간단한 모델이 만들어졌습니다.
모델이 표시된 Entity Framework 디자이너

 

Visual Studio 2010의 추가 단계

Visual Studio 2010으로 작업할 경우 최신 버전의 Entity Framework로 업그레이드하기 위해 몇 가지 추가 단계를 수행해야 합니다. 최신 버그 수정을 포함하여 더 쉽게 사용할 수 있는 향상된 API 화면에 액세스할 수 있으므로 업그레이드하는 것이 좋습니다.

먼저 NuGet에서 Entity Framework의 최신 버전을 가져와야 합니다.

  • 프로젝트 –> NuGet 패키지 관리...
    NuGet 패키지 관리... 옵션이 표시되지 않으면 최신 버전의 NuGet을 설치해야 합니다.
  • 온라인 탭을 선택합니다.
  • EntityFramework 패키지를 선택합니다.
  • 설치를 클릭합니다.

그런 다음 모델을 교체하여 최신 버전의 Entity Framework에 도입된 DbContext API를 사용할 수 있도록 코드를 생성해야 합니다.

  • EF 디자이너에서 모델의 빈 곳을 마우스 오른쪽 단추로 클릭하고 코드 생성 항목 추가...를 선택합니다.
  • 왼쪽 메뉴에서 온라인 템플릿을 선택하고 DbContext를 검색합니다.
  • EF 5.x DbContext Generator for C#을 선택하고 이름으로 BloggingModel을 입력한 후 추가를 클릭합니다.
    EF 5.x DbContext Generator for C#이 선택된 코드 생성 항목 추가 대화 상자

 

3. 데이터베이스 생성

이 모델에서는 Entity Framework가 모델을 사용하여 데이터를 저장하고 검색할 수 있는 데이터베이스 스키마를 계산할 수 있습니다.

Visual Studio 버전에 따라 Visual Studio에서 설치하는 데이터베이스 서버가 달라집니다.

  • Visual Studio 2010을 사용할 경우 SQL Express 데이터베이스를 만듭니다.
  • Visual Studio 2012를 사용할 경우 LocalDb 데이터베이스를 만듭니다.

 

이제 데이터베이스를 만들겠습니다.

  • 디자인 화면에서 마우스 오른쪽 단추를 클릭하고 모델에서 데이터베이스 생성…을 선택합니다.
  • 새 연결...을 클릭하고 사용하는 Visual Studio의 버전에 따라 LocalDb((localdb)\v11.0) 또는 SQL Express(.\SQLEXPRESS) 중 하나를 지정한 후 데이터베이스 이름으로 ModelFirst.Blogging을 입력합니다.
    (localdb)\v11.0의 ModelFirst.Blogging 데이터베이스에 연결.\SQLEXPRESS의 ModelFirst.Blogging 데이터베이스에 연결
  • 확인을 선택한 후 새 데이터베이스를 만들 것인지 확인하는 메시지가 표시되면 를 선택합니다.
  • 다음을 선택하면 Entity Framework Designer 디자이너가 데이터베이스 스키마를 만들 스크립트를 계산합니다.
  • 스크립트가 표시되고 완료를 클릭하면 프로젝트에 추가된 스크립트가 열립니다.
  • 스크립트를 마우스 오른쪽 단추로 클릭하고 실행을 클릭하면 연결할 데이터베이스를 지정하라는 메시지가 표시됩니다. 사용 중인 Visual Studio 버전에 따라 (localdb)\v11.0 또는 .\SQLEXPRESS를 지정합니다.

 

4. 데이터 읽기 및 쓰기

이제 생성된 모델을 사용하여 몇 가지 데이터에 액세스해 보겠습니다. EDMX 파일에 기반하여 데이터에 액세스하는 데 사용할 클래스가 자동 생성됩니다.

이 스크린 샷은 Visual Studio 2012 버전을 사용하며, Visual Studio 2010을 사용할 경우 BloggingModel.tt 및 BloggingModel.Context.tt 파일이 EDMX 파일 아래 중첩되는 대신 프로젝트 아래 바로 표시됩니다.

확장되어 생성된 코드 파일을 표시하는 솔루션 탐색기 

 

아래와 같이 Program.cs에서 Main 메서드를 구현합니다. 이 코드는 컨텍스트에 대한 새 인스턴스를 만들고 이를 사용해서 새 블로그를 삽입합니다. 그런 후 LINQ 쿼리를 사용해서 제목별로 알파벳순으로 정렬된 모든 블로그를 데이터베이스에서 검색합니다.

class Program 

    static void Main(string[] args) 
    { 
        using (var db = new BloggingContext()) 
        { 
            // Create and save a new Blog 
            Console.Write("Enter a name for a new Blog: "); 
            var name = Console.ReadLine(); 
 
            var blog = new Blog { Name = name }; 
            db.Blogs.Add(blog); 
            db.SaveChanges(); 
 
            // Display all Blogs from the database 
            var query = from b in db.Blogs 
                        orderby b.Name 
                        select b; 
 
            Console.WriteLine("All blogs in the database:"); 
            foreach (var item in query) 
            { 
                Console.WriteLine(item.Name); 
            } 
 
            Console.WriteLine("Press any key to exit..."); 
            Console.ReadKey(); 
        } 
    } 
}

이제 응용 프로그램을 실행하고 테스트할 수 있습니다.

Enter a name for a new Blog: ADO.NET Blog
All blogs in the database:
ADO.NET 블로그
Press any key to exit...

 

5. 모델 변경 사항 처리

이제 모델을 변경해 보겠습니다. 이러한 변경 사항을 반영하려면 데이터베이스 스키마를 업데이트해야 합니다.

모델에 새 User 엔터티를 추가해 보겠습니다.

  • Username 키 이름의 새 User 엔터티 이름을 추가하고 키의 속성 유형으로 문자열을 지정합니다.
    User 엔터티 입력이 완료된 엔터티 추가 대화 상자
  • 디자인 화면의 Username 속성을 마우스 오른쪽 단추로 클릭한 다음 속성을 선택하고 속성 창에서 MaxLength 설정을 50으로 변경합니다.
    이렇게 하면 Username에 저장할 수 있는 데이터가 50자로 제한됩니다.
  • User 엔터티에 DisplayName 스칼라 속성을 추가합니다.

 

이제 모델이 업데이트되어 새 User 엔터티 형식에 맞게 데이터베이스를 업데이트할 수 있습니다.

  • 디자인 화면에서 마우스 오른쪽 단추를 클릭하고 모델에서 데이터베이스 생성...을 선택하면 Entity Framework가 업데이트된 모델을 바탕으로 스키마를 다시 만들 스크립트를 계산합니다.
  • 마침을 클릭합니다.
  • 기존 DDL 스크립트와 모델의 매핑 및 저장소 부분을 덮어쓴다는 경고 메시지가 표시되면 두 경고에 대해 모두 를 클릭합니다.
  • 데이터베이스를 만들 업데이트된 SQL 스크립트가 열립니다.
    생성된 스크립트는 기존의 모든 테이블을 삭제하고 처음부터 다시 스키마를 만듭니다. 로컬 개발과 달리 이미 배포된 데이터베이스에는 변경 사항을 바로 적용할 수 없습니다. 이미 배포된 데이터베이스에 변경 사항을 게시해야 할 경우 스크립트를 편집하거나 스키마 비교 도구를 사용하여 마이그레이션 스크립트를 계산해야 합니다.
  • 스크립트를 마우스 오른쪽 단추로 클릭하고 실행을 클릭하면 연결할 데이터베이스를 지정하라는 메시지가 표시됩니다. 사용 중인 Visual Studio 버전에 따라 (localdb)\v11.0 또는 .\SQLEXPRESS를 지정합니다.

 

요약

이 연습에서는 EF 디자이너에서 모델을 만든 다음 그 모델에서 데이터베이스를 만드는 Model First 개발에 대해 알아 보았습니다. 또한 모델을 사용하여 데이터베이스에서 몇 가지 데이터를 읽고 써 본 다음, 모델을 업데이트한 후 새 모델에 맞게 데이터베이스 스키마를 다시 만들어 보았습니다.



[WPF, SQLite, Entity Framework] - USING SQLITE WITH ENTITY FRAMEWORK 6 AND THE REPOSITORY PATTERN

http://damienbod.com/2013/11/18/using-sqlite-with-entity-framework-6-and-the-repository-pattern/


--------------


USING SQLITE WITH ENTITY FRAMEWORK 6 AND THE REPOSITORY PATTERN

In this post, a Data access layer is implemented using the repository pattern together with entity Framework 6 and SQLite as a database. The post continues on from Getting started, using SQLite with .NET

UPDATE 02.03.2014: Using the new SQLite version 1.0.91.3 now with the SQLite provider. The xml app configuration needs to be changed due to this. Updated all other NuGet packages as well.

UPDATE 02.07.2014: Using the new SQLite version 1.0.93.0 now with the SQLite provider. The xml app configuration needs to be changed due to this. Updated all other NuGet packages as well.

Code: https://github.com/damienbod/AnimalMarketEnginsSqliteDb

A SQLite database can be created using the Firefox addon.

1
2
3
4
5
6
CREATE TABLE "AnimalType"
(
 "Id" INTEGER PRIMARY KEY  AUTOINCREMENT  NOT NULL  UNIQUE ,
 "Name" TEXT NOT NULL  DEFAULT Unknown,
 "MeanCost" DOUBLE NOT NULL  DEFAULT 100.0
)
1
2
3
4
5
6
7
8
9
10
CREATE TABLE "EventData"
(
 "EventDataId" INTEGER PRIMARY KEY  NOT NULL ,
 "Factor" int NOT NULL ,
 "StringTestId" NVARCHAR(160) NOT NULL ,
 "FixChange" DOUBLE NOT NULL  DEFAULT (0.0) ,
 "AnimalTypeId" INTEGER NOT NULL ,
 FOREIGN KEY ([AnimalTypeId]) REFERENCES [AnimalType] ([Id])
   ON DELETE NO ACTION ON UPDATE NO ACTION
)

Application data can then be added directly using this tool:
sqlite01

The .sqlite file is added to the project and ‘copied if newer’ when built.
sqlite02


OLD no longer required, but still works
The required NuGet packages need to be added. (At present the Sysem.Data.SQLite.Linq can only be got from https://www.myget.org/F/bricelam/ or you have to build the src itself.)
See http://brice-lambson.blogspot.ch/2013/06/systemdatasqlite-on-entity-framework-6.html

UPDATE: A new version of SQLite 1.0.91.3 has been released (available in NuGet, Version now works with the latest version of Entity Framework 6.0.2).
SqliteEF6_02

And you also require Entity Framework version 6.0.2.
SqliteEF6

A domain model can then be created. In this example it is a very simple 1:n model with just 2 tables.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
using System.Collections.Generic;
 
namespace AnimalMarketDal.DomainModel
{
    public class AnimalType
    {
        public AnimalType()
        {
            EventDataValues = new List<EventData>();
        }
  
        public long Id { get; set; }
        public string Name { get; set; }
        public double MeanCost { get; set; }
         
        public virtual ICollection<EventData> EventDataValues { get; set; }
    }
}
 
namespace AnimalMarketDal.DomainModel
{
    public class EventData
    {
        public long EventDataId { get; set; }
        public int Factor { get; set; }
        public string StringTestId { get; set; }
        public double FixChange { get; set; }
 
        public long AnimalTypeId { get; set; }
        public virtual AnimalType AnimalType { get; set; }
    }
}

Now create a DbContext. It is important to turn off the migrations as this is not code first.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;
using AnimalMarketDal.DomainModel;
 
namespace AnimalMarketDal.Dal
{
    public class AnimalContext  : DbContext
    {
        public AnimalContext()
        {
            // Turn off the Migrations, (NOT a code first Db)
            Database.SetInitializer<AnimalContext>(null);
        }
 
        public DbSet<AnimalType> AnimalTypes { get; set; }
        public DbSet<EventData> EventDataValues { get; set; }
 
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            // Database does not pluralize table names
            modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
        }
     
    }
}

The repository layer is then created. The context is added using Unity and construction injection with a transient lifecycle. This needs to be disposed, so the IDisposable is also implemented.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
using System;
using System.Collections.Generic;
using AnimalMarketDal.DomainModel;
 
namespace AnimalMarketDal.Repo
{
    public interface IEventDataRepository : IDisposable
    {
        AnimalType GetCowType();
 
        AnimalType GetPigType();
 
        AnimalType GetLambType();
 
        IEnumerable<EventData> GetPigData();
 
        IEnumerable<EventData> GetLambData();
 
        IEnumerable<EventData> GetCalfData();
    }
}

And the implementation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
using System;
using System.Collections.Generic;
using System.Linq;
using AnimalMarketCommon.Attributes;
using AnimalMarketDal.Dal;
using AnimalMarketDal.DomainModel;
 
namespace AnimalMarketDal.Repo
{
    [LifecycleTransient]
    public class EventDataRepository : IEventDataRepository
    {
        private const int PigId = 1;
        private const int CalfId = 2;
        private const int LambId = 0;
 
        private readonly AnimalContext _context;
 
        public EventDataRepository(AnimalContext context)
        {
            _context = context;
        }
 
        private IEnumerable<AnimalType> GetAnimalTypes()
        {
            return from a in _context.AnimalTypes select a;
        }
 
        public AnimalType GetCowType()
        {
            return GetAnimalTypes().FirstOrDefault(a => a.Id.Equals(CalfId));
        }
 
        public AnimalType GetPigType()
        {
            return GetAnimalTypes().FirstOrDefault(a => a.Id.Equals(PigId));
        }
 
        public AnimalType GetLambType()
        {
            return GetAnimalTypes().FirstOrDefault(a => a.Id.Equals(LambId));
        }
 
        public IEnumerable<EventData> GetPigData()
        {
            return from a in _context.EventDataValues
                   where a.AnimalTypeId.Equals(PigId)
                          orderby a.StringTestId
                          select a;
        }
 
        public IEnumerable<EventData> GetLambData()
        {
            return from a in _context.EventDataValues
                   where a.AnimalTypeId.Equals(LambId)
                   orderby a.StringTestId
                   select a;
        }
 
        public IEnumerable<EventData> GetCalfData()
        {
            return from a in _context.EventDataValues
                   where a.AnimalTypeId.Equals(CalfId)
                   orderby a.StringTestId
                   select a;
        }
 
        public void SaveChanges()
        {
            _context.SaveChanges();
        }
 
        private bool disposed = false;
 
        protected virtual void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    _context.Dispose();
                }
            }
            this.disposed = true;
        }
 
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    }
}

In this example attributes are used to define the different classes which are to be created and its defined lifecycle; singleton = LifecycleSingletonAttribute and transient = LifecycleTransientAttribute. The AnimalContext is registered as a transient type.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static void RegisterTypes(IUnityContainer container)
        {
            // Add your register logic here...
            var myAssemblies = AppDomain.CurrentDomain.GetAssemblies().Where(a => a.FullName.StartsWith("AnimalMarket")).ToArray();
 
            container.RegisterType(typeof(Startup));
 
            container.RegisterTypes(
                 UnityHelpers.GetTypesWithCustomAttribute<LifecycleSingletonAttribute>(myAssemblies),
                 WithMappings.FromMatchingInterface,
                 WithName.Default,
                 WithLifetime.ContainerControlled,
                 null
                ).RegisterTypes(
                        UnityHelpers.GetTypesWithCustomAttribute<LifecycleTransientAttribute>(myAssemblies),
                        WithMappings.FromMatchingInterface,
                        WithName.Default,
                        WithLifetime.Transient);
 
            container.RegisterType(typeof(AnimalContext));
        }

Reflection is used to search for the attributes.

1
2
3
4
5
6
7
8
9
10
11
12
13
public static IEnumerable<Type> GetTypesWithCustomAttribute<T>( Assembly[] assemblies)
{
    foreach (var assembly in assemblies)
    {
        foreach (Type type in assembly.GetTypes())
        {
            if (type.GetCustomAttributes(typeof(T), true).Length > 0)
            {
                yield return type;
            }
        }
    }
}

The Entity SQLite DbProviderFactories is defined in the application config file.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
    <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
  </configSections>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
  </startup>
   
 <entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
    <providers>
      <provider invariantName="System.Data.SQLite" type="System.Data.SQLite.EF6.SQLiteProviderServices, System.Data.SQLite.EF6, Version=1.0.93.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139" />
      <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
      <provider invariantName="System.Data.SQLite.EF6" type="System.Data.SQLite.EF6.SQLiteProviderServices, System.Data.SQLite.EF6" />
    </providers>
  </entityFramework>
  <connectionStrings>
    <add name="AnimalContext" connectionString="Data Source=.\animals.sqlite" providerName="System.Data.SQLite" />
  </connectionStrings>
 
  <system.data>
    <DbProviderFactories>
      <remove invariant="System.Data.SQLite" />
      <add name="SQLite Data Provider" invariant="System.Data.SQLite" description=".Net Framework Data Provider for SQLite" type="System.Data.SQLite.SQLiteFactory, System.Data.SQLite" />
      <remove invariant="System.Data.SQLite.EF6" />
      <add name="SQLite Data Provider (Entity Framework 6)" invariant="System.Data.SQLite.EF6" description=".Net Framework Data Provider for SQLite (Entity Framework 6)" type="System.Data.SQLite.EF6.SQLiteProviderFactory, System.Data.SQLite.EF6" />
    </DbProviderFactories>
  </system.data>
 
   
</configuration>

The Web Api can then be tested using Fiddler. Here’s an example of the results:
sqlite04

As you can see it is very easy to create a data access layer with SQLite as the database.

Links:
http://system.data.sqlite.org

https://addons.mozilla.org/de/firefox/addon/sqlite-manager/

http://geekswithblogs.net/danielggarcia/archive/2013/12/22/portable-databases-ii-using-sqlite-with-entity-framework.aspx


Databinding with WPF

https://msdn.microsoft.com/ko-kr/data/jj574514




----------------------------------------------------------


Databinding with WPF

This step-by-step walkthrough shows how to bind POCO types to WPF controls in a “master-detail" form. The application uses the Entity Framework APIs to populate objects with data from the database, track changes, and persist data to the database.

The model defines two types that participate in one-to-many relationship: Category (principal\master) and Product (dependent\detail). Then, the Visual Studio tools are used to bind the types defined in the model to the WPF controls. The WPF data-binding framework enables navigation between related objects: selecting rows in the master view causes the detail view to update with the corresponding child data.

The screen shots and code listings in this walkthrough are taken from Visual Studio 2013 but you can complete this walkthrough with Visual Studio 2012 or Visual Studio 2010.

 

Use the 'Object' Option for Creating WPF Data Sources

With previous version of Entity Framework we used to recommend using the Database option when creating a new Data Source based on a model created with the EF Designer. This was because the designer would generate a context that derived from ObjectContext and entity classes that derived from EntityObject. Using the Database option would help you write the best code for interacting with this API surface.

The EF Designers for Visual Studio 2012 and Visual Studio 2013 generate a context that derives from DbContext together with simple POCO entity classes. With Visual Studio 2010 we recommend swapping to a code generation template that uses DbContext as described later in this walkthrough.

When using the DbContext API surface you should use the Object option when creating a new Data Source, as shown in this walkthrough.

If needed, you can revert to ObjectContext based code generation for models created with the EF Designer.

 

Pre-Requisites

You need to have Visual Studio 2013, Visual Studio 2012 or Visual Studio 2010 installed to complete this walkthrough.

If you are using Visual Studio 2010, you also have to install NuGet. For more information, see Installing NuGet.  

 

Create the Application

  • Open Visual Studio
  • File -> New -> Project….
  • Select Windows in the left pane and WPFApplication in the right pane
  • Enter WPFwithEFSample as the name
  • Select OK

 

Install the Entity Framework NuGet package

  • In Solution Explorer, right-click on the WinFormswithEFSample project
  • Select Manage NuGet Packages…
  • In the Manage NuGet Packages dialog, Select the Online tab and choose the EntityFramework package
  • Click Install
    Note: In addition to the EntityFramework assembly a reference to System.ComponentModel.DataAnnotations is also added. If the project has a reference to System.Data.Entity, then it will be removed when the EntityFramework package is installed. The System.Data.Entity assembly is no longer used for Entity Framework 6 applications.

 

Define a Model

In this walkthrough you can chose to implement a model using Code First or the EF Designer. Complete one of the two following sections.

Option 1: Define a Model using Code First

This section shows how to create a model and its associated database using Code First. Skip to the next section (Option 2: Define a model using Database First) if you would rather use Database First to reverse engineer your model from a database using the EF designer

When using Code First development you usually begin by writing .NET Framework classes that define your conceptual (domain) model.

  • Add a new class to the WPFwithEFSample:
    • Right-click on the project name
    • Select Add, then New Item
    • Select Class and enter Product for the class name
  • Replace the Product class definition with the following code:
namespace WPFwithEFSample 

    public class Product 
    { 
        public int ProductId { get; set; } 
        public string Name { get; set; } 
 
        public int CategoryId { get; set; } 
        public virtual Category Category { get; set; } 
    } 
}
  • Add a Category class with the following definition:
using System.Collections.ObjectModel; 
 
namespace WPFwithEFSample 

    public class Category 
    { 
        public Category() 
        { 
            this.Products = new ObservableCollection<Product>(); 
        } 
  
        public int CategoryId { get; set; } 
        public string Name { get; set; } 
 
        public virtual ObservableCollection<Product> Products { get; private set; } 
    } 
}

The Products property on the Category class and Category property on the Product class are navigation properties. In Entity Framework, navigation properties provide a way to navigate a relationship between two entity types.

In addition to defining entities, you need to define a class that derives from DbContext and exposes DbSet<TEntity> properties. The DbSet<TEntity> properties let the context know which types you want to include in the model.

An instance of the DbContext derived type manages the entity objects during run time, which includes populating objects with data from a database, change tracking, and persisting data to the database.

  • Add a new ProductContext class to the project with the following definition:
using System.Data.Entity; 
 
namespace WPFwithEFSample 

    public class ProductContext : DbContext 
    { 
        public DbSet<Category> Categories { get; set; } 
        public DbSet<Product> Products { get; set; } 
    } 
}

Compile the project.

Option 2: Define a model using Database First

This section shows how to use Database First to reverse engineer your model from a database using the EF designer. If you completed the previous section (Option 1: Define a model using Code First), then skip this section and go straight to the Lazy Loading section.

Create an Existing Database

Typically when you are targeting an existing database it will already be created, but for this walkthrough we need to create a database to access.

The database server that is installed with Visual Studio is different depending on the version of Visual Studio you have installed:

  • If you are using Visual Studio 2010 you'll be creating a SQL Express database.
  • If you are using Visual Studio 2012 then you'll be creating a LocalDb database.

 

Let's go ahead and generate the database.

  • View -> Server Explorer
  • Right click on Data Connections -> Add Connection…
  • If you haven’t connected to a database from Server Explorer before you’ll need to select Microsoft SQL Server as the data source
    Select 'Microsoft SQL Server' as the data source
  • Connect to either LocalDb ((localdb)\v11.0) or SQL Express (.\SQLEXPRESS), depending on which one you have installed, and enter Products as the database name
    Connect to DatabaseFirst.Blogging database on (localdb)\v11.0Connect to DatabaseFirst.Blogging database on .\SQLEXPRESS
  • Select OK and you will be asked if you want to create a new database, select Yes
    Dialog asking if a new database should be created
  • The new database will now appear in Server Explorer, right-click on it and select New Query
  • Copy the following SQL into the new query, then right-click on the query and select Execute
CREATE TABLE [dbo].[Categories] ( 
    [CategoryId] [int] NOT NULL IDENTITY, 
    [Name] [nvarchar](max), 
    CONSTRAINT [PK_dbo.Categories] PRIMARY KEY ([CategoryId]) 

 
CREATE TABLE [dbo].[Products] ( 
    [ProductId] [int] NOT NULL IDENTITY, 
    [Name] [nvarchar](max), 
    [CategoryId] [int] NOT NULL, 
    CONSTRAINT [PK_dbo.Products] PRIMARY KEY ([ProductId]) 

 
CREATE INDEX [IX_CategoryId] ON [dbo].[Products]([CategoryId]) 
 
ALTER TABLE [dbo].[Products] ADD CONSTRAINT [FK_dbo.Products_dbo.Categories_CategoryId] FOREIGN KEY ([CategoryId]) REFERENCES [dbo].[Categories] ([CategoryId]) ON DELETE CASCADE

Reverse Engineer Model

We’re going to make use of Entity Framework Designer, which is included as part of Visual Studio, to create our model.

  • Project -> Add New Item…
  • Select Data from the left menu and then ADO.NET Entity Data Model
  • Enter ProductModel as the name and click OK
  • This launches the Entity Data Model Wizard
  • Select Generate from Database and click Next
    Generate from Database selected in first step of wizard
  • Select the connection to the database you created in the first section, enter ProductContext as the name of the connection string and click Next
    DatabaseFirst.Blogging database selected and BloggingContext entered as connection string name in second step of wizard
  • Click the checkbox next to ‘Tables’ to import all tables and click ‘Finish’
    All tables selected for import in final step of wizard

 

Once the reverse engineer process completes the new model is added to your project and opened up for you to view in the Entity Framework Designer. An App.config file has also been added to your project with the connection details for the database.

Additional Steps in Visual Studio 2010

If you are working in Visual Studio 2010 then you will need to update the EF designer to use EF6 code generation.

  • Right-click on an empty spot of your model in the EF Designer and select Add Code Generation Item…
  • Select Online Templates from the left menu and search for DbContext
  • Select the EF 6.x DbContext Generator for C#, enter ProductsModel as the name and click Add

Updating code generation for data binding

EF generates code from your model using T4 templates. The templates shipped with Visual Studio or downloaded from the Visual Studio gallery are intended for general purpose use. This means that the entities generated from these templates have simple ICollection<T> properties. However, when doing data binding using WPF it is desirable to use ObservableCollection for collection properties so that WPF can keep track of changes made to the collections. To this end we will to modify the templates to use ObservableCollection.

  • Open the Solution Explorer and find ProductModel.edmx file
  • Find the ProductModel.tt file which will be nested under the ProductModel.edmx file
    tt file selected in Solution Explorer.
  • Double-click on the ProductModel.tt file to open it in the Visual Studio editor
  • Find and replace the two occurrences of “ICollection” with “ObservableCollection”. These are located approximately at lines 296 and 484.
  • Find and replace the first occurrence of “HashSet” with “ObservableCollection”. This occurrence is located approximately at line 50. Do not replace the second occurrence of HashSet found later in the code.
  • Find and replace the only occurrence of “System.Collections.Generic” with “System.Collections.ObjectModel”. This is located approximately at line 424.
  • Save the ProductModel.tt file. This should cause the code for entities to be regenerated. If the code does not regenerate automatically, then right click on ProductModel.tt and choose “Run Custom Tool”.

If you now open the Category.cs file (which is nested under ProductModel.tt) then you should see that the Products collection has the type ObservableCollection<Product>.

Compile the project.

 

 

Lazy Loading

The Products property on the Category class and Category property on the Product class are navigation properties. In Entity Framework, navigation properties provide a way to navigate a relationship between two entity types.

EF gives you an option of loading related entities from the database automatically the first time you access the navigation property. With this type of loading (called lazy loading), be aware that the first time you access each navigation property a separate query will be executed against the database if the contents are not already in the context.

When using POCO entity types, EF achieves lazy loading by creating instances of derived proxy types during runtime and then overriding virtual properties in your classes to add the loading hook. To get lazy loading of related objects, you must declare navigation property getters as public and virtual (Overridable in Visual Basic), and you class must not be sealed (NotOverridable in Visual Basic). When using Database First navigation properties are automatically made virtual to enable lazy loading. In the Code First section we chose to make the navigation properties virtual for the same reason

 

Bind Object to Controls

Add the classes that are defined in the model as data sources for this WPF application.

  • Double-click MainWindow.xaml in Solution Explorer to open the main form
  • From the main menu, select Project -> Add New Data Source …
    (in Visual Studio 2010, you need to select Data -> Add New Data Source…)
  • In the Choose a Data Source Typewindow, select Object and click Next
  • In the Select the Data Objects dialog, unfold the WPFwithEFSample two times and select Category

    There is no need to select the Product data source, because we will get to it through the Product’s property on the Category data source

    Screenshot of configuring a data source
  • Click Finish.
  • The Data Sources window is opened next to the MainWindow.xaml window
    If the Data Sources window is not showing up, select View -> Other Windows-> Data Sources
  • Press the pin icon, so the Data Sources window does not auto hide. You may need to hit the refresh button if the window was already visible.
    Screenshot of pinning the data sources window
  • Select the Category data source and drag it on the form.

The following happened when we dragged this source:

  • The categoryViewSource resource and the categoryDataGrid control were added to XAML. For more information about DataViewSources, see http://bea.stollnitz.com/blog/?p=387.
  • The DataContext property on the parent Grid element was set to "{StaticResource categoryViewSource }".  The categoryViewSource resource serves as a binding source for the outer\parent Grid element. The inner Grid elements then inherit the DataContext value from the parent Grid (the categoryDataGrid’s ItemsSource property is set to "{Binding}"). 
<Window.Resources> 
    <CollectionViewSource x:Key="categoryViewSource"  
                            d:DesignSource="{d:DesignInstance {x:Type local:Category}, CreateList=True}"/> 
</Window.Resources> 
<Grid DataContext="{StaticResource categoryViewSource}"
    <DataGrid x:Name="categoryDataGrid" AutoGenerateColumns="False" EnableRowVirtualization="True"  
                ItemsSource="{Binding}" Margin="13,13,43,191"  
                RowDetailsVisibilityMode="VisibleWhenSelected"
        <DataGrid.Columns> 
            <DataGridTextColumn x:Name="categoryIdColumn" Binding="{Binding CategoryId}"  
                                Header="Category Id" Width="SizeToHeader"/> 
            <DataGridTextColumn x:Name="nameColumn" Binding="{Binding Name}"  
                                Header="Name" Width="SizeToHeader"/> 
        </DataGrid.Columns> 
    </DataGrid> 
</Grid>

 

Adding a Details Grid

Now that we have a grid to display Categories let's add a details grid to display the associated Products.

  • Select the Products property from under the Category data source and drag it on the form.
    • The categoryProductsViewSource resource and productDataGrid grid are added to XAML
    • The binding path for this resource is set to Products
    • WPF data-binding framework ensures that only Products related to the selected Category show up in productDataGrid
  • From the Toolbox, drag Button on to the form. Set the Name property to buttonSave and the Content property to Save.

The form should look similar to this:

Screenshot of form with two data grids 

 

Add Code that Handles Data Interaction

It's time to add some event handlers to the main window.

  • In the XAML window, click on the <Window element, this selects the main window
  • In the Properties window choose Events at the top right, then double-click the text box to right of the Loaded label
    Screenshot of properties window with Loaded event handler specified
  • Also add the Click event for the Save button by double-clicking the Save button in the designer. 

This brings you to the code behind for the form, we'll now edit the code to use the ProductContext to perform data access. Update the code for the MainWindow as shown below.

The code declares a long-running instance of ProductContext. The ProductContext object is used to query and save data to the database. The Dispose() on the ProductContext instance is then called from the overridden OnClosing method. The code comments provide details about what the code does.

using System.Data.Entity; 
using System.Linq; 
using System.Windows; 
 
namespace WPFwithEFSample 

    public partial class MainWindow : Window 
    { 
        private ProductContext _context = new ProductContext(); 
        public MainWindow() 
        { 
            InitializeComponent(); 
        } 
 
        private void Window_Loaded(object sender, RoutedEventArgs e) 
        { 
            System.Windows.Data.CollectionViewSource categoryViewSource = 
                ((System.Windows.Data.CollectionViewSource)(this.FindResource("categoryViewSource"))); 
 
            // Load is an extension method on IQueryable,  
            // defined in the System.Data.Entity namespace. 
            // This method enumerates the results of the query,  
            // similar to ToList but without creating a list. 
            // When used with Linq to Entities this method  
            // creates entity objects and adds them to the context. 
            _context.Categories.Load(); 
 
            // After the data is loaded call the DbSet<T>.Local property  
            // to use the DbSet<T> as a binding source. 
            categoryViewSource.Source = _context.Categories.Local; 
        } 
 
        private void buttonSave_Click(object sender, RoutedEventArgs e) 
        { 
            // When you delete an object from the related entities collection  
            // (in this case Products), the Entity Framework doesn’t mark  
            // these child entities as deleted. 
            // Instead, it removes the relationship between the parent and the child 
            // by setting the parent reference to null. 
            // So we manually have to delete the products  
            // that have a Category reference set to null. 
 
            // The following code uses LINQ to Objects  
            // against the Local collection of Products. 
            // The ToList call is required because otherwise the collection will be modified 
            // by the Remove call while it is being enumerated. 
            // In most other situations you can use LINQ to Objects directly  
            // against the Local property without using ToList first. 
            foreach (var product in _context.Products.Local.ToList()) 
            { 
                if (product.Category == null
                { 
                    _context.Products.Remove(product); 
                } 
            } 
 
            _context.SaveChanges(); 
            // Refresh the grids so the database generated values show up. 
            this.categoryDataGrid.Items.Refresh(); 
            this.productsDataGrid.Items.Refresh(); 
        } 
 
 
        protected override void OnClosing(System.ComponentModel.CancelEventArgs e) 
        { 
            base.OnClosing(e); 
            this._context.Dispose(); 
        } 
    } 
 
}

 

Test the WPF Application

  • Compile and run the application. If you used Code First, then you will see that a WPFwithEFSample.ProductContext database is created for you.

    If SQL Express is installed (included in Visual Studio 2010) then the database is created on your local SQL Express instance (.\SQLEXPRESS). If SQL Express is not installed then Code First will try and use LocalDb ((localdb)\v11.0) - LocalDb is included with Visual Studio 2012.

    Note: SQL Express will always get precedence if it is installed, even if you are using Visual Studio 2012
  • Enter a category name in the top grid and product names in the bottom grid
    Do not enter anything in ID columns, because the primary key is generated by the database
    Screenshot of application before saving
  • Press the Save button to save the data to the database

After the call to DbContext’s SaveChanges(), the IDs are populated with the database generated values. Because we called Refresh() after SaveChanges() the DataGrid controls are updated with the new values as well.

Screenshot of application after saving


XAML 개요(WPF)

WPF에서 사용하는 XAML 에 대한 정보를 찾아 볼 일이 있어 개요부분만 링크로 정리해 둔다.

https://msdn.microsoft.com/ko-kr/library/ms752059(v=vs.110).aspx

[프로젝트 파일]

(*Package Download 필요 / Audo.- packages.config 있음)

Pilot_Contact_Local.zip

WPF 로 만든 간단 주소록 예제 (version 0.5, ~ing)

Feature.

1. 주소록 리스트

2. 개인별 조회

3. 양방향 바인딩({Binding Mode=TwoWay})

4. SQLite 연동

5. DB 파일은 D:\SqliteDB\PilotContacts\ 아래 복사하거나 App.config 파일의 connectionStrings 을 수정

6. Excel export / import 기능 추가

(keyword : ListView 예제, Binding 예제, SQLite 예제)


[GitHub Link] - 업데이트시 github 에 merge

https://github.com/shaunliz/wpf-simple-contacts




(출처 : MSDN)


WPF(Windows Presentation Foundation) 데이터 바인딩은 응용 프로그램에서 데이터를 표시하고 데이터와 상호 작용하는 간단하고 편리한 방법입니다. 다양한 데이터 소스에서 CLR(공용 언어 런타임) 개체 및 XML의 형태로 데이터에 요소를 바인딩할 수 있습니다. ContentControl (예: Button) 및 ItemsControl(예: ListBox와 ListView)에는 데이터 항목 컬렉션이나 단일 데이터 항목에 유연하게 스타일을 지정할 수 있도록 기본 제공되는 기능이 있습니다. 데이터를 기반으로 정렬, 필터 및 그룹 보기를 생성할 수 있습니다.

WPF의 데이터 바인딩 기능에는 데이터 바인딩을 지원하는 다양한 속성, 데이터의 유연한 UI 표현 및 비즈니스 논리와 UI의 명확한 분리와 같은 기존 모델보다 향상된 여러 가지 기능이 있습니다.

(*링크에서 계속...)

[상세 링크]

https://msdn.microsoft.com/ko-kr/library/ms752347(v=vs.110).aspx


WPF (Windows Presentation Foundation)

1. .NET Framework 3.0 부터 추가된 기술

2. .NET Framework 1.0 에서 Windows Form 이라는 기술이 있었으나, 사용자의 요구에 맞추어 비디오, 미디어, 애니메이션, 2D, 3D 등을 함께 사용할 수 있어야만 했으며, 이를 위해서는 각각의 필요한 요소 기술들을 처리 할 수 있어야 했음.

3. 따라서 하나의 기술 범위 내에서 모든 기능을 구현할 수 있도록 각각의 기술들을 광범위하게 연결하고 통합하도록 하는 기술이 필요해졌으며, 이에 만들어진 기술이 WPF – Windows Presentation Foundation –

4. .NET Framework 3.x 에 새로이 추가된 기술로써, Windows Vista 의 새로운 UX 구현을 위해서 탄생.


[WPF 이전]

 원하는 기능

닷넷 2.0 솔루션 

 윈도우 컨트롤

 윈도우 폼

 2D 그래픽

 GDI+

 3D 그래픽

 DirectX Api

 스트리밍 비디오

 Windows Media Player

 플로우 문서

 PDF


[WPF]

 원하는 기능

닷넷 3.0 솔루션 

 윈도우 컨트롤

 WPF

 2D 그래픽

 WPF

 3D 그래픽

 WPF

 스트리밍 비디오

 WPF

 플로우 문서

 WPF


5. XAML 을 통해 UI 분리 - 로직과 UI 가 분리됨

6. 렌더링 최적화 - DirectX engine 으로 렌더링

그래픽 렌더링 개요 : https://msdn.microsoft.com/ko-kr/library/ms748373(v=vs.110).aspx


코드내에서 'Invoke()' 를 사용하는 경우 동기화 되어 동작을 한다.

Invoke() 가 호출 된 뒤 해당 액션을 완료하기를 대기를 하게되며, 완료 후 다음 코드로 진행.

MSDN 링크 : 

https://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=KO-KR&k=k(System.Windows.Threading.Dispatcher.Invoke);k(TargetFrameworkMoniker-.NETFramework,Version%3Dv4.5.2);k(DevLang-csharp)&rd=true


ex)

textBox.Dispatcher.Invoke(new Action(()=>{textBox.Text = "SYNC."}));

// Action 완료를 기다리는 대기상태

if(bSync) {

...

}

+ Recent posts