Xamarin PCL connected to WCF Service throws System.typeload exception on save - entity-framework

I have the following WCF Service contract:
[ServiceContract]
public interface IGMesDataService
{
[OperationContract]
List<HejMessage> GetHejMessages();
[OperationContract]
void SaveHejMessage(HejMessage msg);
}
The type HejMessage is an Entity and I have added its attributes through the text templates and the result is as follows:
[DataContract]
public partial class HejMessage
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string Text { get; set; }
[DataMember]
public double Latitude { get; set; }
[DataMember]
public double Longitude { get; set; }
}
The code in the xamarin.forms pcl is as follows:
in the app constructor:
GMesDataServiceClient client = new GMesDataServiceClient();
client.GetHejMessagesCompleted += Client_GetHejMessagesCompleted;
client.GetHejMessagesAsync();
and the event handler:
private void Client_GetHejMessagesCompleted(object sender, GetHejMessagesCompletedEventArgs e)
{
foreach(HejMessage msg in e.Result)
AllHejMessages.Add(msg);
}
On a "post message" button click:
private void PostButton_Clicked(object sender, EventArgs e)
{
HejMessage msg = new HejMessage
{
Text = "Awesome item",
Latitude = 100,
Longitude = 100
};
AllHejMessages.Add(msg);
GMesDataServiceClient client = new GMesDataServiceClient();
client.SaveHejMessageCompleted += Client_SaveHejMessageCompleted;
client.SaveHejMessageAsync(msg);
}
the completed event here just catches the exception for me.
When I run the wcf service locally everything works smashingly. When I deploy the service the GetHejMessages call still works but the SaveHejMessage returns the following System.TypeLoadException:
Message:
{"Inheritance security rules violated while overriding member:
'System.Data.Entity.Utilities.TaskExtensions+CultureAwaiter`1<T>.UnsafeOnCompleted(System.Action)'.
Security accessibility of the overriding method must match the security accessibility of the method being overriden."}
Stack Trace:
at System.Data.Entity.Core.Objects.ObjectContext.SaveChangesInternalAsync(SaveOptions options, Boolean executeInExistingTransaction, CancellationToken cancellationToken)\r\n
at System.Data.Entity.Core.Objects.ObjectContext.SaveChangesAsync(SaveOptions options, CancellationToken cancellationToken)\r\n
at System.Data.Entity.Internal.InternalContext.SaveChangesAsync(CancellationToken cancellationToken)\r\n
at System.Data.Entity.Internal.LazyInternalContext.SaveChangesAsync(CancellationToken cancellationToken)\r\n
at System.Data.Entity.DbContext.SaveChangesAsync(CancellationToken cancellationToken)\r\n
at System.Data.Entity.DbContext.SaveChangesAsync()\r\n
at GMesService.GMesDataService.SaveHejMessage(HejMessage msg) in C:\\Users\\Eric\\documents\\visual studio 2015\\Projects\\GMes\\GMesService\\GMesDataService.cs:line 22\r\n
at SyncInvokeSaveHejMessage(Object , Object[] , Object[] )\r\n
at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs)\r\n
at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)\r\n
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)\r\n
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage41(MessageRpc& rpc)\r\n
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage4(MessageRpc& rpc)\r\n
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage31(MessageRpc& rpc)\r\n
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage3(MessageRpc& rpc)\r\n
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage2(MessageRpc& rpc)\r\n
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage11(MessageRpc& rpc)\r\n
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage1(MessageRpc& rpc)\r\n
at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)"
Any Ideas?

Related

Get current logged in user in DbContext

