Skip to content

Commit 8c97de6

Browse files
committed
EF Migrations: finished LocaleResourceBuilder (fluent migration of locale string resouces)
1 parent a84d241 commit 8c97de6

14 files changed

Lines changed: 349 additions & 25 deletions

src/Libraries/SmartStore.Data/Migrations/201403112331027_Initial.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ namespace SmartStore.Data.Migrations
22
{
33
using System;
44
using System.Data.Entity.Migrations;
5+
using System.Web.Hosting;
56
using SmartStore.Core.Data;
67
using SmartStore.Data.Setup;
78

@@ -1773,10 +1774,11 @@ public override void Up()
17731774
#endregion
17741775

17751776
#region Custom
1776-
1777+
17771778
this.SqlFile("Indexes.sql");
1778-
if (DataSettings.Current.IsSqlServer)
1779+
if (HostingEnvironment.IsHosted && DataSettings.Current.IsSqlServer)
17791780
{
1781+
// do not execute in unit tests
17801782
this.SqlFile("Indexes.SqlServer.sql");
17811783
this.SqlFile("StoredProcedures.sql");
17821784
}
@@ -1789,8 +1791,9 @@ public override void Down()
17891791
#region Custom
17901792

17911793
this.SqlFile("Indexes.Inverse.sql");
1792-
if (DataSettings.Current.IsSqlServer)
1794+
if (HostingEnvironment.IsHosted && DataSettings.Current.IsSqlServer)
17931795
{
1796+
// do not execute in unit tests
17941797
this.SqlFile("Indexes.SqlServer.Inverse.sql");
17951798
this.SqlFile("StoredProcedures.Inverse.sql");
17961799
}

src/Libraries/SmartStore.Data/Properties/AssemblyInfo.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
using System.Runtime.CompilerServices;
33
[assembly: AssemblyTitle("SmartStore.Data")]
44
[assembly: InternalsVisibleTo("SmartStore.Web")]
5+
[assembly: InternalsVisibleTo("SmartStore.Data.Tests")]

src/Libraries/SmartStore.Data/Setup/ILocaleResourcesProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ namespace SmartStore.Data.Setup
55

66
public interface ILocaleResourcesProvider
77
{
8-
void AlterLocaleResources(LocaleResourcesBuilder builder);
8+
void MigrateLocaleResources(LocaleResourcesBuilder builder);
99
}
1010

1111
}

