Globalization in Razor view doesn't work asp.net core 3.1 - asp.net-core-3.1

I've read https://learn.microsoft.com/en-us/aspnet/core/fundamentals/localization?view=aspnetcore-3.1 and added everything needed. Added needing in Configure and ConfigureServices.
This is my Startup methods:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddDbContextPool<IDbContext, AppDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddScoped<ITaskBL, TaskBL>();
services.AddLocalization(options => options.ResourcesPath = "Resources");
services.AddMvc()
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
.AddDataAnnotationsLocalization();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseRouting();
var supportedCultures = new[] { "en", "ru" };
var localizationOptions = new RequestLocalizationOptions().SetDefaultCulture(supportedCultures[1])
.AddSupportedCultures(supportedCultures)
.AddSupportedUICultures(supportedCultures);
app.UseRequestLocalization(localizationOptions);
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
This is my Resources hierarchy
Index.ru.resx:
<?xml version="1.0" encoding="utf-8"?>
<root>
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>1.3</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Create task" xml:space="preserve">
<value>Создать задачу</value>
</data>
<data name="Create subtask" xml:space="preserve">
<value>Создать подзадачу</value>
</data>
<data name="Delete task" xml:space="preserve">
<value>Удалить задачу</value>
</data>
</root>
And this is how I used it in view:
#using Microsoft.AspNetCore.Mvc.Localization
#inject IViewLocalizer _localizer
#{
ViewData["Title"] = "Home Page";
}
<div class="text-center">
<h1 class="display-4">Welcome</h1>
<p>#_localizer["Create task"]</p>
</div>
It renders Create task, which I think is the key value. Please, help!

I had RootNamespace element set in .csproj, by removing the element, I removed the issue.

Related

Dynamic Servicefabric Settings and Overrides