For audit purposes I'm trying to get the current logged in user in my DbContext. However I'm having some issues with this. A few things to take into account:
In Blazor Server we have to use AddDbContextFactory
IHttpContextAccessor returns no result in deployed website (might be because IHttpContextAccessor is not thread safe?)
I created a custom DbContext that injects AuthenticationStateProvider.
public partial class CustomDbContext : DbContext
{
private AuthenticationStateProvider _authenticationStateProvider;
#region construction
public CustomDbContext ()
{
}
public CustomDbContext (AuthenticationStateProvider stateProvider)
{
_authenticationStateProvider = stateProvider;
}
[ActivatorUtilitiesConstructor]
public CustomDbContext (DbContextOptions<CustomDbContext> options, AuthenticationStateProvider stateProvider) : base(options)
{
_authenticationStateProvider = stateProvider;
}
public CustomDbContext(DbContextOptions<CustomDbContext> options) : base(options)
{
}
#endregion
...
In this DbContext, when overwriting the SaveChanges I get the User and their claims:
var state = await _authenticationStateProvider.GetAuthenticationStateAsync();
var userIdClaim = state.User.Claims.FirstOrDefault(c => c.Type == "userId")?.Value;
userId = userIdClaim != null && !string.IsNullOrEmpty(userIdClaim ) ? userIdClaim : string.Empty;
...
However when I call .CreateDbContext(); on the injected DbContextFactory, I get the following exception:
'Cannot resolve scoped service
'Microsoft.AspNetCore.Components.Authorization.AuthenticationStateProvider'
from root provider.'
I've found some topics about this, but the suggested solution there is to create a custom DbContextFactory that is scoped. But then you lose the reason why you are using the DbContextFactory, no?
Any ideas on how to solve this?
Thank you
The DBContextFactory is a singleton registered in the root application DI container, while the AuthenticationStateProvider is a scoped service that is registered in the Hub session DI container. You can't access a lower order service from a higher order service.
You need to rethink your design and provide the user information from whatever scoped service is making whatever call to need a DbConbtext.
Additional Information
I'm not sure what your data pipeline looks like so this example uses the Blazor template weather forecast.
First a View Service that components inject and use.
This injects the AuthenticationStateProvider. It gets the current user for each request and passes it to the data pipeline in a request object.
public class WeatherForecastViewService
{
private AuthenticationStateProvider _authenticationStateProvider; // scoped service
private WeatherForecastService _weatherForecastService; //Singleton Service
public WeatherForecastViewService(AuthenticationStateProvider authenticationStateProvider, WeatherForecastService weatherForecastService)
{
_authenticationStateProvider = authenticationStateProvider;
_weatherForecastService = weatherForecastService;
}
public async ValueTask SaveWeatherForecast(WeatherForecast record)
{
var user = await GetCurrentUser();
var request = new RecordRequest<WeatherForecast>(record, user );
await _weatherForecastService.SaveRecord(request);
}
private async ValueTask<ClaimsPrincipal> GetCurrentUser()
{
var state = await _authenticationStateProvider.GetAuthenticationStateAsync();
return state.User ?? new ClaimsPrincipal();
}
}
Here are the request and result objects:
public readonly struct RecordRequest<TRecord>
{
public TRecord Record { get; init; }
public ClaimsPrincipal Identity { get; init; }
public RecordRequest(TRecord record, ClaimsPrincipal identity)
{
this.Record = record;
this.Identity = identity;
}
}
public record RecordResult
{
public bool SuccessState { get; init; }
public string Message { get; init; }
private RecordResult(bool successState, string? message)
{
this.SuccessState = successState;
this.Message = message ?? string.Empty;
}
public static RecordResult Success(string? message = null)
=> new RecordResult(true, message);
public static RecordResult Failure(string message)
=> new RecordResult(false, message);
}
And here's the singleton data service
public class WeatherForecastDataService
{
// This is a singleton
private readonly IDbContextFactory<DbContext> _factory;
public WeatherForecastDataService(IDbContextFactory<DbContext> factory)
=> _factory = factory;
public async ValueTask<RecordResult> SaveRecord(RecordRequest<WeatherForecast> request)
{
if (!request.Identity.IsInRole("SomeRole"))
return RecordResult.Failure("User does not have authority");
// simulates some async DB activity
await Task.Delay(100);
// Get your DbContext from the injected Factory
// using var dbContext = this.factory.CreateDbContext();
// do your db stuff
return RecordResult.Success();
}
}
PS I haven'y actually run this code so there may be some typos!
IHttpContextAccessor returns no result in deployed website (might be because IHttpContextAccessor is not thread safe?)
Nothing to do with whether IHttpContextAccessor is not thread safe... It's simply because the HttpContext object is not available in Blazor Server App, as communication between the client side (browser) and server side is done through the SignalR protocol, not HTTP. But there is a way how to access the HttpContext object before the Blazor App is rendered, as the initial call to the app is always made through HTTP request; that is, when you enter a url into the address bar of your browser and hit the enter button. See here how to do that...
The following code snippet describes how to inject an AuthenticationStateProvider into the ApplicationDbContext object created by default when you select Individual Accounts in Blazor Server App.
Copy and test. It should work...
Data/ApplicationDbContext.cs
public class ApplicationDbContext : IdentityDbContext
{
public DbSet<Employee> Employees { get; set; }
private AuthenticationStateProvider _authenticationStateProvider;
public ApplicationDbContext(DbContextOptions<ApplicationDbContext>
options, AuthenticationStateProvider stateProvider)
: base(options)
{
_authenticationStateProvider = stateProvider;
}
public override async Task<int>
SaveChangesAsync(CancellationToken cancellationToken)
{
var stateProvider = await
_authenticationStateProvider.GetAuthenticationStateAsync();
if (stateProvider.User.Identity.IsAuthenticated)
{
Console.WriteLine("Authenticated User name: " +
stateProvider.User.Identity.Name);
}
// Delegate the saving action to the base class
return await base.SaveChangesAsync(cancellationToken);
}
}
Create an Employee Repository class service:
EmployeeRepository.cs
using <put here the namespace of your app>.Data;
using <put here the namespace of your app>.Models;
using Microsoft.EntityFrameworkCore;
public class EmployeeRepository
{
private readonly ApplicationDbContext ApplicationDbContext;
public EmployeeRepository(ApplicationDbContext
applicationDbContext)
{
ApplicationDbContext = applicationDbContext;
}
public async Task<Employee> CreateEmployee(Employee employee)
{
CancellationTokenSource cancellationTokenSource = new
CancellationTokenSource();
CancellationToken token = cancellationTokenSource.Token;
await ApplicationDbContext.Employees.AddAsync(employee);
await ApplicationDbContext.SaveChangesAsync(token);
return employee;
}
}
Index.razor
#inject EmployeeRepository EmployeeRepository
#using <Put here....>.Models
<button type="button" #onclick="SaveEmployee">Save Employee</button>
#if (emp != null)
{
<div>#emp.ID.ToString()</div>
<div>#emp.FirstName</div>
<div>#emp.LastName</div>
<div>#emp.City</div>
}
#code
{
private Employee emp;
private async Task SaveEmployee()
{
Employee employee = new Employee { FirstName = "Joana", LastName = "Brown", City = "London" };
emp = await EmployeeRepository.CreateEmployee(employee);
}
}
Create model class Employee:
Models/Employee.cs
public class Employee
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string City { get; set; }
}
Note: To test this code, you'll have to create A Blazor Server App with Individual Accounts, create the database, including the Employees table
Last but not least: Startup
// Created by the default template
//services.AddDbContext<ApplicationDbContext>(options =>
// options.UseSqlServer(
// Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<IdentityUser>(options =>
options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddDbContextFactory<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")),
ServiceLifetime.Scoped);
// This is your code...
services.AddScoped<ApplicationDbContext>(p =>
p.GetRequiredService<IDbContextFactory<ApplicationDbContext>>
().CreateDbContext());
services.AddScoped<EmployeeRepository>();
services.AddScoped<AuthenticationStateProvider,
RevalidatingIdentityAuthenticationStateProvider<IdentityUser>>();
services.AddDatabaseDeveloperPageExceptionFilter();
services.AddSingleton<WeatherForecastService>();
UPDATE:
but does that no against the the recommendations of Microsoft? They ae suggesting to always use using
var context = DbFactory.CreateDbContext();
You mean:
using var context = DbFactory.CreateDbContext();
No, it is not against the recommendations of Microsoft. It's another way to instantiate the DbContext. I did it that way in order to stick to this code by you:
services.AddScoped<ApplicationDbContext>(p => p.GetRequiredService<IDbContextFactory<ApplicationDbContext>>().CreateDbContext());
Anyhow, these are the changes you should make in order to reflect "Microsoft's recommendations"
Change:
services.AddScoped<ApplicationDbContext>(p => p.GetRequiredService<IDbContextFactory<ApplicationDbContext>>().CreateDbContext());
To:
services.AddScoped<ApplicationDbContext>();
Change:
private readonly ApplicationDbContext ApplicationDbContext;
public EmployeeRepository(ApplicationDbContext
applicationDbContext)
{
ApplicationDbContext = applicationDbContext;
}
To:
private readonly IDbContextFactory<ApplicationDbContext>
DbFactory;
public EmployeeRepository(IDbContextFactory<ApplicationDbContext>
_DbFactory)
{
DbFactory = _DbFactory;
}
And change:
await ApplicationDbContext.Employees.AddAsync(employee);
await ApplicationDbContext.SaveChangesAsync(token);
To:
await context.Employees.AddAsync(employee);
await context.SaveChangesAsync(token);
Also add:
using var context = DbFactory.CreateDbContext();
at the beginning of the EmployeeRepository.CreateEmployee method
Run and test.
Hope this work...
New Version
Data/ApplicationDbContext.cs
public class ApplicationDbContext : IdentityDbContext
{
public DbSet<Employee> Employees { get; set; }
private AuthenticationStateProvider _authenticationStateProvider;
public ApplicationDbContext(DbContextOptions<ApplicationDbContext>
options, AuthenticationStateProvider stateProvider)
: base(options)
{
_authenticationStateProvider = stateProvider;
}
public override async Task<int>
SaveChangesAsync(CancellationToken cancellationToken)
{
var stateProvider = await
_authenticationStateProvider.GetAuthenticationStateAsync();
if (stateProvider.User.Identity.IsAuthenticated)
{
Console.WriteLine("Authenticated User name: " +
stateProvider.User.Identity.Name);
}
// Delegate the saving action to the base class
return await base.SaveChangesAsync(cancellationToken);
}
}
Create an Employee Repository class service:
EmployeeRepository.cs
using <put here the namespace of your app>.Data;
using <put here the namespace of your app>.Models;
using Microsoft.EntityFrameworkCore;
public class EmployeeRepository
{
private readonly IDbContextFactory<ApplicationDbContext> DbFactory;
public EmployeeRepository(IDbContextFactory<ApplicationDbContext> _DbFactory)
{
DbFactory = _DbFactory;
}
public async Task<Employee> CreateEmployee(Employee
employee)
{
using var context = DbFactory.CreateDbContext();
// CancellationTokenSource provides the token and have authority to cancel the token
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
CancellationToken token = cancellationTokenSource.Token;
await context.Employees.AddAsync(employee);
await context.SaveChangesAsync(token);
return employee;
}
}
Index.razor
#inject EmployeeRepository EmployeeRepository
#using <Put here....>.Models
<button type="button" #onclick="SaveEmployee">Save Employee</button>
#if (emp != null)
{
<div>#emp.ID.ToString()</div>
<div>#emp.FirstName</div>
<div>#emp.LastName</div>
<div>#emp.City</div>
}
#code
{
private Employee emp;
private async Task SaveEmployee()
{
Employee employee = new Employee { FirstName = "Joana", LastName = "Brown", City = "London" };
emp = await EmployeeRepository.CreateEmployee(employee);
}
}
Create model class Employee:
Models/Employee.cs
public class Employee
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string City { get; set; }
}
Note: To test this code, you'll have to create A Blazor Server App with Individual Accounts, create the database, including the Employees table
Last but not least: Startup
public void ConfigureServices(IServiceCollection services)
{
services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddDbContextFactory<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")),
ServiceLifetime.Scoped);
services.AddScoped<ApplicationDbContext>();
services.AddScoped<EmployeeRepository>();
services.AddScoped<AuthenticationStateProvider, RevalidatingIdentityAuthenticationStateProvider<IdentityUser>>();
services.AddDatabaseDeveloperPageExceptionFilter();
services.AddSingleton<WeatherForecastService>();
}

