IModel.GetRelationalModel is not copying annotations - c#

When converting from an IModel to a RelationalModel either by:
Using the .GetRelationalModel() extension for IModel.
Using RelationalModel constructor which takes an IModel
Annotations from the orignal IModel are not preserved. Meaning functions like IMigrationsModelDiffer.GetDifferences() don't work as intended. Manually copying the annotations after the fact also doesn't help as GetDifferences() won't use runtime annotations.
Is there an alternative way to create the RelationalModel which preserves these annotations? Or an alternative to GetDifferences which will use runtime annotations?
var oldRelationalModel = oldModel.GetRelationalModel();
Log.Information("Old IModel Annotations: {0}", oldModel.GetAnnotations().Count());
Log.Information("Old RelationalModel Annotations: {0}", oldRelationalModel.GetAnnotations().Count());
var newRelationalModel = newModel.GetRelationalModel();
Log.Information("Old IModel Annotations: {0}", newModel.GetAnnotations().Count());
Log.Information("Old RelationalModel Annotations: {0}", newRelationalModel.GetAnnotations().Count());
[15:57:46 INF] Old IModel Annotations: 5
[15:57:46 INF] Old RelationalModel Annotations: 0
[15:57:46 INF] Old IModel Annotations: 5
[15:57:46 INF] Old RelationalModel Annotations: 0

Related

NSwag-generated TypeScript uses camelCase while .net5 webapi configured to return PascalCase JSON