Is there a way to not tell the service about the settings at all and just provide them at the application level?
I am still unhappy with how servicefabric configuration seems to work.
Near as I can tell I have to specify in the service’s settings.xml all of the possible configuration values. Then I can override those in the application’s ApplicationParameters. Per documentation this looks like it holds true for environment variables also.
The complication that creates is that our configuration is used to hydrate options in many cases with arrays.
For example consider the json:
{
"AuthorizationOptions": {
"Policies": [
{
"Name": "User",
"Groups": [ "Domain Users" ]
}
]
}
}
There are 2 arrays; that are necessary and useful. To express this in the service fabric configuration it translates to:
<Section Name="AuthorizationOptions">
<Parameter Name="Policies:0:Name" Value="User"/>
<Parameter Name="Policies:0:Groups:0" Value="Domain Users"/>
</Section>
While the translation is not pleasant in comparison to the structured object it is completely usable.
However, If I don’t specify the section and parameters in the service, I can’t seem to override them in the application. So in this case I would have to define the exact number of policies and groups per policy in the service and the application could modify the policy name, or the group values, but not the number of policies total or number of groups total.
Is there a way to not tell the service about the settings at all and just provide them at the application level?
If not what alternatives exist to make the service reusable across applications that I may want to use to provide this type of dynamic configuration differently?
The last part of the puzzle that may assist in answering this question is I am using some pre-release code to translate the service fabric settings into Microsoft.Extensions.Configuration.IConfiguration. However, that is just taking the settings it finds; it isn't the cause of the override issue I am running into.
Example Service Settings.xml:
<?xml version="1.0" encoding="utf-8" ?>
<Settings xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/2011/01/fabric">
<Section Name="AuthorizationOptions">
<!-- I should not have to provide these at the application level!
However, it fails to deploy if I don't. -->
<Parameter Name="Policies:0:Name" Value="User"/>
<Parameter Name="Policies:0:Groups:0" Value="Domain Users"/>
</Section>
</Settings>
Example Application ApplicationManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<ApplicationManifest xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ApplicationTypeName="ServiceFabric.ExampleType" ApplicationTypeVersion="1.0.0" xmlns="http://schemas.microsoft.com/2011/01/fabric">
<Parameters>
<Parameter Name="Service.Example_ASPNETCORE_ENVIRONMENT" DefaultValue="" />
<Parameter Name="Service.Example_InstanceCount" DefaultValue="-1" />
<Parameter Name="Service.Example_AuthorizationOptions_Policies_0_Name" DefaultValue="Users" />
<Parameter Name="Service.Example_AuthorizationOptions_Policies_0_Groups_0" DefaultValue="Domain Users" />
</Parameters>
<ServiceManifestImport>
<ServiceManifestImport>
<ServiceManifestRef ServiceManifestName="Service.ExamplePkg" ServiceManifestVersion="1.0.0" />
<ConfigOverrides>
<ConfigOverride Name="Config">
<Settings>
<Section Name="AuthorizationOptions">
<Parameter Name="Policies:0:Name" Value="[Service.Example_AuthorizationOptions_Policies_0_Name]" />
<Parameter Name="Policies:0:Groups:0" Value="[Service.Example_AuthorizationOptions_Policies_0_Groups_0]" />
</Section>
</Settings>
</ConfigOverride>
</ConfigOverrides>
<EnvironmentOverrides CodePackageRef="code">
<EnvironmentVariable Name="ASPNETCORE_ENVIRONMENT" Value="[Service.Example_ASPNETCORE_ENVIRONMENT]" />
</EnvironmentOverrides>
</ServiceManifestImport>
<DefaultServices>
<Service Name="Service.Example" ServicePackageActivationMode="ExclusiveProcess">
<StatelessService ServiceTypeName="Service.ExampleType" InstanceCount="[Service.Example_InstanceCount]">
<SingletonPartition />
</StatelessService>
</Service>
</DefaultServices>
</ApplicationManifest>
Example Application ApplicationParameters (Local.1Node.xml):
<?xml version="1.0" encoding="utf-8"?>
<Application xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Name="fabric:/ServiceFabric.Example" xmlns="http://schemas.microsoft.com/2011/01/fabric">
<Parameters>
<Parameter Name="Service.Example_ASPNETCORE_ENVIRONMENT" Value="Development" />
<Parameter Name="Service.Example_InstanceCount" Value="-1" />
<Parameter Name="Service.Example_AuthorizationOptions_Policies_0_Name" Value="Users" />
<Parameter Name="Service.Example_AuthorizationOptions_Policies_0_Groups_0" Value="Domain Users" />
</Parameters>
</Application>
Example Application PublishProfiles (Local.1Node.xml):
<?xml version="1.0" encoding="utf-8"?>
<PublishProfile xmlns="http://schemas.microsoft.com/2015/05/fabrictools">
<ClusterConnectionParameters />
<ApplicationParameterFile Path="..\ApplicationParameters\Local.1Node.xml" />
</PublishProfile>
Should be unrelated, but example consumption of the settings:
internal sealed class Example : StatelessService
{
public Example(StatelessServiceContext context)
: base(context)
{ }
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
return new ServiceInstanceListener[]
{
new ServiceInstanceListener(serviceContext =>
new HttpSysCommunicationListener(serviceContext, "ServiceEndpoint", (url, listener) =>
{
ServiceEventSource.Current.ServiceMessage(serviceContext, $"Starting HttpSys on {url}");
return new WebHostBuilder()
.UseHttpSys(options =>
{
options.Authentication.Schemes = AuthenticationSchemes.Negotiate; // Microsoft.AspNetCore.Server.HttpSys
options.Authentication.AllowAnonymous = false;
}).ConfigureServices(services => services.AddSingleton<StatelessServiceContext>(serviceContext))
.UseContentRoot(Directory.GetCurrentDirectory())
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.SetBasePath(Directory.GetCurrentDirectory());
config.AddServiceFabricConfiguration(FabricRuntime.GetActivationContext(), options => {
options.IncludePackageName=false;
});
})
.UseStartup<Startup>()
.UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.None)
.UseUrls(url)
.Build();
}))
};
}
}
From that point forward everything is in the IConfiguration object as expected.
There are many ways to configure a service fabric application, and each approach will bring you to a different challenges.
SF team recommend the approach in the docs, because you can have a better version control of configurations, and makes harder to commit mistakes, as it is explicitly declared in a file, I've used a few different approaches because of limitations like yours, the follwoing approach might solve your problem:
Configure like the original approach, but with complex types stored as JSON values: it is the closest solution to the recommended design and you still can keep control of the configuration versions on source control.
It would be something like:
Settings.xml:
<?xml version="1.0" encoding="utf-8" ?>
<Settings xmlns... namespaces here...>
<Section Name="AuthorizationOptions">
<Parameter Name="Policies"/>
</Section>
</Settings>
ApplicationManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<ApplicationManifest ApplicationTypeName="ServiceFabric.ExampleType" ApplicationTypeVersion="1.0.0" xmlns:...namespaces....>
<Parameters>
<Parameter Name="Service.Example_ASPNETCORE_ENVIRONMENT" DefaultValue="" />
<Parameter Name="Service.Example_InstanceCount" DefaultValue="-1" />
<Parameter Name="Service.Example_AuthorizationOptions_Policies" DefaultValue="[]" />
</Parameters>
<ServiceManifestImport>
<ServiceManifestImport>
<ServiceManifestRef ServiceManifestName="Service.ExamplePkg" ServiceManifestVersion="1.0.0" />
<ConfigOverrides>
<ConfigOverride Name="Config">
<Settings>
<Section Name="AuthorizationOptions">
<Parameter Name="Policies" Value="[Service.Example_AuthorizationOptions_Policies]" />
</Section>
</Settings>
</ConfigOverride>
</ConfigOverrides>
<EnvironmentOverrides CodePackageRef="code">
<EnvironmentVariable Name="ASPNETCORE_ENVIRONMENT" Value="[Service.Example_ASPNETCORE_ENVIRONMENT]" />
</EnvironmentOverrides>
</ServiceManifestImport>
<DefaultServices>
<Service Name="Service.Example" ServicePackageActivationMode="ExclusiveProcess">
<StatelessService ServiceTypeName="Service.ExampleType" InstanceCount="[Service.Example_InstanceCount]">
<SingletonPartition />
</StatelessService>
</Service>
</DefaultServices>
</ApplicationManifest>
ApplicationParameters.xml
<?xml version="1.0" encoding="utf-8"?>
<Application Name="fabric:/ServiceFabric.Example" xmlns:...namespaces....>
<Parameters>
<Parameter Name="Service.Example_ASPNETCORE_ENVIRONMENT" Value="Development" />
<Parameter Name="Service.Example_InstanceCount" Value="-1" />
<Parameter Name="Service.Example_AuthorizationOptions_Policies" Value="[{'Name': 'User','Groups': ['Domain Users']}, {'Name': 'Admin','Groups': ['Administrators']}]" />
</Parameters>
</Application>
In your service code:
public class Policy
{
public string Name { get; set; }
public string[] Groups { get; set; }
}
var settings = this.Context.CodePackageActivationContext.GetConfigurationPackageObject("Config").Settings;
var authOptions = settings.Sections["AuthorizationOptions"].Parameters["Policies"].Value;
var obj = JsonConvert.DeserializeObject<Policy[]>(authOptions);
You could go a level further and store the entire
AuthorizationOptions as a JSON, but like said previously, more
generic it become, easier will be to commit mistakes and harder to find
configuration issues.

