Entity Framework - Reuse Complex Type - c#

I have an Entity in Code First Entity framework that currently looks like this:
public class Entity
{
// snip ...
public string OriginalDepartment { get; set; }
public string OriginalQueue { get; set; }
public string CurrentDepartment { get; set; }
public string CurrentQueue { get; set; }
}
I would like to create Complex Type for these types as something like this:
public class Location
{
public string Department { get; set; }
public string Queue { get; set; }
}
I'd like to use this same type for both Current and Original:
public Location Original { get; set; }
public Location Current { get; set; }
Is this possible, or do I need to create two complex types CurrentLocation and OriginalLocation?
public class OriginalLocation
{
public string Department { get; set; }
public string Queue { get; set; }
}
public class CurrentLocation
{
public string Department { get; set; }
public string Queue { get; set; }
}

It is supported out of box, you do not need to create two complex types.
You can also configure your complex types explicitely with model builder
modelBuilder.ComplexType<Location>();
To customize column names, you should configure them from parent entity configuration
public class Location
{
public string Department { get; set; }
public string Queue { get; set; }
}
public class MyEntity
{
public int Id { get; set; }
public Location Original { get; set; }
public Location Current { get; set; }
}
public class MyDbContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.ComplexType<Location>();
modelBuilder.Entity<MyEntity>().Property(x => x.Current.Queue).HasColumnName("myCustomColumnName");
}
}
This will map MyEntity.Current.Queue to myCustomName column

Related

Return all derived models in EF Core (TPH )

