17.02.2023, 21:03. Показов 3365. Ответов 2
Задача:
Необходимо подключить базу данных типа
SQLite или
MySQL (предпочтительнее
SQLite) к
C# проекту
WPF через пакет
Microsoft.EntityFrameworkCore.
Что было сделано:
- Создана БД SQLite в SQLiteStudio (ниже запрос генерации таблицы):
| SQL |
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
94
95
96
97
98
99
100
101
| --
-- File generated with SQLiteStudio v3.4.3 on Fri Feb 17 19:20:29 2023
--
-- Text encoding used: System
--
PRAGMA foreign_keys = off;
BEGIN TRANSACTION;
-- Table: App
CREATE TABLE IF NOT EXISTS App (
Id INTEGER PRIMARY KEY AUTOINCREMENT,
Name TEXT NOT NULL
UNIQUE,
DeveloperCompanyId INTEGER REFERENCES Company (Id) ON DELETE CASCADE
ON UPDATE CASCADE
NOT NULL,
PublisherCompanyId INTEGER REFERENCES Company (Id) ON DELETE CASCADE
ON UPDATE CASCADE
NOT NULL,
UploadDatetime TEXT NOT NULL,
ReleaseDatetime TEXT,
DiscountPercent INT,
DiscountStartDatetime TEXT,
DiscountExpireDatetime TEXT,
AppTypeId INTEGER REFERENCES AppType (Id) ON DELETE SET NULL
ON UPDATE CASCADE
NOT NULL,
AppReleaseStateId INTEGER REFERENCES AppReleaseState (Id) ON DELETE SET NULL
ON UPDATE CASCADE
NOT NULL
)
STRICT;
-- Table: AppReleaseState
CREATE TABLE IF NOT EXISTS AppReleaseState (
Id INTEGER PRIMARY KEY AUTOINCREMENT,
Name TEXT NOT NULL
)
STRICT;
-- Table: AppType
CREATE TABLE IF NOT EXISTS AppType (
Id INTEGER PRIMARY KEY AUTOINCREMENT,
Name TEXT NOT NULL
)
STRICT;
-- Table: Company
CREATE TABLE IF NOT EXISTS Company (
Id INTEGER PRIMARY KEY AUTOINCREMENT,
LegalName TEXT NOT NULL,
CompanyForm TEXT NOT NULL,
OperatingName TEXT NOT NULL,
StreetAddress TEXT NOT NULL,
City TEXT NOT NULL,
StateOrProvince TEXT NOT NULL,
PostalCode TEXT NOT NULL,
Email TEXT NOT NULL,
RegistrationDatetime TEXT NOT NULL,
CountryId TEXT REFERENCES Country (TwoLetterIsoCode) ON DELETE CASCADE
ON UPDATE CASCADE
NOT NULL,
OwnerUserId INTEGER REFERENCES USER (Id) ON DELETE CASCADE
ON UPDATE CASCADE
NOT NULL
)
STRICT;
-- Table: Country
CREATE TABLE IF NOT EXISTS Country (
TwoLetterIsoCode TEXT PRIMARY KEY
NOT NULL
UNIQUE,
Name TEXT NOT NULL
)
WITHOUT ROWID,
STRICT;
-- Table: User
CREATE TABLE IF NOT EXISTS USER (
Id INTEGER PRIMARY KEY AUTOINCREMENT,
Login TEXT NOT NULL,
Password TEXT NOT NULL,
Email TEXT NOT NULL,
Phone TEXT,
Wallet REAL NOT NULL,
RegistrationDatetime TEXT NOT NULL,
CountryId TEXT REFERENCES Country (TwoLetterIsoCode) ON DELETE CASCADE
ON UPDATE CASCADE
NOT NULL
)
STRICT;
COMMIT TRANSACTION;
PRAGMA foreign_keys = ON; |
|
- Создан проект WPF на .NET 7.0 в Visual Studio 2022, к нему подключены пакеты NuGet:
- Microsoft.EntityFrameworkCore - основной пакет;
- Microsoft.EntityFrameworkCore.Sqlite - пакет для работы с SQLite;
- Microsoft.EntityFrameworkCore.Tools - пакет, который понадобится для реверс-инжиниринга классов и создания контекста существующей БД.
- В созданный проект закинут сам файл БД (Toolkit.db) для простоты поиска. Расширение (.db) добавил для лучшей видимости (1-ое вложение);
- В консоли диспетчера пакетов прописана следующая команда:
| Code |
1
| Scaffold-DbContext "datasource=C:\Users\Nilson\source\repos\Toolkit-NET\Toolkit-NET_Client\Toolkit.db" Microsoft.EntityFrameworkCore.Sqlite |
|
Эта команда из пакета Microsoft.EntityFrameworkCore.Tools, как писал выше, для генерации классов и контекста. Генерация происходит успешно со следующим выводом:
| Code |
1
2
3
4
| PM> Scaffold-DbContext "datasource=C:\Users\Nilson\source\repos\Toolkit-NET\Toolkit-NET_Client\Toolkit.db" Microsoft.EntityFrameworkCore.Sqlite
Build started...
Build succeeded.
To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see http://go.microsoft.com/fwlink/?LinkId=723263. |
|
В проекте создаются следующие файлы:- ToolkitContext.cs:
| C# |
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
94
95
96
97
98
99
100
101
102
103
| using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;
namespace Toolkit_NET_Client;
public partial class ToolkitContext : DbContext
{
public ToolkitContext()
{
}
public ToolkitContext(DbContextOptions<ToolkitContext> options)
: base(options)
{
}
public virtual DbSet<App> Apps { get; set; }
public virtual DbSet<AppReleaseState> AppReleaseStates { get; set; }
public virtual DbSet<AppType> AppTypes { get; set; }
public virtual DbSet<Company> Companies { get; set; }
public virtual DbSet<Country> Countries { get; set; }
public virtual DbSet<Developer> Developers { get; set; }
public virtual DbSet<User> Users { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see http://go.microsoft.com/fwlink/?LinkId=723263.
=> optionsBuilder.UseSqlite("datasource=C:\\Users\\Nilson\\source\\repos\\Toolkit-NET\\Toolkit-NET_Client\\Toolkit.db");
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<App>(entity =>
{
entity.ToTable("App");
entity.HasIndex(e => e.Name, "IX_App_Name").IsUnique();
entity.Property(e => e.DiscountPercent).HasColumnType("INT");
entity.HasOne(d => d.AppReleaseState).WithMany(p => p.Apps)
.HasForeignKey(d => d.AppReleaseStateId)
.OnDelete(DeleteBehavior.SetNull);
entity.HasOne(d => d.AppType).WithMany(p => p.Apps)
.HasForeignKey(d => d.AppTypeId)
.OnDelete(DeleteBehavior.SetNull);
entity.HasOne(d => d.Developer).WithMany(p => p.Apps).HasForeignKey(d => d.DeveloperId);
});
modelBuilder.Entity<AppReleaseState>(entity =>
{
entity.ToTable("AppReleaseState");
});
modelBuilder.Entity<AppType>(entity =>
{
entity.ToTable("AppType");
});
modelBuilder.Entity<Company>(entity =>
{
entity.ToTable("Company");
entity.HasOne(d => d.Country).WithMany(p => p.Companies).HasForeignKey(d => d.CountryId);
entity.HasOne(d => d.OwnerUser).WithMany(p => p.Companies).HasForeignKey(d => d.OwnerUserId);
});
modelBuilder.Entity<Country>(entity =>
{
entity.HasKey(e => e.TwoLetterIsoCode);
entity.ToTable("Country");
});
modelBuilder.Entity<Developer>(entity =>
{
entity.ToTable("Developer");
entity.HasIndex(e => e.CompanyId, "IX_Developer_CompanyId").IsUnique();
entity.HasOne(d => d.Company).WithOne(p => p.Developer).HasForeignKey<Developer>(d => d.CompanyId);
});
modelBuilder.Entity<User>(entity =>
{
entity.ToTable("User");
entity.HasOne(d => d.Country).WithMany(p => p.Users).HasForeignKey(d => d.CountryId);
});
OnModelCreatingPartial(modelBuilder);
}
partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
} |
|
- App.cs, AppReleaseState.cs, AppType.cs, Company.cs, Country.cs, Developer.cs, User.cs (объединил в один блок):
| C# |
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
| // ----- Начало App.cs -----
using System;
using System.Collections.Generic;
namespace Toolkit_NET_Client;
public partial class App
{
public long Id { get; set; }
public string Name { get; set; } = null!;
public long DeveloperId { get; set; }
public string UploadDatetime { get; set; } = null!;
public string? ReleaseDatetime { get; set; }
public long? DiscountPercent { get; set; }
public string? DiscountStartDatetime { get; set; }
public string? DiscountExpireDatetime { get; set; }
public long AppTypeId { get; set; }
public long AppReleaseStateId { get; set; }
public virtual AppReleaseState AppReleaseState { get; set; } = null!;
public virtual AppType AppType { get; set; } = null!;
public virtual Developer Developer { get; set; } = null!;
}
// ----- Конец App.cs -----
// ----- Начало AppReleaseState.cs -----
using System;
using System.Collections.Generic;
namespace Toolkit_NET_Client;
public partial class AppReleaseState
{
public long Id { get; set; }
public string Name { get; set; } = null!;
public virtual ICollection<App> Apps { get; } = new List<App>();
}
// ----- Конец AppReleaseState.cs -----
// ----- Начало AppType.cs -----
using System;
using System.Collections.Generic;
namespace Toolkit_NET_Client;
public partial class AppType
{
public long Id { get; set; }
public string Name { get; set; } = null!;
public virtual ICollection<App> Apps { get; } = new List<App>();
}
// ----- Конец AppType.cs -----
using System;
using System.Collections.Generic;
namespace Toolkit_NET_Client;
// ----- Начало Company.cs -----
public partial class Company
{
public long Id { get; set; }
public string LegalName { get; set; } = null!;
public string CompanyForm { get; set; } = null!;
public string OperatingName { get; set; } = null!;
public string StreetAddress { get; set; } = null!;
public string City { get; set; } = null!;
public string StateOrProvince { get; set; } = null!;
public string PostalCode { get; set; } = null!;
public string Email { get; set; } = null!;
public string RegistrationDatetime { get; set; } = null!;
public string CountryId { get; set; } = null!;
public long OwnerUserId { get; set; }
public virtual Country Country { get; set; } = null!;
public virtual Developer? Developer { get; set; }
public virtual User OwnerUser { get; set; } = null!;
}
// ----- Конец Company.cs -----
// ----- Начало Country.cs -----
using System;
using System.Collections.Generic;
namespace Toolkit_NET_Client;
public partial class Country
{
public string TwoLetterIsoCode { get; set; } = null!;
public string Name { get; set; } = null!;
public virtual ICollection<Company> Companies { get; } = new List<Company>();
public virtual ICollection<User> Users { get; } = new List<User>();
}
// ----- Конец Country.cs -----
// ----- Начало Developer.cs -----
using System;
using System.Collections.Generic;
namespace Toolkit_NET_Client;
public partial class Developer
{
public long Id { get; set; }
public long CompanyId { get; set; }
public virtual ICollection<App> Apps { get; } = new List<App>();
public virtual Company Company { get; set; } = null!;
}
// ----- Конец Developer.cs -----
// ----- Начало User.cs -----
using System;
using System.Collections.Generic;
namespace Toolkit_NET_Client;
public partial class User
{
public long Id { get; set; }
public string Login { get; set; } = null!;
public string Password { get; set; } = null!;
public string Email { get; set; } = null!;
public string? Phone { get; set; }
public double Wallet { get; set; }
public string RegistrationDatetime { get; set; } = null!;
public string CountryId { get; set; } = null!;
public virtual ICollection<Company> Companies { get; } = new List<Company>();
public virtual Country Country { get; set; } = null!;
}
// ----- Конец User.cs ----- |
|
- В файле MainWindow.xaml.cs прописываем простое обращение к контексту базы данных с выводом количества пользователей в БД при создании первичного окна:
| C# |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| using System;
using System.Linq;
using System.Windows;
namespace Toolkit_NET_Client
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
using (var db = new ToolkitContext())
{
var users = db.Users.ToList();
Console.WriteLine("Users in DB: " + users.Count);
}
}
}
} |
|
Проблема:
При запуске приложения ловим исключение (2-ое вложение):
| Code |
1
2
3
| System.InvalidOperationException: "No suitable constructor was found for entity type 'GroupStyleSelector'. The following constructors had parameters that could not be bound to properties of the entity type:
Cannot bind 'object', 'method' in 'GroupStyleSelector(object object, IntPtr method)'
Note that only mapped properties can be bound to constructor parameters. Navigations to related entities, including references to owned types, cannot be bound." |
|
Вывод консоли:
| Code |
1
2
3
4
5
| Вызвано исключение: "System.InvalidOperationException" в Microsoft.EntityFrameworkCore.dll
Исключение типа "System.InvalidOperationException" возникло в Microsoft.EntityFrameworkCore.dll, но не было обработано в коде пользователя
No suitable constructor was found for entity type 'GroupStyleSelector'. The following constructors had parameters that could not be bound to properties of the entity type:
Cannot bind 'object', 'method' in 'GroupStyleSelector(object object, IntPtr method)'
Note that only mapped properties can be bound to constructor parameters. Navigations to related entities, including references to owned types, cannot be bound. |
|
Стек вызовов:
| Code |
1
2
3
| [Внешний код]
→ Toolkit-NET_Client.dll!Toolkit_NET_Client.MainWindow.MainWindow() Строка 18
[Внешний код] |
|
Если посмотреть данные переменной
$exception, то также можно узнать следующее (3-е вложение):
- Source="Microsoft.EntityFrameworkCore"
- StackTrace=
| Code |
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
| в Microsoft.EntityFrameworkCore.Metadata.Internal.ConstructorBindingFactory.GetBindings(IReadOnlyEntityType entityType, Func`5 bind, InstantiationBinding& constructorBinding, InstantiationBinding& serviceOnlyBinding)
в Microsoft.EntityFrameworkCore.Metadata.Internal.ConstructorBindingFactory.GetBindings(IMutableEntityType entityType, InstantiationBinding& constructorBinding, InstantiationBinding& serviceOnlyBinding)
в Microsoft.EntityFrameworkCore.Metadata.Conventions.ConstructorBindingConvention.ProcessModelFinalizing(IConventionModelBuilder modelBuilder, IConventionContext`1 context)
в Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.ImmediateConventionScope.OnModelFinalizing(IConventionModelBuilder modelBuilder)
в Microsoft.EntityFrameworkCore.Metadata.Conventions.Internal.ConventionDispatcher.OnModelFinalizing(IConventionModelBuilder modelBuilder)
в Microsoft.EntityFrameworkCore.Metadata.Internal.Model.FinalizeModel()
в Microsoft.EntityFrameworkCore.Infrastructure.ModelRuntimeInitializer.Initialize(IModel model, Boolean designTime, IDiagnosticsLogger`1 validationLogger)
в Microsoft.EntityFrameworkCore.Infrastructure.ModelSource.GetModel(DbContext context, ModelCreationDependencies modelCreationDependencies, Boolean designTime)
в Microsoft.EntityFrameworkCore.Internal.DbContextServices.CreateModel(Boolean designTime)
в Microsoft.EntityFrameworkCore.Internal.DbContextServices.get_Model()
в Microsoft.EntityFrameworkCore.Infrastructure.EntityFrameworkServicesBuilder.<>c.<TryAddCoreServices>b__8_4(IServiceProvider p)
в Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitFactory(FactoryCallSite factoryCallSite, RuntimeResolverContext context)
в Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
в Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite,
...
в Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
в Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitScopeCache(ServiceCallSite callSite, RuntimeResolverContext context)
в Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
в Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
в Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass2_0.<RealizeService>b__0(ServiceProviderEngineScope scope)
в Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
в Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType)
в Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
в Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
в Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
в Microsoft.EntityFrameworkCore.DbContext.get_ContextServices()
в Microsoft.EntityFrameworkCore.DbContext.get_Model()
в Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.get_EntityType()
в Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.CheckState()
в Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.get_EntityQueryable()
в Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.System.Collections.Generic.IEnumerable<TEntity>.GetEnumerator()
в System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
в System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
в Toolkit_NET_Client.MainWindow..ctor() в C:\Users\Nilson\source\repos\Toolkit-NET\Toolkit-NET_Client\MainWindow.xaml.cs:строка 18
в System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean wrapExceptions) |
|
И... на этом все. Что за
'GroupStyleSelector' и почему вызывается его конструктор - понятия не имею. В стеке вызовов его нет.
Насколько я понял, учитывая, что на верхушке стека вызовов вызывается функция завершение создания модели БД (
ProcessModelFinalizing) и получение биндингов (
GetBindings) - это что-то связанное со связями сущностей.
И уверен я на 99%, потому что при удалении таблицы
App, удалении всех сгенерированных файлов классов сущностей и контекста БД, а затем заново запуская скрипт
Scaffold-DbContext, то магическим чудом все начинает работать.
С чем конкретно связано я, сидя над этой проблемой 2 дня, так и не разобрался.
Возможно, это как-то связано с неправильным заданием ограничений/внешних ключей и т.д., но проблема в том, что база данных спокойно работает, если к ней обращаться через запросы в
SQLiteStudio. К тому же, мой знакомый решил попробовать создать точно такой же проект и подключить туда эту же базу данных - все то же самое.
Затем он вообще создал проект
Windows Forms, и уже там все нормально работало. Но, возможно он как раз упрощенную версию бд взял, не могу вспомнить точно.
Может это вообще проблема в
WPF или самом пакете
Microsoft.EntityFramework? Я уже не знаю, столько всего перебробывал, что руки падают, но на чистых sql запросах не хотелось бы, придется все вручную обрабатывать. Вполне вероятно, что я где-то допустил ошибку и сейчас не могу найти ее, потому что кажется, что ну нечего больше менять.
Удивительное, что тоже самое происходит при подключении базы данных
MySQL. Процесс абсолютно идентичный, только пакет там уже используется
MySql.EntityFrameworkCore.
У меня были догадки, что возможно это из-за ключей
DeveloperCompanyId и
PublisherCompanyId в таблице
App, которые оба ссылаются на одну сущность, и, возможно, там какая-то проблема. Пробывал делать отдельные таблицы
Developer и
Publisher, которые сами имели ключи к
Company, и уже к ним привязывать - все равно отказывалась работать.
Кстати, проблема абсолютно такая же, как в этой теме -
https://www.cyberforum.ru/ado-... 88614.html
Никто внятно так и не ответил в чем причина и как решить.
И в StackTrace я немного строк стер между, чтобы текст влез. В любом случае они там все такие же, как возле них.