PostSharp.Samples.Xaml + EntityFramework

While converting PostSharp.Samples.Xaml sample to use EntityFramework, I am encountering "k__BackingField" problem mentioned in PostSharp inserting k__Backing Field into Entity Class, causing Database generation to fail. Can you show me how to use the proposed solution "Use MulticastAttributeUsageAttribute.AttributeTargets to restrict it to properties." without losing PostSharp's INotifyPropertyChanged? I am trying to persuade the stakeholders by showcasing both EF and PS.
Here's the change to AddressModel.cs
using System;
using System.ComponentModel;
using System.Text;
using PostSharp.Patterns.Contracts;
using PostSharp.Patterns.Model;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace PostSharp.Samples.Xaml
{
public class AddressModel : ModelBase
{
public int AddressId { get; set; }
[DisplayName("Address Line 1")]
[Patterns.Contracts.Required]
public string Line1 { get; set; }
[DisplayName("Address Line 2")]
public string Line2 { get; set; }
[Patterns.Contracts.Required]
public string Town { get; set; }
public string Country { get; set; }
public DateTime Expiration { get; set; }
[IgnoreAutoChangeNotification]
public TimeSpan Lifetime => DateTime.Now - Expiration;
[SafeForDependencyAnalysis]
public string FullAddress
{
get
{
var stringBuilder = new StringBuilder();
if (Line1 != null) stringBuilder.Append(Line1);
if (Line2 != null)
{
if (stringBuilder.Length > 0) stringBuilder.Append("; ");
stringBuilder.Append(Line2);
}
if (Town != null)
{
if (stringBuilder.Length > 0) stringBuilder.Append("; ");
stringBuilder.Append(Town);
}
if (Country != null)
{
if (stringBuilder.Length > 0) stringBuilder.Append("; ");
stringBuilder.Append(Country);
}
return stringBuilder.ToString();
}
}
[ForeignKey("AddressId")]
[Parent]
public virtual CustomerModel Person { get; set; }
}
}
app.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</configSections>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="PostSharp" publicKeyToken="b13fd38b8f9c99d7" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.0.37.0" newVersion="5.0.37.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
</startup>
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
<parameters>
<parameter value="mssqllocaldb" />
</parameters>
</defaultConnectionFactory>
<providers>
<provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
</providers>
</entityFramework>
</configuration>
CustomerModel.cs
using System.IO;
using System.Threading;
using PostSharp.Patterns.Collections;
using PostSharp.Patterns.Contracts;
using PostSharp.Patterns.Model;
using PostSharp.Patterns.Threading;
using System;
using System.ComponentModel.DataAnnotations;
using PostSharp.Extensibility;
namespace PostSharp.Samples.Xaml
{
public class CustomerModel : ModelBase
{
[Key]
public int CustomerId { get; set; }
public string FirstName { get; set; }
[Patterns.Contracts.Required]
public string LastName { get; set; }
public string Phone { get; set; }
public string Mobile { get; set; }
public string Email { get; set; }
[Child]
public AdvisableCollection<AddressModel> Addresses { get; set; }
[Reference]
public AddressModel PrincipalAddress { get; set; }
[Reader]
public void Save(string path)
{
using (var stringWriter = new StreamWriter(path))
{
// We need to make sure the object graph is not being modified when we save,
// and this is ensured by [ReaderWriterSynchronized] in ModelBase.
stringWriter.WriteLine($"UserID: {CustomerId}");
//Thread.Sleep(1000);
stringWriter.WriteLine($"FirstName: {FirstName}");
//Thread.Sleep(1000);
stringWriter.WriteLine($"LastName: {LastName}");
//Thread.Sleep(1000);
stringWriter.WriteLine($"Phone: {Phone}");
//Thread.Sleep(1000);
stringWriter.WriteLine($"Mobile: {Mobile}");
//Thread.Sleep(1000);
stringWriter.WriteLine($"Email: {Email}");
//Thread.Sleep(1000);
foreach (var address in Addresses)
{
//Thread.Sleep(1000);
if (address == PrincipalAddress)
stringWriter.WriteLine($"Principal address: {address}");
else
stringWriter.WriteLine($"Secondary address: {address}");
}
try
{
using (var ctx = new EFContext())
{
ctx.Person.Add(this);
ctx.SaveChanges();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
}
}
MainWindow.xaml
<Window x:Class="PostSharp.Samples.Xaml.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:xaml="clr-namespace:PostSharp.Samples.Xaml"
xmlns:controls="clr-namespace:PostSharp.Patterns.Model.Controls;assembly=PostSharp.Patterns.Xaml"
Title="Contact Form" Height="352.584" Width="352.753"
x:Name="Window">
<Window.DataContext>
<xaml:CustomerViewModel />
</Window.DataContext>
<DockPanel>
<ToolBarTray DockPanel.Dock="Top">
<ToolBar>
<Button Name="SaveButton" Command="{Binding ElementName=Window, Path=SaveCommand}">Save</Button>
<controls:UndoButton />
<controls:RedoButton />
</ToolBar>
</ToolBarTray>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="29*" />
<ColumnDefinition Width="316*" />
</Grid.ColumnDefinitions>
<Label Content="First Name:" HorizontalAlignment="Left" Margin="10,62,0,0" VerticalAlignment="Top"
Grid.ColumnSpan="2" />
<TextBox HorizontalAlignment="Left" Height="23" Margin="97.846,62,0,0" TextWrapping="Wrap"
Text="{Binding Path=Customer.FirstName, ValidatesOnExceptions=True}" VerticalAlignment="Top"
Width="189" Grid.Column="1" />
<Label Content="Last Name:" HorizontalAlignment="Left" Margin="10,88,0,0" VerticalAlignment="Top"
Grid.ColumnSpan="2" />
<TextBox HorizontalAlignment="Left" Height="23" Margin="97.846,92,0,0" TextWrapping="Wrap"
Text="{Binding Path=Customer.LastName, ValidatesOnExceptions=True}" VerticalAlignment="Top"
Width="189" Grid.Column="1" />
<Label Content="Principal Address:" HorizontalAlignment="Left" Margin="10,119,0,0" VerticalAlignment="Top"
RenderTransformOrigin="-0.105,-0.462" Grid.ColumnSpan="2" />
<ComboBox HorizontalAlignment="Left" Margin="97.846,120,0,0" VerticalAlignment="Top" Width="189"
ItemsSource="{Binding Path=Customer.Addresses}"
SelectedValue="{Binding Path=Customer.PrincipalAddress}" DisplayMemberPath="FullAddress"
Grid.Column="1" />
<xaml:FancyTextBlock HorizontalAlignment="Left" Margin="10,15,0,0" Text="{Binding Path=FullName}"
VerticalAlignment="Top" Width="214" Grid.ColumnSpan="2" />
<Label Content="Line1:" HorizontalAlignment="Left" Margin="11.846,150,0,0" VerticalAlignment="Top"
Grid.Column="1" />
<TextBox HorizontalAlignment="Left" Height="23" Margin="97.846,154,0,0" TextWrapping="Wrap"
Text="{Binding Path=Customer.PrincipalAddress.Line1, ValidatesOnExceptions=True}"
VerticalAlignment="Top" Width="189" Grid.Column="1" />
<Label Content="Town:" HorizontalAlignment="Left" Margin="12.846,176,0,0" VerticalAlignment="Top"
Grid.Column="1" />
<TextBox HorizontalAlignment="Left" Height="23" Margin="97.846,182,0,0" TextWrapping="Wrap"
Text="{Binding Path=Customer.PrincipalAddress.Town, ValidatesOnExceptions=True}"
VerticalAlignment="Top" Width="189" Grid.Column="1" />
<Label Content="User Id:" HorizontalAlignment="Left" Margin="11.846,210,0,0" VerticalAlignment="Top"
Grid.ColumnSpan="2" />
<TextBox HorizontalAlignment="Left" Height="23" Margin="97.846,210,0,0" TextWrapping="Wrap"
Text="{Binding Path=Customer.UserID, ValidatesOnExceptions=True}" VerticalAlignment="Top"
Width="189" Grid.Column="1" />
</Grid>
</DockPanel>
</Window>
MainWindow.xaml.cs
using System.Windows;
using System.Windows.Input;
using Microsoft.Win32;
using PostSharp.Patterns.Collections;
using PostSharp.Patterns.Model;
using PostSharp.Patterns.Recording;
using PostSharp.Patterns.Threading;
using PostSharp.Patterns.Xaml;
namespace PostSharp.Samples.Xaml
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
[NotifyPropertyChanged]
public partial class MainWindow : Window
{
/* private readonly */
public readonly CustomerModel customer = new CustomerModel
{
Email = "2",
Mobile = "3",
Phone = "4",
CustomerId = 1,
FirstName = "Jan",
LastName = "Novak",
Addresses = new AdvisableCollection<AddressModel>
{
new AddressModel
{
Line1 = "Saldova 1G",
Town = "Prague",
Country = "USA"
},
new AddressModel
{
Line1 = "Tyrsova 25",
Town = "Brno",
Country = "USA"
},
new AddressModel
{
Line1 = "Pivorarka 154",
Town = "Pilsen",
Country = "USA"
}
}
};
private readonly Recorder recorder;
public MainWindow()
{
// We need to have a local reference for [NotifyPropertyChanged] to work.
recorder = RecordingServices.DefaultRecorder;
InitializeComponent();
// Register our custom operation formatter.
RecordingServices.OperationFormatter = new MyOperationFormatter(RecordingServices.OperationFormatter);
// Create initial data.
var customerViewModel = new CustomerViewModel {Customer = customer};
customerViewModel.Customer.PrincipalAddress = customerViewModel.Customer.Addresses[0];
// Clear the initialization steps from the recorder.
recorder.Clear();
DataContext = customerViewModel;
}
[Command]
public ICommand SaveCommand { get; private set; }
public bool CanExecuteSave => recorder.UndoOperations.Count > 0;
private void ExecuteSave()
{
var openFileDialog = new SaveFileDialog();
if (openFileDialog.ShowDialog().GetValueOrDefault())
Save(openFileDialog.FileName);
}
[Background]
[DisableUI]
private void Save(string path)
{
customer.Save(path);
}
}
}
packages.config
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="EntityFramework" version="6.2.0" targetFramework="net461" />
<package id="PostSharp" version="5.0.40" targetFramework="net461" developmentDependency="true" />
<package id="PostSharp.Patterns.Aggregation" version="5.0.40" targetFramework="net461" developmentDependency="true" />
<package id="PostSharp.Patterns.Aggregation.Redist" version="5.0.40" targetFramework="net461" />
<package id="PostSharp.Patterns.Common" version="5.0.40" targetFramework="net461" developmentDependency="true" />
<package id="PostSharp.Patterns.Common.Redist" version="5.0.40" targetFramework="net461" />
<package id="PostSharp.Patterns.Model" version="5.0.40" targetFramework="net461" developmentDependency="true" />
<package id="PostSharp.Patterns.Model.Redist" version="5.0.40" targetFramework="net461" />
<package id="PostSharp.Patterns.Threading" version="5.0.40" targetFramework="net461" developmentDependency="true" />
<package id="PostSharp.Patterns.Threading.Redist" version="5.0.40" targetFramework="net461" />
<package id="PostSharp.Patterns.Xaml" version="5.0.40" targetFramework="net461" developmentDependency="true" />
<package id="PostSharp.Patterns.Xaml.Redist" version="5.0.40" targetFramework="net461" />
<package id="PostSharp.Redist" version="5.0.40" targetFramework="net461" />
</packages>
PostSharp.Samples.Xaml.csproj
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\packages\PostSharp.5.0.40\build\PostSharp.props" Condition="Exists('..\..\packages\PostSharp.5.0.40\build\PostSharp.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{B10B8456-75F7-4D68-9775-CC87517B07B6}</ProjectGuid>
<OutputType>WinExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>PostSharp.Samples.Xaml</RootNamespace>
<AssemblyName>PostSharp.Samples.Xaml</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
<LangVersion>6</LangVersion>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
<HintPath>..\..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="EntityFramework.SqlServer, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
<HintPath>..\..\packages\EntityFramework.6.2.0\lib\net45\EntityFramework.SqlServer.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="PostSharp, Version=5.0.40.0, Culture=neutral, PublicKeyToken=b13fd38b8f9c99d7">
<HintPath>..\..\packages\PostSharp.Redist.5.0.40\lib\net35-client\PostSharp.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="PostSharp.Patterns.Aggregation">
<HintPath>..\..\packages\PostSharp.Patterns.Aggregation.Redist.5.0.40\lib\net45\PostSharp.Patterns.Aggregation.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="PostSharp.Patterns.Common">
<HintPath>..\..\packages\PostSharp.Patterns.Common.Redist.5.0.40\lib\net46\PostSharp.Patterns.Common.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="PostSharp.Patterns.Model">
<HintPath>..\..\packages\PostSharp.Patterns.Model.Redist.5.0.40\lib\net40\PostSharp.Patterns.Model.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="PostSharp.Patterns.Threading">
<HintPath>..\..\packages\PostSharp.Patterns.Threading.Redist.5.0.40\lib\net45\PostSharp.Patterns.Threading.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="PostSharp.Patterns.Xaml">
<HintPath>..\..\packages\PostSharp.Patterns.Xaml.Redist.5.0.40\lib\net40\PostSharp.Patterns.Xaml.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Xaml">
<RequiredTargetFramework>4.0</RequiredTargetFramework>
</Reference>
<Reference Include="WindowsBase" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="App.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
<Page Include="FancyTextBlock.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="MainWindow.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Compile Include="AddressModel.cs" />
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="CustomerModel.cs" />
<Compile Include="CustomerViewModel.cs" />
<Compile Include="DisableUIAttribute.cs" />
<Compile Include="EFContext.cs" />
<Compile Include="FancyTextBlock.xaml.cs">
<DependentUpon>FancyTextBlock.xaml</DependentUpon>
</Compile>
<Compile Include="MainWindow.xaml.cs">
<DependentUpon>MainWindow.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
</ItemGroup>
<ItemGroup>
<Compile Include="ModelBase.cs" />
<Compile Include="MyOperationFormatter.cs" />
<Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<None Include="app.config" />
<None Include="packages.config" />
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
<AppDesigner Include="Properties\" />
<None Include="README.md" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105.The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\packages\PostSharp.5.0.40\build\PostSharp.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\PostSharp.5.0.40\build\PostSharp.props'))" />
<Error Condition="!Exists('..\..\packages\PostSharp.5.0.40\build\PostSharp.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\PostSharp.5.0.40\build\PostSharp.targets'))" />
</Target>
<Import Project="..\..\packages\PostSharp.5.0.40\build\PostSharp.targets" Condition="Exists('..\..\packages\PostSharp.5.0.40\build\PostSharp.targets')" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
Everything else remain the same, and you should be able to build it and see the exception in CustomerModel.cs line#80 when you change any field in the app and try to save.
You get following ex.Message;
One or more validation errors were detected during model generation:
PostSharp.Samples.Xaml.AddressModel: : EntityType 'AddressModel' has no key defined. Define the key for this EntityType.
<Person>k__BackingField: Name: The specified name is not allowed: '<Person>k__BackingField'.
PostSharp.Samples.Xaml.AddressModel_<Person>k__BackingField: Name: The specified name is not allowed: 'AddressModel_<Person>k__BackingField'.
AddressModel_<Person>k__BackingField_Source: Name: The specified name is not allowed: 'AddressModel_<Person>k__BackingField_Source'.
AddressModel_<Person>k__BackingField_Target: Name: The specified name is not allowed: 'AddressModel_<Person>k__BackingField_Target'.
AddressModels: EntityType: EntitySet 'AddressModels' is based on type 'AddressModel' that has no keys defined.
AddressModel_<Person>k__BackingField: Name: The specified name is not allowed: 'AddressModel_<Person>k__BackingField'.
A new file that was not in the original sample solution, EFContext.cs:
using System.Data.Entity;
namespace PostSharp.Samples.Xaml
{
public class EFContext :DbContext
{
public DbSet<CustomerModel> Person { get; set; }
}
}
You can use OnModelCreating and modelBuilder.Entity<T>.Ignore(x => x.Property) in runtime to ignore the failing property programmatically. The following demonstrates changes to EFContext:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
IgnorePostSharpProperties<CustomerModel>(modelBuilder);
IgnorePostSharpProperties<AddressModel>(modelBuilder);
base.OnModelCreating(modelBuilder);
}
public void IgnorePostSharpProperties<T>(DbModelBuilder modelBuilder)
where T : class
{
foreach (PropertyInfo property in typeof(T).GetProperties(BindingFlags.NonPublic | BindingFlags.Instance))
{
// We don't want properties that are compiler-generated with a special PostSharp name.
if (!property.IsDefined(typeof(CompilerGeneratedAttribute), false) || !property.Name.Contains("<"))
continue;
ParameterExpression paramExpression = Expression.Parameter(typeof(T));
Expression expression = Expression.Lambda(
Expression.Property(paramExpression, property.Name),
paramExpression
);
EntityTypeConfiguration<T> entityTypeConfig = modelBuilder.Entity<T>();
MethodInfo ignoreMethodDefinition = entityTypeConfig.GetType().GetMethod("Ignore", BindingFlags.Public | BindingFlags.Instance);
MethodInfo ignoreMethodInstance = ignoreMethodDefinition.MakeGenericMethod(property.PropertyType);
// Ignore the property.
ignoreMethodInstance.Invoke(entityTypeConfig, new object[] {expression});
}
}

Add custom variables in magento 2 to use in REST API

I am using some data in my web site like social links, contact address, contact phone, slider banners, I can use them as html in blocks or contact pages. But I am facing a problem, How to call them as REST API. I Already uses Magento2 API:
/V1/cmsBlock/:blockId
/V1/cmsPage/:pageId
But the respobse is html and it is so bad. any help?
For the data like social links, contact numbers etc I suggest you to add this as text in the configuration, to do so you can create a module that has the following structure:
app/code/Jsparo/Customapi/registration.php
<?php
\Magento\Framework\Component\ComponentRegistrar::register(
\Magento\Framework\Component\ComponentRegistrar::MODULE,
'Jsparo_Customapi',
__DIR__
);
app/code/Jsparo/Customapi/etc/module.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Module/etc/module.xsd">
<module name="Jsparo_Customapi" setup_version="1.0.0">
</module>
</config>
app/code/Jsparo/Customapi/etc/adminhtml/system.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
<system>
<tab id="jsparo" translate="label" sortOrder="1100">
<label>Jsparo</label>
</tab>
<section id="jsparo_social" translate="label" type="text" sortOrder="100" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Social</label>
<tab>jsparo</tab>
<resource>Jsparo_Social::config</resource>
<group id="facebook" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Facebook</label>
<field id="url" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="1" showInStore="0">
<label>Facebook Url</label>
</field>
</group>
</section>
</system>
</config>
app/code/Jsparo/Customapi/etc/webapi.xml
<?xml version="1.0"?>
<routes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Webapi:etc/webapi.xsd">
<route url="/V1/jsparo/facebook" method="GET">
<service class="Jsparo\Customapi\Api\FacebookInterface" method="getUrl"/>
<resources>
<resource ref="anonymous"/>
</resources>
</route>
</routes>
app/code/Jsparo/Customapi/etc/di.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<preference for="Jsparo\Customapi\Api\FacebookInterface" type="Jsparo\Customapi\Model\Facebook"/>
</config>
app/code/Jsparo/Customapi/Api/FacebookInterface.php
<?php
namespace Jsparo\Customapi\Api;
interface FacebookInterface {
/**
* #return string $url
*/
public function getUrl();
}
app/code/Jsparo/Customapi/Helper/Data.php
<?php
namespace Jsparo\Customapi\Helper;
use Magento\Framework\App\Helper\AbstractHelper;
class Data extends AbstractHelper {
const prefix = 'jsparo_social/';
private function moduleConfig($key) {
return $this->scopeConfig->getValue(self::prefix . $key);
}
public function getFacebookUrl() {
return $this->moduleConfig('facebook/url');
}
}
app/code/Jsparo/Customapi/Model/Facebook.php
<?php
namespace Jsparo\Customapi\Model;
use Jsparo\Customapi\Helper\Data;
class Facebook implements FacebookInterface {
private $helper;
public function __construct(
Data $helper
) {
$this->helper = $helper;
}
public function getUrl() {
return $this->helper->getFacebookUrl();
}
}
You might have to do some adjustments and add all the fields / api endpoints that you need to it.
You can add also Caching to your API by using the Magento\Framework\App\CacheInterface to avoid having to perform certain computations.
Notice that I created the endpoint with the anonymous role, so that it is not protected.
EDIT: I created a github repository where you can see the full source and edited the couple typos that were above. I assume that this module source code gets added in app/code/Jsparo/Customapi.

WCF with multiple services and

I'm trying to create a WCF that serves multiple rest services in one assembly. To test it, I have created 2 services with the same content that serves two interfaces.
I'm able to get metadata using the url
localhost/testwebservice/services/Service1.svc?wsdl or
localhost/testwebservice/services/Service2.svc?wsdl
, but when I try to execute a method
localhost/testwebservice/services/Service1.svc/GetData/0
, I get a DestinationUnreachable error.
The web.config file is
<system.serviceModel>
<services>
<service behaviorConfiguration="ServiceBehavior" name="test.Services.Service1">
<endpoint address="" binding="webHttpBinding"
contract="test.Contracts.IService1" />
<endpoint address="mex" binding="mexHttpBinding" contract="test.Contracts.IService1" />
</service>
<service behaviorConfiguration="ServiceBehavior" name="test.Services.Service2">
<endpoint address="" binding="webHttpBinding"
contract="test.Contracts.IService2" />
<endpoint address="mex" binding="mexHttpBinding" contract="test.Contracts.IService2" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="ServiceBehavior">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
</system.serviceModel>
Each .cs interface file is like this
namespace test.Contracts
{
[ServiceContract]
public interface IService1
{
[OperationContract]
[WebInvoke(Method = "GET", UriTemplate = "GetData/{value}")]
string GetData(string value);
}
}
Each .svc file is like this
namespace test.Services
{
public class Service1 : IService1
{
public string GetData(string value)
{
return string.Format("You entered: {0}", value);
}
}
}
I also, would like to access the webservice as localhost/testwebservice/services/Service1 and localhost/testwebservice/services/service2, instead of using .svc, but I can not config the webservice in order to remove the .svc suffix. I have tried to use the address of the endpoint, but i'm not able to make it run.

Zend Navigation Find Page and Render Menu with its Subpages

I am putting together a Zend Navigation for a site with 4 different levels of access: Guest, Member1, Member2, and Admin.
My navigation XML looks something like this:
<?xml version="1.0" encoding="UTF-8"?>
<config>
<nav>
<default>
<label>Home</label>
<controller>index</controller>
<action>index</action>
<resource>index</resource>
<privilege>index</privilege>
<pages>
<home>
...
</home>
<signin>
...
</signin>
<signup>
...
</signup>
</pages>
</default>
<member1>
<label>Member1 Main</label>
<controller>member1</controller>
<action>index</action>
<resource>member1</resource>
<privilege>index</privilege>
<pages>
<dashboard>
...
</dashboard>
<settings>
<label>Settings</label>
<controller>auth</controller>
<action>editpassword</action>
<resource>auth</resource>
<privilege>editpassword</privilege>
<class>settings</class>
<title>User settings</title>
<pages>
<account>
...
</account>
<logout>
...
</logout>
</pages>
</settings>
</pages>
</member1>
<member2>
<label>Member2 Main</label>
<controller>member2</controller>
<action>index</action>
<resource>member2</resource>
<privilege>index</privilege>
<pages>
<dashboard>
...
</dashboard>
<profile>
...
</profile>
<settings>
<label>Settings</label>
<controller>auth</controller>
<action>editpassword</action>
<resource>auth</resource>
<privilege>editpassword</privilege>
<class>settings</class>
<pages>
<account>
...
</account>
<logout>
...
</logout>
</pages>
</settings>
</pages>
</member2>
<admin>
<label>Dashboard</label>
<controller>admin</controller>
<action>index</action>
<resource>admin</resource>
<privilege>index</privilege>
<pages>
<dashboard>
...
</dashboard>
<logout>
...
</logout>
</pages>
</admin>
</nav>
</config>
Since I am using submenus and want consistency for top menu, I want to use a Zend's findBy feature to locate current user's status and display that menu. This is done as such:
if ( $this->user ) {
$submenu = $this->navigation()->findOneByLabel('Member1 Main');
$options = array(
'ulClass' => 'navigation',
'renderParents' => true,
'minDepth' => null,
'maxDepth' => null
);
echo $this->navigation()->menu()->renderMenu($submenu, $options);
} else {
echo $this->navigation()->menu()->setUlClass('navigation')->setOnlyActiveBranch(true)->setMinDepth(1)->setMaxDepth(1);
}
My Bootstrap bit for Nav is pretty generic and looks like this:
function _initNavigation()
{
$this->bootstrap('layout');
$layout = $this->getResource('layout');
$view = $layout->getView();
$navConfig = new Zend_Config_Xml(APPLICATION_PATH . '/configs/navigation.xml', 'nav');
$navigation = new Zend_Navigation($navConfig);
$front = Zend_Controller_Front::getInstance();
$myPlagin = $front->getPlugin('My_Controller_Plugin_Acl');
$view->navigation($navigation)->setAcl($myPlagin->getMyAcl())
->setRole($myPlagin->getMyUserRole());
}
Now, I can get the "Member1 Main" page to appear, but it only shows that one page, but what I need to render is that page's whole submenu. It seems that findOneByLabel only looks up that particular page only and not its descendants. Is there a way to pull the whole submenu?
Thanks.
Turns out I had to use findAllByLabel to get the expected result. Thanks #RockyFord