Entity Framework Core: Combine owned properties with inheritance - c#

Given are the following classes:
public class Rule
{
public long Id { get; set; }
public string Filter { get; set; }
public RuleAction Action { get; set; }
}
public abstract class RuleAction
{
}
public class RuleAction1 : RuleAction
{
public string Value { get; set; }
}
public class RuleAction2 : RuleAction
{
public decimal Percent { get; set; }
}
I want to map these classes to the following table layout. I use Entity Framework Core Preview 2.
Table "Rule"
- Id
- Filter
- ActionDiscriminator
- Value // only set if the object in Action is typeof(RuleAction1)
- Percent // only set if the object in Action is typeof(RuleAction2)
The important part is that "Action" is not mapped to a separate table. I know i can map the property as a "Owned property" like described in this article (OwnsOne): https://blogs.msdn.microsoft.com/dotnet/2017/06/28/announcing-ef-core-2-0-preview-2/ but this dosn't seem to work in combination with inheritance, at least i couldn't find an example.
Anybody knows how to combine owned properties with inheritance?

Could you just do something like this:
public class RuleAction1 : RuleAction
{
public string Value { get; set; }
public decimal Percent { get; set; } = null;
}
public class RuleAction2 : RuleAction
{
public decimal Percent { get; set; }
public string Value { get; set; } = null;
}
That way those value match the table schema but just default to null values. Or you could do something like this:
public abstract class RuleAction
{
public string Value { get; set; } = null;
public decimal Percent { get; set; } = null;
}
public class RuleAction1 : RuleAction
{
}
public class RuleAction2 : RuleAction
{
}
I could be way off, sorry if this is only slowing you down.

Related

Combine two classes from a table per hierarchy inheritance structure in Entity Framework

We're are using the table per hierarchy inheritance technique with Entity Framework.
I'll take the example classes from this page as the basic classes: Table per Hierarchy Inheritance - The Model. Please don't take the class names and properties as set. I didn't want to expose anything critical. This question is just about the structure.
public class Contract
{
public int ContractId { get; set; }
public DateTime StartDate { get; set; }
public int Months { get; set;}
public decimal Charge { get; set; }
}
public class MobileContract : Contract
{
public string MobileNumber { get; set; }
}
public class TvContract : Contract
{
public PackageType PackageType { get; set; }
}
public class BroadBandContract : Contract
{
public int DownloadSpeed { get; set; }
}
public enum PackageType
{
S, M, L, XL
}
Now, in our case we have the situation that there is a contract that can be both, a TvContract and a BroadBandContract, e.g. a MultiContract. We don't know how to deal with this since we cannot inherit from two classes.
If we query the DbSet<TvContract> or DbSet<BroadBandContract> from the we also want to get the MultiContracts.
Does someone have an idea how to deal with this scenario?
I see our current class structure is of inheritance. While to me it looks like composition. A contract may contain different subcontracts and once the contract expires, subcontracts too expire. However, subcontracts are of different types and that is what you could use for your advantage. You may also see a requirement where someone would add or remove the packages they have thus contract type is not really static. I would design to handle that. I would have also preferred an end date or Months individually on SubContract allowing for variable start and end dates for SubContract obejcts.
I would redesign the classes like this and then only query Contract potentially with SubContract based filters.
public class Contract
{
public int ContractId
{
get;
set;
}
public DateTime StartDate
{
get;
set;
}
public int Months
{
get;
set;
}
public decimal Charge
{
get;
set;
}
public bool IsMultiContract
{
get
{
return SubContracts.GroupBy(x => x.ContractType).Count() > 0;
}
}
/// <summary>
/// Optional unique check for subcontract types may be added
/// </summary>
public List<SubContract> SubContracts
{
get;
set;
}
}
public enum ContractType
{
Broadband,
Mobile,
TV
}
public class SubContract
{
public ContractType ContractType
{
get;
set;
}
}
public class MobileContract : SubContract
{
public string MobileNumber
{
get;
set;
}
}
public class TvContract : SubContract
{
public PackageType PackageType
{
get;
set;
}
}
public class BroadBandContract : SubContract
{
public int DownloadSpeed
{
get;
set;
}
}
public enum PackageType
{
S,
M,
L,
XL
}