Format of the initialization string does not conform to specification starting at index 0 when using postgresql database in .net 6 app

trying to update my database via dotnet ef database update, however the build starts and succeeds and then errors out with the following:
System.ArgumentException: Format of the initialization string does not conform to specification starting at index 0.
at System.Data.Common.DbConnectionOptions.GetKeyValuePair(String connectionString, Int32 currentPosition, StringBuilder buffer, Boolean useOdbcRules, String& keyname, String& keyvalue)
at System.Data.Common.DbConnectionOptions.ParseInternal(Dictionary`2 parsetable, String connectionString, Boolean buildChain, Dictionary`2 synonyms, Boolean firstKey)
at System.Data.Common.DbConnectionOptions..ctor(String connectionString, Dictionary`2 synonyms, Boolean useOdbcRules)
at System.Data.Common.DbConnectionStringBuilder.set_ConnectionString(String value)
at Npgsql.NpgsqlConnectionStringBuilder..ctor(String connectionString)
at Npgsql.NpgsqlConnection.GetPoolAndSettings()
at Npgsql.NpgsqlConnection.set_ConnectionString(String value)
at Npgsql.NpgsqlConnection..ctor(String connectionString)
at Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlRelationalConnection.CreateDbConnection()
at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.get_DbConnection()
at Microsoft.EntityFrameworkCore.Diagnostics.RelationalLoggerExtensions.MigrateUsingConnection(IDiagnosticsLogger`1 diagnostics, IMigrator migrator, IRelationalConnection connection)
at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.Migrate(String targetMigration)
at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.UpdateDatabase(String targetMigration, String connectionString, String contextType)
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.UpdateDatabaseImpl(String targetMigration, String connectionString, String contextType)
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.UpdateDatabase.<>c__DisplayClass0_0.<.ctor>b__0()
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)
Format of the initialization string does not conform to specification starting at index 0.
worth mentioning that my appsettings.json is in the api solution, and my dbcontext file is in my data access layer
appsettings.json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"prattleDatabase": "Host=localhost; Database=prattle; Username=develop; Password=develop123;"
}
}
programs.cs
builder.Services.AddDbContext <PrattleContext> (option =>
option.UseNpgsql(builder.Configuration.GetConnectionString("prattleDatabase")));
and my dbcontext:
public class PrattleContext : DbContext
{
protected readonly IConfiguration _configuration;
public PrattleContext()
{
}
public PrattleContext(IConfiguration configuration)
{
_configuration = configuration;
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var builder = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
var dbConnectionInfo = builder.Build().GetSection("ConnectionStrings").GetSection("prattleDatabase").Value;
optionsBuilder.UseNpgsql("dbConnectionInfo");
}
public DbSet<User> Users { get; set; }
public DbSet<Message> Messages { get; set; }
}
You don't need to build configuration again.
This should work for you.
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var connectionstring = _configuration.GetConnectionString("prattleDatabase");
optionsBuilder.UseNpgsql(connectionstring);
}