I have a task which requires me to return all models from a table using inheritance (TPH).
I have a model class called WorkflowInstance and a derived class CustomWorkflowInstance (which has a string property). There is a discriminator of course.
I want to know of a way where I can return all the elements without considering the discriminator
public class WorkflowInstance : Entity, ITenantScope, ICorrelationScope
{
public WorkflowInstance();
public SimpleStack<ActivityScope> Scopes { get; set; }
public SimpleStack<ScheduledActivity> ScheduledActivities { get; set; }
public WorkflowFault? Fault { get; set; }
public HashSet<BlockingActivity> BlockingActivities { get; set; }
public IDictionary<string, IDictionary<string, object?>> ActivityData { get; set; }
public WorkflowOutputReference? Output { get; set; }
public WorkflowInputReference? Input { get; set; }
public Variables Variables { get; set; }
public Instant? FaultedAt { get; set; }
public Instant? CancelledAt { get; set; }
public Instant? FinishedAt { get; set; }
public Instant? LastExecutedAt { get; set; }
public Instant CreatedAt { get; set; }
public string? Name { get; set; }
public string? ContextId { get; set; }
public string? ContextType { get; set; }
public string CorrelationId { get; set; }
public WorkflowStatus WorkflowStatus { get; set; }
public int Version { get; set; }
public string? TenantId { get; set; }
public string DefinitionId { get; set; }
public ScheduledActivity? CurrentActivity { get; set; }
public string? LastExecutedActivityId { get; set; }
}
public class CustomWorkflowInstance : WorkflowInstance
{
public Guid UserId { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<WorkflowInstance>()
.HasDiscriminator<int>("Discriminator")
.HasValue(0)
.HasValue<WorkflowInstance>(0)
.HasValue<CustomWorkflowInstance>(1);}
I want to find a way to query the table as it is meaning adding where clause FinishedAt > etc (the issue is that UserId exist only in derived class but all the data is in base class where discriminator always equals 0)
so by doing select * from WorkflowInstanceTABLE where Used="xx" it automatically adds the where discriminator = 1 (because I wrote _dbContext.CustomWorkflowInstance which contains the userId in question.

How do I set up a one-to-many relationship using Entity Framework Core?

I'm working on a trucking API using Entity Framework (EF) Core. Basic CRUD operations are working fine using the repository pattern. There is an error in
configurations I am implementing, however.
I want to obtain multiple trailers and trucks associated with single load, reflecting the one-to-many relationship.
public class LoadConfiguration : IEntityTypeConfiguration<Load>
{
public void Configure(Microsoft.EntityFrameworkCore.Metadata.Builders.EntityTypeBuilder<Load> builder)
{
builder.Property(p=>p.Id).IsRequired();
builder.HasOne(t=>t.Customer).WithMany().HasForeignKey(p=>p.CustomerId);
builder.Property(p=>p.LoadedFrom).IsRequired();
builder.HasMany(p=>p.Trailer).WithOne().HasForeignKey(t=>t.TrailerId);
builder.HasMany(p=>p.Truck).WithOne().HasForeignKey(t=>t.TruckId);
builder.Property(p=>p.Destination).IsRequired();
}
}
public class Truck:BaseEntity
{
public int PlateNo { get; set; }
public string ModelName { get; set; }
public Location StateCode { get; set; }
public int PollutionCertificateValidity { get; set; }
public int DateOfPurchase { get; set; }
public int FitnessCertificateValidity { get; set; }
}
public class Load:BaseEntity
{
public Customer Customer { get; set; }
public int CustomerId { get; set; }
public string LoadedFrom { get; set; }
public Trailer Trailer { get; set; }
public int TrailerId { get; set; }
public Truck Truck { get; set; }
public int TruckId { get; set; }
public string Destination { get; set; }
}
public class Trailer:BaseEntity
{
public int TrailerCapacity { get; set; }
public Truck Truck { get; set; }
public int TruckId { get; set; }
}
public class BaseEntity
{
public int Id { get; set; }
}
A one-to-many relationship is defined by using navigation collections, that has the capacity to hold many Trucks and Trailers. You can choose the collection type freely, but I would suggest ICollection generic type.
Modify your Load class as follows:
public class Load:BaseEntity
{
public Customer Customer { get; set; }
public int CustomerId { get; set; }
public string LoadedFrom { get; set; }
public string Destination { get; set; }
// navigation collections
public ICollection<Trailer> Trailers { get; set; }
public ICollection<Truck> Trucks { get; set; }
}
You will then be able to set up the relationship in your LoadConfiguration class by using
the pluralized name:
builder.HasMany(p=>p.Trailers).WithOne();
builder.HasMany(p=>p.Trucks).WithOne();
.. even though EF Core will be smart enough to figure out the relation by convention so the fluent configuration is redundant.

Automapper generic source to known target resolver issue

It's been a while since i've used automapper, but i'm almost sure that my situation should be possible.
Setup
I created the following mapping configuration:
var map = cfg.CreateMap<TSource, Structure>();
So in my situation the source is a generic type (unknown) and the target type is Structure (known).
A possible option for the TSource type could be:
public class DataChannel
{
public string Id { get; set; }
public string Description { get; set; }
public string Ean { get; set; }
public DateTimeOffset ValidFrom { get; set; }
public bool IsManual { get; set; }
public string Type { get; set; }
public string Unit { get; set; }
public string Address { get; set; }
public string BuildingId { get; set; }
}
The target Structure object looks like this:
public class Structure : IStructure
{
public Structure()
{
Children = new List<Structure>();
Properties = new List<StructureProperty>();
}
public int Id { get; set; }
public ICollection<StructureProperty> Properties { get; set; }
public List<Structure> Children { get; set; }
}
Situation
For example, I would like the string properties "Unit" and "Type" to be added as a StructureProperty object to the Properties collection of the Structure entity.
map.ForMember(c => c.Properties, m => m.MapFrom<StructurePropertyResolver<TSource>>());
How can this be done?

Automapper not working on nested objects

I currently working with .net core 2.1 and try to use automapper for nested objects to convert model to dto and dto to model. When every field is mapped correctly issue appears with relationship mapping.
Models
public class DropdownValue
{
public int Id { get; set; }
public string Value { get; set; }
public int PropertyId { get; set; }
public Property Property { get; set; }
}
public class Property
{
public int Id { get; set; }
public string Title { get; set; }
public ValueTypes ValueType { get; set; }
public InputTypes InputType { get; set; }
public List<DropdownValue> DropdownValues { get; set; }
}
Dtos
public class DropdownValueDto
{
public int Id { get; set; }
public string Value { get; set; }
public PropertyDto Property { get; set; }
}
public class PropertyDto
{
public int Id { get; set; }
public string Title { get; set; }
public InputTypes InputType { get; set; }
public ValueTypes ValueType { get; set; }
}
Mapper
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<Property, PropertyDto>();
CreateMap<DropdownValue, DropdownValueDto>();
}
}
Usage in handler
_mapper.Map<List<Models.DropdownValue>, List<DropdownValueDto>>(dropdownValues)
I always use automapper mapping tool in .net 4x framework projects but when i develop .net core projects, i always use and recommend mapster mapping tool. It is pretty fast and simple ! Benchmark Results It also solves your problem. You can check the example usage where is below.
First create a mapper class.
public static class Mapper
{
public static void CreateMap()
{
TypeAdapterConfig<Property, PropertyDto>
.NewConfig();
TypeAdapterConfig<DropdownValue, DropdownValueDto>
.NewConfig();
}
}
Initialize in startup
public Startup(IHostingEnvironment env)
{
// other stuffs
// Mapping
Mapper.CreateMap();
}
Usage
dropdownValues.Adapt<List<Models.DropdownValue>, List<DropdownValueDto>>()
//Models
public class DropdownValue
{
public int Id { get; set; }
public string Value { get; set; }
public int PropertyId { get; set; }
public Property Property { get; set; } = new Property();
}
public class Property
{
public int Id { get; set; }
public string Title { get; set; }
public ValueTypes ValueType { get; set; } = new ValueTypes();
public InputTypes InputType { get; set; } = new InputTypes();
public List<DropdownValue> DropdownValues { get; set; } = new List<DropdownValue>();
}
//Dtos
public class DropdownValueDto
{
public int Id { get; set; }
public string Value { get; set; }
public PropertyDto Property { get; set; } = new PropertyDto();
}
public class PropertyDto
{
public int Id { get; set; }
public string Title { get; set; }
public InputTypes InputType { get; set; } = new InputTypes();
public ValueTypes ValueType { get; set; } = new ValueTypes();
}