Passing values from concretized instance of an base class to another base class instance

I am currently working on making viewmodels capable of parsing data extracted from database to the UI and vice versa, and to do so I do a lot of manual mapping between my two viewmodels.
Currently I try to pass some values that determines an attribute, but since each attributetype requires specifying a lot specific parameter, and 90% of the variables will be redundant in all cases since attributes only have one type..
Thus i have create a placeholder base class, which just contains an Id, that each atttribute have,
and each of the specific attribute type parameter will then use this placeholder as base class..
example:
public class BooleanViewSpecification : AttributeTypeSpecification
{
public string TrueOptionText { get; set; }
public string FalseOptionText { get; set; }
}
public class DateTimeTypeViewSpecification : AttributeTypeSpecification
{
public DateTime EarliestDataTime { get; set; }
public DateTime LatestDataTime { get; set; }
}
and my Attribute class is just an
public class AttributeView
{
public DataType Type { get; set; }
public AttributeTypeSpecification AttributeTypeViewSpecification { get; set; }
}
And the same Goes for my DB view model
public class BooleanSpecification : AttributeTypeSpecification
{
public string TrueOptionText { get; set; }
public string FalseOptionText { get; set; }
}
public class DateTimeTypeSpecification : AttributeTypeSpecification
{
public DateTime EarliestDataTime { get; set; }
public DateTime LatestDataTime { get; set; }
}
and my Attribute class is just an
public class Attribute
{
public DataType Type { get; set; }
public AttributeTypeSpecification AttributeTypeSpecification { get; set; }
}
Problem is then mapping from one class to another class
public static IEnumerable<AttributeView> MapToViewModel(this IEnumerable<Attribute> attributes)
{
return attributes.Select(z => new AttributeView()
{
Type = z.Type,
AttributeTypeViewSpecification = z.AttributeTypeSpecification
});
}
Which does not seem to work?
I use entity framework and migrate using Code-First what I receive is the Id of the location, and not the actual values?
I cant seem to understand why I cant be given the values - if it during the mapping does have the value?
So why cant they be mapped over?
I retrieve the value
Context.Include(Attribute).ThenInclude(AttributeTypeSpecification)
The only thing I receive is the actual Id rather than the specified entries?

How to pass different ViewModels to a generic method?

I have a project written in C# MVC 5. There are several Controllers/Views that perform a similar set of functions based on <input ... fields in the Razor View.
There's currently logic in each controller to save input field values to a database table and later allow these saved input values to be retrieved and the View's input fields are populated by them.
These Views have many similarities and so several of their fields are duplicated across these Models.
It's become difficult to maintain code in each controller that saves its own fields and I want to use generics to create a single, common routine that saves the values for any of these controllers.
For example, 2 simple ViewModels:
public class ViewModel1
{
public string Name { get; set; }
public int Unique1 { get; set; }
...
}
public class ViewModel2
{
public string Name { get; set; }
public int Unique2 { get; set; }
...
}
Database model used:
public class SavedInputs
{
[Key] public int Id { get; set; }
public string Name { get; set; }
public int? Unique1 { get; set; }
public int? Unique2 { get; set; }
}
Now I want to create a function that can save either ViewModel's fields. Something like:
public bool SaveToDb(T model)
{
var inputs = new SavedInputs()
{
Name = model.Name,
Unique1 = model.Unique1,
Unique2 = model.Unique2
}
_db.SavedInputs.Add(inputs);
...
}
Then from each controller's Save action:
public ActionResult SaveInputs(ViewModel1 model)
{
var success = SaveToDb(model);
}
Of course ... SaveToDb() above isn't going to work but hopefully it shows what I'm wanting to accomplish.
There's many ways to do this.
Either you could have a common interface or abstract class that all your view models share which have the properties you want to save. Then SaveToDb just takes the interface instead of the generic type. Then in SaveToDb you can access all the properties of the interface properties from the model passed in and do whatever you want with them. I guess if you want to just have some properties null then abstract class makes sense, since you can override whatever you want to use
e.g.
public abstract class SavedInputsBase
{
public virtual string Name { get; set; }
public virtual int Unique1 { get; set; }
public virtual int Unique2 { get; set; }
}
public class ViewModel1 : SavedInputsBase
{
public override string Name { get; set; }
public override int Unique1 { get; set; }
...
}
public class ViewModel2 : SavedInputsBase
{
public override string Name { get; set; }
public override int Unique2 { get; set; }
...
}
public bool SaveToDb(SavedInputsBase model)
{
var inputs = new SavedInputs()
{
Name = model.Name,
Unique1 = model.Unique1,
Unique2 = model.Unique2
}
_db.SavedInputs.Add(inputs);
}
Alternatively you could use something like Automapper nuget package and have SaveToDb take SavedInputs and then just map your viewmodel to SavedInputs. Or just manually map it everywhere you want to call SaveToDb.

