using STI and ActiveRecordBase<> with full FindAll - c#

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.

Related

how to make a business layer working with different Models?

I am designing the architecture of a .Net project in C#.
I will use EF.
I want to set a MVC pattern architecture.
Although I will start with a first Model implementation, I might have to use other Models that will be QUITE similar to the original one.
I obviously want to implement a single Business layer that would ideally work with both Model implementations.
So I suppose I will have to intensively rely on interfaces at some point.
USUALLY, I build my entities and context this way:
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
... other properties
}
public class Bar
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Foo Fooo { get; set; }
... other properties
}
...
public class MyContext : DbContext
{
...
}
Now, in my case, I will have let's say 2 different models.
On top of the precedently written Foo and Bar classes, I will have:
public class Foo2
{
public int Id { get; set; }
public string Name { get; set; }
... other properties
}
public class Bar2
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Foo Fooo { get; set; }
... other properties
}
...
public class MyContext2 : DbContext
{
...
}
Now how to set a single business layer that can deal either with MyContext or MyContext2 ?
I suppose the the fist step is to create interfaces for For Foo / Foo2 and Bar / Bar2:
public interface IFoo
{
int Id { get; set; }
string Name { get; set; }
}
public interface IBar
{
int Id { get; set; }
string Name { get; set; }
IFoo Fooo { get; set; }
}
Then what????

Entity Framework Core: Combine owned properties with inheritance

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.

Creating different tables using using same base abstract class

I have 2 models which have exactly same fields, but I chose to make different models for them because I needed two different tables, one for each.
Earlier everything was working fine when I had two different tables for each model, but then I started using abstract base class because the code inside both the models were same.
Now I have a single table comprised of all the data that I save.
How can I create different tables for those two models.
public abstract class baseGrammar
{
[Key]
public int Id { get; set; }
[Required]
public string question { get; set; }
[Required]
public string ans { get; set; }
public string ruleId { get; set; }
public string ruleApplicable { get; set; }
[ForeignKey("ruleId")]
public virtual ruleTable RuleTable { get; set; }
}
The one shown above is my abstract base class.
public class article : baseGrammar
{
}
public class adjective : baseGrammar
{
}
Just if someone intrested in ruleTable model.
public class ruleTable
{
[Key]
public string ruleId { get; set; }
public string topic { get; set; }
public string rule { get; set; }
public string example { get; set; }
public virtual ICollection<baseGrammar> BaseGrammar { get; set; }
}
Am also adding context class so as to provide better description
public class english : DbContext
{
public english() : base("name=localServerEng")
{
Database.SetInitializer<DbContext>(null);
Database.SetInitializer<english>(new UniDBInitializer<english>());
}
public virtual DbSet<adjective> adjectiveDb { get; set; }
public virtual DbSet<adverb> adverbDb { get; set; }
public virtual DbSet<alternativeVerb> alternativeVerbDb { get; set; }
public virtual DbSet<antonyms> antonymsDb { get; set; }
public virtual DbSet<article> articleDb { get; set; }
private class UniDBInitializer<T> : DropCreateDatabaseIfModelChanges<english>
{
}
public System.Data.Entity.DbSet<StructureSSC.Areas.AreaEnglish.Models.baseGrammar> baseGrammars { get; set; }
}
Screenshot of SQL Server showing 1 table comprising of all columns instead of different tables
This set up will give you 2 tables: (1) adjectives (2) articles
The context should be like this:
public class SomeContext : DbContext
{
public SomeContext()
: base("name=SomeContext")
{
}
public virtual DbSet<article> Articles { get; set; }
public virtual DbSet<adjective> Adjectives { get; set; }
}
public abstract class baseGrammar
{
//... common properties/columns
}
public class article : baseGrammar
{
}
public class adjective : baseGrammar
{
}
Please note the naming convention. In .NET class names and property names should follow Pascal Notation. Therefore, they should be:
BaseGrammar
Article
Adjective
RuleApplicable // other properties should follow same convention

Entity Framework code first create a superclass in two existing classes,

This is my first time here so sorry if I did not put the doubt by default. In my project I have two models class doubts and a class of works, I want to create a classification for both using a superclass, like the design pattern strategy. But I have a problem, the project state is advanced and I have used the doubt and works classes many times. Already tried in many ways, but either the error in the update-database or the error in what I had done before.
Does anyone know one way that I can implement a generic class classification for the doubts and works classes without many changes of what I had already done?
My code is below
public abstract class Classificable
{
[Key]
public int id { get; set; }
public virtual Classification classication { get; set; }
}
public class Doubt : Classificable
{
public int doubtID { get; set; }
public string question { get; set; }
public string content { get; set; }
public virtual Student student { get; set; }
public virtual Course course { get; set; }
public virtual Work work { get; set; }
public virtual ICollection<Answer> answers { get; set; }
}
public class Work : Classificable
{
public int workID { get; set; }
public string name { get; set; }
public string nameWork { get; set; }
public string filePath { get; set; }
public virtual Student student { get; set; }
public virtual Course course { get; set; }
public virtual ICollection<Doubt> doubts { get; set; }
}
public class DB_DIS : DbContext
{
public DB_DIS()
: base("name=DB_DIS")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Doubt>().ToTable("Doubts");
modelBuilder.Entity<Work>().ToTable("Works");
}
public virtual DbSet<Doubt> Doubts { get; set; }
public virtual DbSet<Work> Works { get; set; }
}`
What is the property or method in Classificable that you are trying to apply to Doubt and Work?
Have you written your data structure in such a way that is contains an id field AND workID feild?
In my experience, if you are trying to share properties between EF Classes you are better off sharing common fields. I've used it in the past for base classes of AuditableBase as follows:
public class AuditableBase
{
public string UpdateUserId { get; set; }
public DateTime UpdateDate { get; set; }
}
Then all my classes that I want to "Audit" will have a UpdateUserId and UpdateDate, and I can do some pre-save processing to set those based on the type of AuditableBase.

How to add userId to every query without adding it to all domain classes?

I'm working with sqlite and the c# sqlite-net library.
Some of my entities:
public class Product
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
[MaxLength(50)]
public string Name { get; set; }
[MaxLength(50)]
public string Brand { get; set; }
[MaxLength(50)]
public string Type { get; set; }
[MaxLength(50)]
}
public class ProductImage
{
public int Id { get; set; }
public string Name { get; set; }
public string Container { get; set; }
public int ProductId { get; set; }
}
Now all entities belong to a user and the user should always work only with his own entities. So when I insert an entity in the database I want to store the userId with it.
However... I don't want to add the userId to all my domain classes.
I'm storing my entities like this right now:
await _databaseManager.GetDatabaseInstance().InsertAsync(entity);
and selecting like this:
var products = await _databaseManager.GetDatabaseInstance().Table().ToListAsync();
So is there a way with sqlite-net to add the userId to the database without adding it to all domain classes?
Doesn't it support inheritance, as in looking at the type's hierarchy? I would suggest it would, or at least should. So, if it does you could use an abstract base class or an interface. Something like this:
public abstract class StandardEntity {
public int UserId { get; set; } // decorate with attributes as necessary
}
And inherit:
public class Product : StandardEntity {
}

Categories

Resources