src/Libraries/SmartStore.Data/Setup/LocaleResourcesBuilder.cs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,33 @@ public class LocaleResourcesBuilder : IHideObjectMembers
2525
{
2626
private readonly List<LocaleResourceEntry> _entries = new List<LocaleResourceEntry>();
2727

28+
/// <summary>
29+
/// Deletes one or many locale resources in any language from the database
30+
/// </summary>
31+
/// <param name="keys">The key(s) of the resources to delete</param>
2832
public void Delete(params string[] keys)
2933
{
3034
keys.Each(x => _entries.Add(new LocaleResourceEntry { Key = x, Important = true }));
3135
}
3236

37+
/// <summary>
38+
/// Deletes one or many locale resources in the specified language from the database
39+
/// </summary>
40+
/// <param name="lang">The language identifier</param>
41+
/// <param name="keys">The key(s) of the resources to delete</param>
3342
public void DeleteFor(string lang, params string[] keys)
3443
{
3544
Guard.ArgumentNotEmpty(() => lang);
45+
lang = lang.NullEmpty();
46+
3647
keys.Each(x => _entries.Add(new LocaleResourceEntry { Key = x, Lang = lang, Important = true }));
3748
}
3849

50+
/// <summary>
51+
/// Adds or updates locale resources
52+
/// </summary>
53+
/// <param name="key"></param>
54+
/// <returns></returns>
3955
public IResourceAddBuilder AddOrUpdate(string key)
4056
{
4157
Guard.ArgumentNotEmpty(() => key);
@@ -48,12 +64,17 @@ public IResourceAddBuilder AddOrUpdate(string key)
4864
return new ResourceAddBuilder(key, fn);
4965
}
5066

67+
internal void Reset()
68+
{
69+
_entries.Clear();
70+
}
71+
5172
internal IEnumerable<LocaleResourceEntry> Build()
5273
{
5374
return _entries.OrderByDescending(x => x.Important).ThenBy(x => x.Lang);
5475
}
5576

56-
#region Nested classes
77+
#region Nested builder for AddOrUpdate
5778

5879
internal class ResourceAddBuilder : IResourceAddBuilder
5980
{
@@ -72,7 +93,7 @@ public IResourceAddBuilder Value(string value)
7293
public IResourceAddBuilder Value(string lang, string value)
7394
{
7495
Guard.ArgumentNotEmpty(() => value);
75-
_fn(value, lang);
96+
_fn(value, lang.NullEmpty());
7697
return this;
7798
}
7899
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Data.Entity;
4+
using System.Linq;
5+
using System.Linq.Expressions;
6+
using System.Text;
7+
using System.Threading.Tasks;
8+
using SmartStore.Core.Data;
9+
using SmartStore.Core.Domain.Localization;
10+
11+
namespace SmartStore.Data.Setup
12+
{
13+
14+
internal class LocaleResourcesMigrator
15+
{
16+
private readonly SmartObjectContext _ctx;
17+
private readonly DbSet<Language> _languages;
18+
private readonly DbSet<LocaleStringResource> _resources;
19+
20+
public LocaleResourcesMigrator(SmartObjectContext ctx)
21+
{
22+
Guard.ArgumentNotNull(() => ctx);
23+
24+
_ctx = ctx;
25+
_languages = _ctx.Set<Language>();
26+
_resources = _ctx.Set<LocaleStringResource>();
27+
}
28+
29+
public void Migrate(IEnumerable<LocaleResourceEntry> entries, bool updateTouchedResources = false)
30+
{
31+
Guard.ArgumentNotNull(() => entries);
32+
33+
if (!entries.Any())
34+
return;
35+
36+
using (var scope = new DbContextScope(_ctx, autoDetectChanges: false))
37+
{
38+
var langMap = _languages.ToDictionary(x => x.UniqueSeoCode);
39+
40+
var toDelete = new List<LocaleStringResource>();
41+
var toUpdate = new List<LocaleStringResource>();
42+
var toAdd = new List<LocaleStringResource>();
43+
44+
// remove all entries with invalid lang identifier
45+
var invalidEntries = entries.Where(x => x.Lang != null && !langMap.ContainsKey(x.Lang));
46+
if (invalidEntries.Any())
47+
{
48+
entries = entries.Except(invalidEntries);
49+
}
50+
51+
foreach (var lang in langMap)
52+
{
53+
foreach (var entry in entries.Where(x => x.Lang == null || langMap[x.Lang].Id == lang.Value.Id))
54+
{
55+
var db = GetResource(entry.Key, lang.Value.Id);
56+
57+
if (db == null && entry.Value.HasValue())
58+
{
59+
// ADD action
60+
toAdd.Add(new LocaleStringResource { LanguageId = lang.Value.Id, ResourceName = entry.Key, ResourceValue = entry.Value });
61+
}
62+
63+
if (db == null)
64+
continue;
65+
66+
if (entry.Value == null)
67+
{
68+
// DELETE action
69+
toDelete.Add(db);
70+
}
71+
else
72+
{
73+
// UPDATE action
74+
if (updateTouchedResources || !db.IsTouched.GetValueOrDefault())
75+
{
76+
db.ResourceValue = entry.Value;
77+
toUpdate.Add(db);
78+
if (toDelete.Contains(db))
79+
toDelete.Remove(db);
80+
}
81+
}
82+
}
83+
}
84+
85+
// add new resources to context
86+
_resources.AddRange(toAdd);
87+
88+
// remove deleted resources
89+
_resources.RemoveRange(toDelete);
90+
91+
// update modified resources
92+
toUpdate.Each(x => _ctx.Entry(x).State = System.Data.Entity.EntityState.Modified);
93+
94+
// save now
95+
_ctx.SaveChanges();
96+
}
97+
}
98+
99+
private LocaleStringResource GetResource(string key, int langId)
100+
{
101+
var query = _resources.Where(x => x.ResourceName.Equals(key, StringComparison.InvariantCultureIgnoreCase) && x.LanguageId == langId);
102+
return query.FirstOrDefault();
103+
}
104+
105+
}
106+
107+
}

src/Libraries/SmartStore.Data/Setup/SmartObjectContextExtensions.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,17 @@ public static class SmartObjectContextExtensions
1111

1212
#region Resource seeding
1313

14-
public static void UpdateLocaleResources(this SmartObjectContext ctx)
14+
public static void MigrateLocaleResources(this SmartObjectContext ctx, Action<LocaleResourcesBuilder> fn, bool updateTouchedResources = false)
1515
{
16+
Guard.ArgumentNotNull(() => ctx);
17+
Guard.ArgumentNotNull(() => fn);
18+
19+
var builder = new LocaleResourcesBuilder();
20+
fn(builder);
21+
var entries = builder.Build();
22+
23+
var migrator = new LocaleResourcesMigrator(ctx);
24+
migrator.Migrate(entries, updateTouchedResources);
1625
}
1726

1827
#endregion
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Data.Entity;
5+
using System.Data.Entity.Infrastructure;
6+
using System.Data.Entity.Migrations;
7+
using System.Data.Entity.Migrations.Infrastructure;
8+
9+
namespace SmartStore.Data.Tests
10+
{
11+
public class TestDatabaseInitializer<TContext, TConfig> : IDatabaseInitializer<TContext>
12+
where TContext : DbContext, new()
13+
where TConfig : DbMigrationsConfiguration<TContext>, new()
14+
{
15+
private readonly string _connectionString;
16+
17+
public TestDatabaseInitializer(string connectionString)
18+
{
19+
Guard.ArgumentNotEmpty(() => connectionString);
20+
this._connectionString = connectionString;
21+
}
22+
23+
protected virtual TConfig CreateConfiguration()
24+
{
25+
var config = new TConfig();
26+
config.TargetDatabase = new DbConnectionInfo(_connectionString, "System.Data.SqlServerCe.4.0");
27+
return config;
28+
}
29+
30+
31+
public void InitializeDatabase(TContext context)
32+
{
33+
var config = this.CreateConfiguration();
34+
new DbMigrator(config).Update();
35+
}
36+
}
37+
}

src/Libraries/SmartStore.Data/SmartStore.Data.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@
119119
<Compile Include="Setup\ILocaleResourcesProvider.cs" />
120120
<Compile Include="Setup\InstallDatabaseInitializer.cs" />
121121
<Compile Include="Setup\LocaleResourcesBuilder.cs" />
122+
<Compile Include="Setup\LocaleResourcesMigrator.cs" />
122123
<Compile Include="Setup\MigratorUtils.cs" />
123124
<Compile Include="Setup\SeedData\SeedDataException.cs" />
124125
<Compile Include="Setup\SeedData\SeedEntityExtensions.cs" />
@@ -147,6 +148,7 @@
147148
<Compile Include="Mapping\Shipping\ShipmentItemMap.cs" />
148149
<Compile Include="Mapping\Tasks\ScheduleTaskMap.cs" />
149150
<Compile Include="Setup\SmartObjectContextExtensions.cs" />
151+
<Compile Include="Setup\TestDatabaseInitializer.cs" />
150152
<Compile Include="SmartDbConfiguration.cs" />
151153
<Compile Include="SqlServerDataProvider.cs" />
152154
<Compile Include="SqlCeDataProvider.cs" />

src/Presentation/SmartStore.Web/Infrastructure/Installation/InstallDataSeeder.cs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -161,15 +161,11 @@ private void ExecutePendingResourceMigrations(string resPath)
161161
continue;
162162

163163
var builder = new LocaleResourcesBuilder();
164-
provider.AlterLocaleResources(builder);
165-
164+
provider.MigrateLocaleResources(builder);
165+
166166
var resEntries = builder.Build();
167-
foreach (var entry in resEntries)
168-
{
169-
// TBD [...]
170-
}
171-
172-
// TBD [...]
167+
var resMigrator = new LocaleResourcesMigrator(_ctx);
168+
resMigrator.Migrate(resEntries);
173169
}
174170
}
175171