C# Table Row to a Dynamic Class

I'm working on application in which the user defines classes using dedicated editor. The result of this step is db table which holds a class name and list of properties attached to it by the user.
The classes can only hold only primitive types, but struct, as parameters.
The next step is to load the table rows as dynamic objects to another application.
Beside using reflection, is there another way to convert table row to POCO?
These are the models:
[Description("represents the base model")]
public class
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string Comment { get; set; }
}
public class ParameterModel : BaseModel
{
public string Type { get; set; }
public string DefaultValue { get; set; }
}
[Description("Represents dynamic activity")]
public class ActivityModel : BaseModel
{
public List<ParameterModel> Parameters { get; set; }
}
Thank you very much.

using STI and ActiveRecordBase<> with full FindAll

Is it possible to use generic support with single table inheritance, and still be able to FindAll of the base class?
As a bonus question, will I be able to use ActiveRecordLinqBase<> as well? I do love those queries.
More detail:
Say I have the following classes defined:
public interface ICompany
{
int ID { get; set; }
string Name { get; set; }
}
[ActiveRecord("companies",
DiscriminatorColumn="type",
DiscriminatorType="String",
DiscriminatorValue="NA")]
public abstract class Company<T> : ActiveRecordBase<T>, ICompany
{
[PrimaryKey]
private int Id { get; set; }
[Property]
public String Name { get; set; }
}
[ActiveRecord(DiscriminatorValue="firm")]
public class Firm : Company<Firm>
{
[Property]
public string Description { get; set; }
}
[ActiveRecord(DiscriminatorValue="client")]
public class Client : Company<Client>
{
[Property]
public int ChargeRate { get; set; }
}
This works fine for most cases. I can do things like:
var x = Client.FindAll();
But sometimes I want all of the companies. If I was not using generics I could do:
var x = (Company[]) FindAll(Company);
Client a = (Client)x[0];
Firm b = (Firm)x[1];
Is there a way to write a FindAll that returns an array of ICompany's that can then be typecast into their respective types?
Something like:
var x = (ICompany[]) FindAll(Company<ICompany>);
Client a = (Client)x[0];
Or maybe I am going about implementing the generic support all wrong?
How about this:
[ActiveRecord("companies",
DiscriminatorColumn="type",
DiscriminatorType="String",
DiscriminatorValue="NA")]
public abstract class Company : ActiveRecordBase<Company>, ICompany {
[PrimaryKey]
private virtual int Id { get; set; }
[Property]
public virtual String Name { get; set; }
}
[ActiveRecord(DiscriminatorValue="firm")]
public class Firm : Company {
[Property]
public virtual string Description { get; set; }
}
[ActiveRecord(DiscriminatorValue="client")]
public class Client : Company {
[Property]
public virtual int ChargeRate { get; set; }
}
var allClients = ActiveRecordMediator<Client>.FindAll();
var allCompanies = ActiveRecordMediator<Company>.FindAll(); // Gets all Companies (Firms and Clients). Same as Company.FindAll();
Note that you can't just downcast your Companies as Clients or Firms, you need to use proper polymorphism or a visitor. See this for an explanation.

Categories

Resources