NullReferenceException: at DevExpress.Persistent.BaseImpl.EFCore.AuditTrail.AuditDataItem.UpdateDefaultString

My PurchaseOrder business object contained
public int? ReceivedByStaffId { get; set; }
[ForeignKey("ReceivedByStaffId")]
public virtual Staff ReceivedBy { get; set; }
And I was using the Audit module.
My Controller was throwing the following error on objectSpace.CommitChanges();
System.Exception: SaveChanges: inner:
ex:System.NullReferenceException: Object reference not set to an instance of an object.
at DevExpress.Persistent.BaseImpl.EFCore.AuditTrail.AuditDataItem.UpdateDefaultString(IEFCoreWeakReference objectForUpdate, EntityEntry entityEntry, Object key)
at DevExpress.Persistent.BaseImpl.EFCore.AuditTrail.AuditDataItem.UpdatePersistentData(List`1 weakReferences)
at DevExpress.Persistent.BaseImpl.EFCore.AuditTrail.AuditDataContainer.AttachAuditData(DbContext dbContext)
at DevExpress.Persistent.BaseImpl.EFCore.AuditTrail.AuditDataContainer.SaveAuditData(DbContext dbContext)
at DevExpress.Persistent.BaseImpl.EFCore.AuditTrail.AuditTrailService.AuditedDbContext_SavedChanges(Object sender, SavedChangesEventArgs e)
at Microsoft.EntityFrameworkCore.DbContext.SaveChanges(Boolean acceptAllChangesOnSuccess)
at MyApp2.Module.BusinessObjects.MyAppDbContext.SaveChanges() in D:\dev\MyApp2\MyApp2.Module\BusinessObjects\MyAppDbContext.cs:line 462
stacktrace: at System.Environment.get_StackTrace()
at MyApp2.Module.BusinessObjects.MyAppDbContext.SaveChanges() in D:\dev\MyApp2\MyApp2.Module\BusinessObjects\MyAppDbContext.cs:line 462
at DevExpress.ExpressApp.EFCore.EFCoreObjectSpace.DoCommit()
at DevExpress.EntityFrameworkCore.Security.SecuredEFCoreObjectSpace.DoCommit()
at DevExpress.ExpressApp.BaseObjectSpace.CommitChanges()
at MyApp2.Module.Win.Features.PurchFeature.POStateViewController.actPurchStateMenu_Execute(Object sender, SingleChoiceActionExecuteEventArgs args) in D:\dev\MyApp2\MyApp2.Module.Win\Features\PurchFeature\POStateViewController.cs:line 207
at DevExpress.ExpressApp.Actions.ActionBase.ExecuteCore(Delegate handler, ActionBaseEventArgs eventArgs)
at DevExpress.ExpressApp.Actions.SingleChoiceAction.DoExecute(ChoiceActionItem selectedItem)
at DevExpress.ExpressApp.Templates.ActionControls.Binding.SingleChoiceActionBinding.ActionControl_Execute(Object sender, SingleChoiceActionControlExecuteEventArgs e)
at DevExpress.XtraBars.BarItem.OnClick(BarItemLink link)
at DevExpress.XtraBars.BarItemLink.OnLinkClick()
at DevExpress.XtraBars.BarButtonItemLink.OnLinkAction(BarLinkAction action, Object actionArgs)
at DevExpress.XtraBars.ViewInfo.BarSelectionInfo.UnPressLink(BarItemLink link)
at DevExpress.XtraBars.Controls.CustomLinksControl.OnMouseUp(MouseEventArgs e)
at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
at System.Windows.Forms.Control.WndProc(Message& m)
at DevExpress.XtraBars.Controls.CustomControl.WndProc(Message& msg)
at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, WM msg, IntPtr wparam, IntPtr lparam)
at Interop.User32.DispatchMessageW(MSG& msg)
at Interop.User32.DispatchMessageW(MSG& msg)
at System.Windows.Forms.Application.ComponentManager.Interop.Mso.IMsoComponentManager.FPushMessageLoop(UIntPtr dwComponentID, msoloop uReason, Void* pvLoopData)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(msoloop reason, ApplicationContext context)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(msoloop reason, ApplicationContext context)
at System.Windows.Forms.Application.Run()
at DevExpress.ExpressApp.Win.WinApplication.Start()
at MyApp2.Win.Program.Main() in D:\dev\MyApp2\MyApp2.Win\Program.cs:line 112
at MyApp2.Module.BusinessObjects.MyAppDbContext.SaveChanges() in D:\dev\MyApp2\MyApp2.Module\BusinessObjects\MyAppDbContext.cs:line 468
at DevExpress.ExpressApp.EFCore.EFCoreObjectSpace.DoCommit()
at DevExpress.EntityFrameworkCore.Security.SecuredEFCoreObjectSpace.DoCommit()
at DevExpress.ExpressApp.BaseObjectSpace.CommitChanges()
at MyApp2.Module.Win.Features.PurchFeature.POStateViewController.actPurchStateMenu_Execute(Object sender, SingleChoiceActionExecuteEventArgs args) in D:\dev\MyApp2\MyApp2.Module.Win\Features\PurchFeature\POStateViewController.cs:line 207
Since the Foreign Key is nullable the navigation property also needs to be nullable
public int? ReceivedByStaffId { get; set; }
[ForeignKey("ReceivedByStaffId")]
public virtual Staff? ReceivedBy { get; set; }

ASP.NET core EF ambiguous constructor

I use ASP.NET Core with .NET 5 and recently wanted to change from local development to Azure Web production mode.
Locally I use SQLite and everything works fine, on production I want to use Azure SQL.
However when I want to migrate my database, I get a rather long exception:
System.Exception: Could not resolve a service of type 'Server.Calendars.CalendarDataContext' for the parameter 'calendarDataContext' of method 'Configure' on type 'Server.Startup'.
---> System.InvalidOperationException: Unable to activate type 'Server.Calendars.CalendarDataContext'. The following constructors are ambiguous:
Void .ctor(Microsoft.Extensions.Configuration.IConfiguration)
Void .ctor(Microsoft.EntityFrameworkCore.DbContextOptions`1[Server.Calendars.CalendarDataContext])
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateConstructorCallSite(ResultCache lifetime, Type serviceType, Type implementationType, CallSiteChain callSiteChain)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.TryCreateExact(ServiceDescriptor descriptor, Type serviceType, CallSiteChain callSiteChain, Int32 slot)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.TryCreateExact(Type serviceType, CallSiteChain callSiteChain)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateCallSite(Type serviceType, CallSiteChain callSiteChain)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.<>c__DisplayClass7_0.<GetCallSite>b__0(Type type)
at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.GetCallSite(Type serviceType, CallSiteChain callSiteChain)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.CreateServiceAccessor(Type serviceType)
at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
at Microsoft.AspNetCore.Hosting.ConfigureBuilder.Invoke(Object instance, IApplicationBuilder builder)
--- End of inner exception stack trace ---
at Microsoft.AspNetCore.Hosting.ConfigureBuilder.Invoke(Object instance, IApplicationBuilder builder)
at Microsoft.AspNetCore.Hosting.ConfigureBuilder.<>c__DisplayClass4_0.<Build>b__0(IApplicationBuilder builder)
at Microsoft.AspNetCore.Hosting.GenericWebHostBuilder.<>c__DisplayClass15_0.<UseStartup>b__1(IApplicationBuilder app)
at Microsoft.Extensions.DependencyInjection.AutoRegisterMiddleware.<>c__DisplayClass4_0.<Configure>b__0(IApplicationBuilder app)
at Microsoft.AspNetCore.Mvc.Filters.MiddlewareFilterBuilderStartupFilter.<>c__DisplayClass0_0.<Configure>g__MiddlewareFilterBuilder|0(IApplicationBuilder builder)
at Microsoft.AspNetCore.Server.IIS.Core.IISServerSetupFilter.<>c__DisplayClass2_0.<Configure>b__0(IApplicationBuilder app)
at Microsoft.AspNetCore.HostFilteringStartupFilter.<>c__DisplayClass0_0.<Configure>b__0(IApplicationBuilder app)
at Microsoft.AspNetCore.Hosting.GenericWebHostService.StartAsync(CancellationToken cancellationToken)
My class CalendarDataContext .cs for Azure SQL
public class CalendarDataContext : DbContext
{
public DbSet<CalendarEntry> CalendarEntries { get; set; }
protected readonly IConfiguration Configuration;
public CalendarDataContext(IConfiguration configuration)
{
Configuration = configuration;
}
public CalendarDataContext(DbContextOptions<CalendarDataContext> options)
: base(options)
{ }
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
if (!options.IsConfigured)
{
options.UseSqlServer(Configuration.GetConnectionString("CalendarDatabase"));
}
}
}
and CalendarDataContextSqlite.cs for SQLite
public class CalendarDataContextSqlite : CalendarDataContext
{
public CalendarDataContextSqlite(IConfiguration configuration) : base(configuration) { }
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
if (!options.IsConfigured)
{
var databaseName = Configuration.GetConnectionString("CalendarDatabase");
var databasePath = PathHelper.DataPath(databaseName);
options.UseSqlite("Data Source=" + databasePath);
}
}
}
I think the issue is with the line CalendarDataContext(DbContextOptions<CalendarDataContext> options) that I need for creating a temporary InMemory-Database for my tests.
How can I make this ambiguous constructor less ambiguous?
Edit: Add startup.cs
public class Startup
{
public IWebHostEnvironment Environment { get; }
public Startup(IConfiguration configuration, IWebHostEnvironment environment)
{
Environment = environment;
}
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
if (Environment.IsProduction())
{
services.AddDbContext<CalendarDataContext>();
}
else if (Environment.IsDevelopment())
{
services.AddDbContext<CalendarDataContext, CalendarDataContextSqlite>();
}
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app,
IWebHostEnvironment env,
CalendarDataContext calendarDataContext)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
calendarDataContext.Database.Migrate();
}
}
Firstly, Add IConfiguration as a local member in your Startup.cs
IConfiguration Configuration;
public IWebHostEnvironment Environment { get; }
public Startup(IConfiguration configuration, IWebHostEnvironment environment)
{
Configuration = configuration;
Environment = environment;
}
then Register a configured CalendarDataContext or CalendarDataContextSqlite in Startup
public void ConfigureServices(IServiceCollection services)
{
if (Environment.IsProduction())
{
services.AddDbContext<CalendarDataContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("CalendarDatabase"));
}
else if (Environment.IsDevelopment())
{
services.AddDbContext<CalendarDataContext, CalendarDataContextSqlite>(options => {
var databaseName = Configuration.GetConnectionString("CalendarDatabase");
var databasePath = PathHelper.DataPath(databaseName);
options.UseSqlite("Data Source=" + databasePath);
});
}
}
Then, CalendarDataContext:
public class CalendarDataContext : DbContext
{
public DbSet<CalendarEntry> CalendarEntries { get; set; }
public CalendarDataContext(DbContextOptions<CalendarDataContext> options)
: base(options) { }
protected CalendarDataContext(DbContextOptions options)
: base(options) { }
}
And, CalendarDataContextSqlite:
public class CalendarDataContextSqlite : CalendarDataContext
{
public CalendarDataContextSqlite(DbContextOptions<CalendarDataContextSqlite> options)
: base(options) { }
}
Now,
No need for the OnConfiguring in the context classes.
In production you'll have a configured CalendarDataContext to be injected wherever the constructor asks for a CalendarDataContext.
And for developerment you'll have a configured CalendarDataContextSqlite to be injected wherever the constructor asks for a CalendarDataContext.
That configured context will also be injected into Startup.Configure so you can migrate your database.