I have a .net5 webapi controller, which returns JSON responses in PascalCase:
[{"Email":"user#email.com","Id":16820,"Name":"User LastName","Phone":null}, ...
... achieved by either of the following configurations, the included System.Text.Json serializer:
services.AddControllers()
.AddJsonOptions(opt => opt.JsonSerializerOptions.PropertyNamingPolicy = null);
or by referencing Microsoft.AspNetCore.Mvc.NewtonsoftJson and configuring for NewtonSoftJSON.net:
services.AddControllers()
.AddNewtonsoftJson(c => c.SerializerSettings.ContractResolver = new DefaultContractResolver());
We don't use JsonProperty or anything of that sort to decorate members of the DTOs
Using NSwagStudio
or CLI ,
the generated OpenApi spec is correctly generating the models in PascalCase, but the generated TypeScript interfaces and classes use camelCase, e.g.:
export interface IContact {
email?: string | undefined;
id: number;
name?: string | undefined;
phone?: string | undefined;
...
}
We need the generated interface and class members to be in PascalCase.
In addition to switching back to Newtonsoft, I also played with multiple inputs and settings in NSwagStudio to generate the above interface members in PascalCase to no avail. My understanding was that NSwag defers the serialization to the c# project settings so that generated code matches the .net core serializer output, but either that's not happening in my case or I am misinterpreting something.
Is it possible to configure nSwag to generate TypeScript (Angular template) classes and interfaces (incl. their members) in PascalCase?

Custom serialization in Kafka using CSharp

I want to write a custom serializer for kafka in CSharp. I've searched a lot and I couldn't find a good reference on how to write custom serializer for kafka in a dotnet language.
Any thoughts on how to write one with dotnet?
I just find the answer to this question.
The library to use for kafka in dotnet is provided by confluent.
Kafka .NET Client
There should be a serialization class implementing the interface :
Confluent.Kafka.ISerializer<T>
Normally we should create the producer via ProducerBuilder class :
Confluent.Kafka.ProducerBuilder<TKey, TValue>
There is a method to this class to set serializers for both key and value.
It is a little different than the original documentation for custom serializer in Kafka which instructing to set the name of the custom serializer class in the producer configuration.
Following is the code to set the value serializer:
var config = new ProducerConfig
{
BootstrapServers = "localhost:9092",
ClientId = "thiPC"
};
var producerBuilder = new ProducerBuilder<Null, Customer>(config);
producerBuilder.SetValueSerializer(new CustomerSerializer()); // <------ The Magic Code
var producer = producerBuilder.Build();
The sample code for the Serialization class is like this;
public class CustomerSerializer : Confluent.Kafka.ISerializer<Customer>
{
public byte[] Serialize(Customer data, SerializationContext context)
{ ..........}
}
There is nothing important for the Customer class in my example it is a normal class just to hold the customer properties.
In serialization class, you should return a byte[] in the Serialize method which is obvious!
I hope this would be useful for folks implementing Kafka with dotnet.

How to check DBContext is disposed or not?

I want to share a DB context with another method called from outside (inherited class) without creating a new context unless it is being disposed. I want to check the context is disposed so that I could create new context.
It's rest api. There is a bulk upload for multiple entities and I want to share the transaction so if one fail, it will not be committed to DB
Regardless of the comments questioning design quality, valid scenarios exist were the dbContext could be in a disposed state, such as (not a complete list):
For example (within injected dbContext MVC services):
your service iterates though a lower tier of one-or-more service calls, possibly using asynchronous socket handler on a lower tier API library, with each response using the parent requester dbContext.
Your service calls a database job, (asynchronous task or not).
Exception handling logging to database (if the dbContext is already lost - avoid loss of logging debug details)
Note: Long running processes using dbContext like this should follow good practice of avoiding dbContext bloat such as using AsNoTracking() method were possible - as bloat can quickly become a concern.
Performance consideration:
Most trusted option is to recreate the dbContext on each child (api call/async task), but this may incur undesired performance overheads, such as when dealing with 1000's of api iterative calls and atomic unit transactions are not viable.
Solution Tested Using Framework:
Entity Type: Microsoft.EntityFrameworkCore.DbContext
Version=5.0.16.0, Culture=neutral, PublicKeyToken=adb9793829ddae60
Warnings:
Lots of warning advice available on this type of extended dbContext use, such use should be used with caution/avoided where possible.
See warning details : c-sharp-working-with-entity-framework-in-a-multi-threaded-server
Extend you DbContext with partial class Or add method to your existing extended partial class.
FYI - Please comment if still working on updated EntityFrameworkCore libraries.
public partial class FooDbContext : DbContext
{
// Using Type: 5.0.16.0 EntityFrameworkCore.DbContext (confirm if working with any core library upgrades)
public bool IsDisposed()
{
bool result = true;
var typeDbContext = typeof(DbContext);
var isDisposedTypeField = typeDbContext.GetField("_disposed", BindingFlags.NonPublic | BindingFlags.Instance);
if (isDisposedTypeField != null)
{
result = (bool)isDisposedTypeField.GetValue(this);
}
return result;
}
}
Usage:
if (fooDbContext == null || fooDbContext.IsDisposed())
{
// Recreate context
}

BreezeJs + EF + Angular capabilities with DTO's

i'm evaluating Breeze.Js for a large enterprise, data oriented, Angular 5 application in order to take advantage of the following features that are missing in the vanilla Angular framework:
client side data store
client side model state tracking
client side model validation rules
bulk data persistence (SaveChanges() method to persist all entities).
For test purposes i've written the following simple BreezeController in my ASP.NET WebApi + EntityFramework server side:
[EnableCors(origins: "*", headers: "*", methods: "*")]
[BreezeController]
public class PeopleController : ApiController
{
private AdventureWorksDbContext db = new AdventureWorksDbContext();
#region "Breeze"
readonly EFContextProvider<AdventureWorksDbContext> _contextProvider =
new EFContextProvider<AdventureWorksDbContext>();
// ~/breeze/todos/Metadata
[HttpGet]
public string Metadata()
{
return System.Text.Encoding.UTF8.GetString(AdventureWorks.WebApi.Properties.Resources.WebApiMetadata);
}
// ~/breeze/todos/Todos
// ~/breeze/todos/Todos?$filter=IsArchived eq false&$orderby=CreatedAt
[HttpGet]
public IQueryable<PersonDTO> GetPeople()
{
return db.People.ProjectTo<PersonDTO>();
}
// ~/breeze/todos/SaveChanges
[HttpPost]
public SaveResult SaveChanges(Newtonsoft.Json.Linq.JObject saveBundle)
{
return _contextProvider.SaveChanges(saveBundle);
}
#endregion
}
As you can see in my example (it uses AdventureWorks DB) i've done the following modifications:
1) "GetPeople()" endpoint returns a queryable of DTO ("ProjectTo" extension is provided by Automapper). I need to do this in order to shape the model in a usable way for the client, avoid recursions, deep dive in the schema, big fields serialization and so on.
2) "Metadata()" endpoint returns a string resource that represents metadata of the DTO class. I builded it using "PocoMetadata" tool of the "Breeze Tooling Suite" (https://github.com/Breeze/breeze.tooling). This is needed because i can't return the _contextProvider.Metadata() result as long as i'm using DTO's and not EF POCO class.
Now, if in my Angular 5 client i issue an ODATA query like the following i can see that executeQuery() method actually works:
export class BreezeDataStoreComponent implements OnInit {
private _em: EntityManager;
constructor() {
this._em = new EntityManager({
serviceName: 'http://localhost:31328/breeze/People'
});
}
ngOnInit() {
const query = EntityQuery.from('GetPeople')
.where('FirstName', FilterQueryOp.StartsWith, 'F')
.orderBy('LastName', true);
this._em.executeQuery(query).then(res => {
// Here i can get all People instances.
// Now i try to get the first, edit the name and saveChanges.
(res.results[0] as any).FirstName = 'Franklino';
this._em.saveChanges().then(saveResult => {
const test = saveResult.entities;
});
});
}
}
Unfortunately problems comes with SaveChanges().
When the Angular client calls that method, in my server side i get the following error:
System.InvalidOperationException: Sequence contains no matching
element
I think it's due to the fact that i'm calling SaveChanges() over an EF context provider passing a JObject bundle referred to DTO instead of POCO class.
So my question is:
Is it possible to use BreezeJs query and bulk persistence (SaveChanges() method) using DTO's? It's a pretty common need in big data-centric enterprise applications since i think it's a bad practice exposing EF POCOs on WebApi.
should i rely instead over a classic WebApi that respond to the POST\PUT\DELETE HTTP verbs? In that case, how to configure Breeze client in order to contact those endpoints instead of "SaveChanges" when persisting data?
If Breeze is not suitable for this needs are there other technolgies that provides the 4 abovementioned points?
Thank you very much.
To make SaveChanges work with your DTOs, you would need to either
Write your own method to unpack the JObject saveBundle, or
Use the BeforeSaveChanges method to modify the dictionary of DTOs and replace them with entities that EF understands.
Number 2 seems like the better choice. If you do not have a 1:1 match between entities and DTOs, some logic would be required when doing the mapping.

Using a Custom UserStore and RoleStore in ASP.NET 5

I have implemented a custom RoleStore and a custom UserStore for my project that is using ASP.NET 5, MVC 6, EF 7, and Identity 3. However - I can't quite figure out how to configure identity to use my custom RoleStore and custom UserStore instead of the usual offering. How can I reconfigure the system to use my custom classes?
PS: I also have custom User and Role class.
Solution
Here's what I ended up doing. First, I uninstalled the 'Identity Entity Framework' package from my project. This sent a few things missing, so I re-implemented them (read: copied them from here), and put them in a 'Standard' namespace to indicate they hadn't been customised. I now have a 'Security' namespace that contains the following:
Standard
IdentityRole.cs
IdentityRoleClaim.cs
IdentityUser.cs
IdentityUserClaim.cs
IdentityUserLogin.cs
IdentityUserRole.cs
BuilderExtensions.cs
IdentityDbContext.cs
Resources.resx
Role.cs
RoleStore.cs
User.cs
UserStore.cs
The items shown in bold contain project specific functionality.
The code that allows me to use the custom stores is in the 'BuilderExtensions' file, which contains the following class:
public static class BuilderExtensions
{
public static IdentityBuilder AddCustomStores<TContext, TKey>(this IdentityBuilder builder)
where TContext : DbContext
where TKey : IEquatable<TKey>
{
builder.Services.TryAdd(GetDefaultServices(builder.UserType, builder.RoleType, typeof(TContext), typeof(TKey)));
return builder;
}
private static IServiceCollection GetDefaultServices(Type userType, Type roleType, Type contextType, Type keyType)
{
var userStoreType = typeof(UserStore<,,,>).MakeGenericType(userType, roleType, contextType, keyType);
var roleStoreType = typeof(RoleStore<,,>).MakeGenericType(roleType, contextType, keyType);
var services = new ServiceCollection();
services.AddScoped(
typeof(IUserStore<>).MakeGenericType(userType),
userStoreType);
services.AddScoped(
typeof(IRoleStore<>).MakeGenericType(roleType),
roleStoreType);
return services;
}
}
This then allows me to write the following in my Startup.cs file:
services.AddIdentity<User, Role>()
.AddCustomStores<PrimaryContext, string>()
.AddDefaultTokenProviders();
And the custom store will be used. Note that PrimaryContext is the name of my whole-project DbContext. it inherits from IdentityDbContext.
Discussion
I could have probably kept the 'Identity Entity Framework' package and saved myself duplicating the contents of the 'Standard' namespace, but I chose not to so that I can keep my identifiers short and unambiguous.
Check out this section
Reconfigure application to use new storage provider in Overview of Custom Storage Providers for ASP.NET Identity
Specifically "If the default storage provider was included in your project, you must remove the default provider and replace it with your provider."
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
var manager = new ApplicationUserManager(new YourNewUserStore(context.Get<ExampleStorageContext>()));
...
}

Categories