src/Presentation/SmartStore.Web/SmartStore.Web.csproj

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,9 @@
438438
</ItemGroup>
439439
<ItemGroup>
440440
<Content Include="App_Data\Localization\App\de\all.smres.xml" />
441+
<Content Include="App_Data\Localization\App\de\head.txt" />
441442
<Content Include="App_Data\Localization\App\en\all.smres.xml" />
443+
<Content Include="App_Data\Localization\App\en\head.txt" />
442444
<Content Include="App_Data\Localization\Installation\installation.de.xml" />
443445
<Content Include="App_Data\Localization\Installation\installation.en.xml">
444446
<SubType>Designer</SubType>
@@ -2495,12 +2497,11 @@
24952497
<Target Name="MvcBuildViews" AfterTargets="AfterBuild" Condition="'$(MvcBuildViews)'=='true'">
24962498
<AspNetCompiler VirtualPath="temp" PhysicalPath="$(WebProjectOutputDir)" />
24972499
</Target>
2498-
<ProjectExtensions>
24992500
<ProjectExtensions>
25002501
<VisualStudio>
25012502
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
25022503
<WebProjectProperties>
2503-
<SaveServerSettingsInUserFile>True</SaveServerSettingsInUserFile>
2504+
<SaveServerSettingsInUserFile>True</SaveServerSettingsInUserFile>
25042505
</WebProjectProperties>
25052506
</FlavorProperties>
25062507
</VisualStudio>

0 commit comments

Comments
 (0)