Exception, with two references in base class with Entity Framework 5

I had this problem in a project, since I don't want to give the whole domain scope, I've tried to simplify the classes as much as possible.
Basically I have a base class BaseClass, where two properties are referencing a a class that is Extending BaseClass => ClassA
It works if I remove ClassB.
It works if I remove one of the two references.
It works when I don't extend ClassA from the BaseClass
But not in this constellation:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Entity;
using System.Threading;
namespace EFTestbed
{
public abstract class BaseClass
{
public int Id { get; set; }
public ClassA ClassA1 { get; set; }
public ClassA ClassA2 { get; set; }
}
public class ClassA : BaseClass
{
public string TitleA { get; set; }
}
public class ClassB : BaseClass
{
public string TitleB { get; set; }
}
public class Context : DbContext
{
public DbSet<ClassA> ClassAs { get; set; }
public DbSet<ClassB> ClassBs { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ClassA>()
.HasOptional(e => e.ClassA1).WithOptionalDependent();
modelBuilder.Entity<ClassA>()
.HasOptional(e => e.ClassA2).WithOptionalDependent();
modelBuilder.Entity<ClassB>()
.HasOptional(e => e.ClassA1).WithOptionalDependent();
modelBuilder.Entity<ClassB>()
.HasOptional(e => e.ClassA2).WithOptionalDependent();
}
}
public class Program
{
static void Main(string[] args)
{
Thread.CurrentThread.CurrentCulture =
Thread.CurrentThread.CurrentUICulture =
new System.Globalization.CultureInfo("en-GB");
System.Console.WriteLine("Creating & Updating database...");
Database.SetInitializer(new DropCreateDatabaseAlways<Context>());
using (var context = new Context())
{
context.ClassAs.Add(new ClassA() { TitleA = "Test1" });
System.Console.WriteLine(
string.Format("Number of objects written to the database: {0}",
context.SaveChanges()));
}
System.Console.Write("Press Enter to continue...");
System.Console.ReadLine();
}
}
}
I always get this Exception "Sequence contains more than one element"
How do I get it to work?
Here is the stack trace of the Exception:
at System.Linq.Enumerable.Single[TSource](IEnumerable`1 source)
at System.Data.Entity.ModelConfiguration.Configuration.Properties.Navigation.NavigationPropertyConfiguration.ConfigureDependentBehavior(EdmAssociationType associationType, EdmModel model, EntityTypeConfiguration entityTypeConfiguration)
at System.Data.Entity.ModelConfiguration.Configuration.Properties.Navigation.NavigationPropertyConfiguration.Configure(EdmNavigationProperty navigationProperty, EdmModel model, EntityTypeConfiguration entityTypeConfiguration)
at System.Data.Entity.ModelConfiguration.Configuration.Types.EntityTypeConfiguration.ConfigureAssociations(EdmEntityType entityType, EdmModel model)
at System.Data.Entity.ModelConfiguration.Configuration.Types.EntityTypeConfiguration.Configure(EdmEntityType entityType, EdmModel model)
at System.Data.Entity.ModelConfiguration.Configuration.ModelConfiguration.ConfigureEntities(EdmModel model)
at System.Data.Entity.ModelConfiguration.Configuration.ModelConfiguration.Configure(EdmModel model)
at System.Data.Entity.DbModelBuilder.Build(DbProviderManifest providerManifest, DbProviderInfo providerInfo)
at System.Data.Entity.DbModelBuilder.Build(DbConnection providerConnection)
at System.Data.Entity.Internal.LazyInternalContext.CreateModel(LazyInternalContext internalContext)
at System.Data.Entity.Internal.RetryLazy`2.GetValue(TInput input)
at System.Data.Entity.Internal.LazyInternalContext.InitializeContext()
at System.Data.Entity.Internal.InternalContext.Initialize()
at System.Data.Entity.Internal.InternalContext.GetEntitySetAndBaseTypeForType(Type entityType)
at System.Data.Entity.Internal.Linq.InternalSet`1.Initialize()
at System.Data.Entity.Internal.Linq.InternalSet`1.get_InternalContext()
at System.Data.Entity.Internal.Linq.InternalSet`1.ActOnSet(Action action, EntityState newState, Object entity, String methodName)
at System.Data.Entity.Internal.Linq.InternalSet`1.Add(Object entity)
at System.Data.Entity.DbSet`1.Add(TEntity entity)
at EFTestbed.Program.Main(String[] args) in d:\users\sasp1de\documents\visual studio 2010\Projects\EFTestbed\EFTestbed\TestEF\Program.cs:line 54
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()
This is now fixed in the latest version of EF:
http://entityframework.codeplex.com/workitem/546