Error while retrieving data from database view using code first

I have a view in my SQL database. All I want is to retrieve data from that view.
I have added POCO class.
namespace WFPersistence.DataModel
{
public class Instance
{
public Guid InstanceId { get; set; }
public DateTime? PendingTimer { get; set; }
public DateTime? CreationTime { get; set; }
public DateTime? LastUpdatedTime { get; set; }
public int? ServiceDeploymentId { get; set; }
public string SuspensionExceptionName { get; set; }
public string SuspensionReason { get; set; }
public string ActiveBookmarks { get; set; }
public string CurrentMachine { get; set; }
public string LastMachine { get; set; }
public string ExecutionStatus { get; set; }
public bool? IsInitialized { get; set; }
public bool? IsSuspended { get; set; }
public bool? IsCompleted { get; set; }
public byte? EncodingOption { get; set; }
public byte[] ReadWritePrimitiveDataProperties { get; set; }
public byte[] WriteOnlyPrimitiveDataProperties { get; set; }
public byte[] ReadWriteComplexDataProperties { get; set; }
public byte[] WriteOnlyComplexDataProperties { get; set; }
public string IdentityName { get; set; }
public string IdentityPackage { get; set; }
public long? Build { get; set; }
public long? Major { get; set; }
public long? Minor { get; set; }
public long? Revision { get; set; }
}
public class Instances : Collection<Instance>
{
}
}
This is how I am trying to map with view.
public class WFPersistenceStore : DbContext
{
public WFPersistenceStore() : base("WFPersist")
{
}
public DbSet<Instance> PersistedInstances { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Instance>().ToTable("System.Activities.DurableInstancing.Instances");
}
}
This is how I am connecting with view
using (var PersistStore = new WFPersistenceStore())
{
var result = from t in PersistStore.PersistedInstances
select t;
////
///
}
I am getting this error:
An unhandled exception of type 'System.ArgumentException' occurred in
RentalHost.exe
Additional information: The database name
'System.Activities.DurableInstancing.Instances' is invalid. Database
names must be of the form [.].
Your method should be like
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Instance>().ToTable("Instances");
}
I have resolved my issue by just putting the following line inside the constructor of my context class (i.e. WFPersistenceStore).
Database.SetInitializer<WFPersistenceStore>(null);
This wasn't mentioned anywhere clearly in official documents if i am not wrong.
The above line needed for EF6 version only but not required for earlier versions of EF.

Categories