I'm using entity framework database first and I faced a problem that I cannot solve elegantly. Let's say in my autogenerated entity I have some proprties that I don't want to use (or I don't want to allow somebody to use them). So I came up with a good idea to write an interface which provide only a part of properties from the entity and I would like to use interface reference rather than exact entity class. For example let's say that I have an Entity table in database and this is the generated code and my interface (I don't want to even know about RedundantString column in database):
public interface IEntity
{
int Id { get; }
Type NavigationProperty { get; }
}
internal class Entity : IEntity
{
public int Id { get; set; }
public string RedundantString { get; set; }
public virtual Type NavigationProperty { get; set; }
}
Everything is ok, however there is one problem. In my DbContext I have a property:
public virtual DbSet<Entity> Entities { get; set; }
As I said I do not want to have direct access to Entity type, I would rather use IEntity. So I want to pack my context in antother interface that would provide only property:
IDbSet<IEntity> Entities { get; }
Here is the problem. I can't implement that property using IDbSet<Entity> because IDbSet is not declared to be covariant, so common cast:
IDbSet<IEntity> IMyDbContext.Entities
{
get { return (IDbSet<IEntity>)Entities; }
}
is compiling fine but throws a runtime exception.
What can I do to solve that problem?
Related
As you know, C# 9.0 (.Net 5) now allows Covariant Returns. I need help applying this to a set of classes having Auto-Implemented properties.
I have two abstract classes that represent financial bank accounts and transactions. I made them abstract since I will pull data from various data sources and while the main properties will be common across all sources, each source may have additional fields I want to keep. A 1 to Many relationship exists between both classes (1 account has many transactions AND 1 transaction belongs to only 1 account).
public abstract class BankAccount
{
public string Name { get; set; }
public IList<Transaction> Transactions { get; set; } = new List<Transaction>();
...
}
public abstract class Transaction
{
public string Name { get; set; }
public virtual BankAccount BankAccount { get; set; } // This doesn't work unless I remove set;
...
}
And here is an example of the concrete implementations
public class PlaidBankAccount : BankAccount
{
public string PlaidId { get; set; }
...
}
public class PlaidTransaction : Transaction
{
public string PlaidId { get; set; }
public override PlaidBankAccount BankAccount { get; set; } // This doesn't work unless I remove set;
...
}
What I want to do is to override the base class getters and setters so that they use derived classes. For example:
If I create an instance of the concrete transaction and call the BankAccount getter, I want to get an instance of the derived PlaidBankAccount not the base BankAccount.
What I've found is that when I only define virtual getter in the base class and override it in the derived class, it works. But just as I add both properties {get;set;}, I get the same error as in previous C# versions:
error CS1715: 'PlaidTransaction.BankAccount': type must be 'BankAccount' to match overridden member 'Transaction.BankAccount'
How could I fix this?
In C# 9 properties are only able to have co-variant returns when they are readonly, so unfortunately, no set; is possible.
An overriding property declaration must specify exactly the same access modifier, type, and name as the inherited property. Beginning with C# 9.0, read-only overriding properties support covariant return types. The overridden property must be virtual, abstract, or override.
From the Microsoft Docs - Override keyword
I'm having a problem and I don't know how to solve it. I already watched millions of posts like this but doesn't help me.
I have this:
public interface IInterface
{
int Order { get; set; }
}
public abstract class AbstractClass { }
and two implementations
public class FirstImplementation : AbstractClass, IInterface
{
[Column(nameof(IInterface.Order))]
public int Order { get; set; }
}
public class SecondImplementation : AbstractClass, IInterface
{
[Column(nameof(IInterface.Order))]
public int Order { get; set; }
}
and other implementations, but they don't have Order property.
public class MyContext
{
public DbSet<AbstractClass> AbstratClass { get; set; }
}
I cannot put property Order in AbstractClass,cause other logic and because other implementations don't have the property, and I try these solution with ColumnAttribute to map it in one column.
But when I try these query, it throws an exception:
System.NotSupportedException: 'The specified type member 'Order' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.'
The query:
myContext.AbstractClass
.OfType<FirstImplementation>()
.Concat<IInterface>(myContext.AbstractClass
.OfType<SecondImplementation>())
.Max(x => x.Order);
The goal should be to execute Max with IQueryable<T>
I tried some solutions in stackoverflow but didn't work
EDIT:
Working query:
myContext.AbstractClass.OfType<FirstImplementation>().Max(x => x.Order)
or
myContext.AbstractClass.OfType<FirstImplementation>().AsQueryable<IInterface>().Max(x => x.Order)
Exception throw when I Concat IQueryable
Your context only contains a DbSet of AbstractClass, but not of FirstImplementation. According to this OfType<DerivedType> only works with entity framework, if DerivedType is known to the DbContext (see first comment of smitpatel).
I have some classes with common properties, however, I cannot make them derive from a base type (LINQ-to-SQL limitations).
I would like to treat them as if they had a base type, but not by using Reflection (performance is critical).
For example:
public class User
{
public int Id { get; set; }
public string FirstName { get; set; }
}
public class Vehicle
{
public int Id { get; set; }
public string Label { get; set; }
}
In this case I would be happy if I had the Id property available, regardless of the type I'm holding.
Is there any way in C# to to something similar to this:
public static int GetId<T>(T entity) where T // has an int property 'Id'
{
return entity.Id;
}
I guess I could have used dynamic, however, I'm looking for a way to restrict the code in compile time from using this method for an object that has no Id property.
You can use interfaces:
public interface IHasId
{
int Id { get; }
}
public class User : IHasId { ... }
public class Vehicle : IHasId { ... }
public static int GetId<T>(T entity) where T : IHasId
{
return entity.Id;
}
However, if you are not able to modify the classes to add the interface, you won't be able to do this. No compile-time checks will verify that a property exists on T. You'd have to use reflection - which is slow and obviously not ideal.
There is no way to guarantee a type has a given member without constraining to a common base type or interface. One way to work around this limitation is to use a lambda to access the value
public static int Use<T>(T value, Func<T, int> getIdFunc) {
int id = getIdFunc(value);
...
}
Use(new User(), u => u.Id);
Use(new Vehicle(), v => v.Id);
You can create an interface with the common properties and make your classes implement it:
public interface IEntity
{
int Id { get; set; }
}
public class User : IEntity
{
public int Id { get; set; }
public string FirstName { get; set; }
}
public class Vehicle : IEntity
{
public int Id { get; set; }
public string Label { get; set; }
}
public static int GetId<T>(T entity) where T : IEntity
{
return entity.Id;
}
You could simplify GetId like this:
public static int GetId(IEntity entity)
{
return entity.Id;
}
The other answers mentioning the interface approach are certainly good, but I want to tailor the response to your situation involving Linq-to-SQL.
But first, to address the question title as asked
Can C# constraints be used without a base type?
Generally, the answer is no. Specifically, you can use struct, class, or new() as constraints, and those are not technically base types, and they do give some guidance on how the type can be used. That doesn't quite rise to the level of what you wish to do, which is to limit a method to types that have a certain property. For that, you will need to constrain to a specific interface or base class.
For your specific use case, you mention Linq-to-SQL. If you are working from models that are generated for you, then you should have options to modify those classes without modifying the generated model class files directly.
You probably have something like
// code generated by tool
// Customer.cs
public partial class Customer // : EntityBaseClasses, interfaces, etc
{
public int ID
{
get { /* implementation */ }
set { /* implementation */ }
}
}
And other similar files for things such as Accounts or Orders or things of that nature. If you are writing code that wishes to take advantage of the commonly available ID property, you can take utilize the partial in the partial class to define a second class file to introduce a common interface type to these models.
public interface IIdentifiableEntity
{
int ID { get; }
}
And the beauty here is that using it is easy, because the implementation already exists in your generated models. You just have to declare it, and you can declare it in another file.
public partial class Customer : IIdentifiableEntity { }
public partial class Account : IIdentifiableEntity { }
// etc.
This approach has proven valuable for me when using a repository pattern, and wishing to define a general GetById method without having to repeat the same boilerplate in repository after repository. I can constrain the method/class to the interface, and get GetById for "free."
Either you need to make both classes implement an interface with the properties you need, and use that in the generic constraint, or you write separate methods for each type. That's the only way you'll get compile-time safety.
In VS2012(.NET 4.5 and Entity Framework 5 )
When exposed the inheritance relationship,caused the compile-time errors:
You cannot use Ignore method on the property 'InnerString' on type
'MrTree.SubSubClass' because this type inherits from the type
'MrTree.BaseClass' where this property is mapped. To exclude this
property from your model, use NotMappedAttribute or Ignore method on
the base type.
The code is as follows:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
MyDbcontext db = new MyDbcontext();
int i = db.SubSubClasses.Count();
}
}
public class BaseClass
{
[NotMapped]
public string InnerString { get; set; }
}
public class SubClass : BaseClass
{
}
public class SubSubClass : SubClass
{
}
public class MyDbcontext : DbContext
{
public DbSet<SubSubClass> SubSubClasses { get; set; }
public DbSet<SubClass> SubClasses { get; set; }
}
}
Can you tell me what's wrong?
I have found a work around for this issue I have experienced myself. I did post some supplementary information about what might be causing the issue but it was deleted by some egotistical nerd who felt it wasn't worthy.
So here is my ANSWER to this problem - might not be entirely right but it works for me so please don't delete this Mr Nerd just in case it helps out someone else.
Firstly, this issue only affects classes that do not inherit the abstract class using the NotMapped attribute directly but instead those that inherit from another class that itself inherits from the base class, as per the original statement.
The following works for me using something similar to the following class setup:
BASE CLASS:
public abstract class EntityBase
{
public virtual int Id { get; set; } // this will be mapped
public virtual int DoNotMap { get; set; } // this should be ignored
}
FIRST LEVEL INHERITANCE
public class Contact : EntityBase
{
public string Name { get; set; }
}
SECOND LEVEL INHERITANCE
public class Employee : Contact
{
public DateTime StartDate { get; set; }
}
Create a generic configuration class that inherits from EntityTypeConfiguration:
public class DataEntityBaseConfiguration<T>
: EntityTypeConfiguration<T> where T : EntityBase
{
public DataEntityBaseConfiguration()
{
Ignore(x => x.DoNotMap);
}
}
Create configuration classes for all first level inheritance that inherit directly from the EntityBase class, i.e.:
public class ContactConfiguration : DataEntityBaseConfiguration<Contact>
{
// place any further configuration rules in the constructor
}
WORKAROUND: any classes that inherit from the EntityBase indirectly, simply create a Configuration class that inherits from EntityTypeConfiguration:
public class EmployeeConfiguration : EntityTypeConfiguration<Employee>
{
// place any further configuration rules in the constructor
}
In your DataContext, in the OnModelCreating method, add you configurations:
modelBuilder.Configurations.Add(new ContactConfiguration());
modelBuilder.Configurations.Add(new EmployeeConfiguration());
Assuming a TPT approach in this instance, map the specialised version, Employee to its own table with an FK relation to Contact:
modelBuilder.Entity<Contact>().Map<Employee>(m => m.ToTable("Employees"));
This, for me at least, creates a contact table without the "DoNotMap" property and an Employee table without the "DoNotMap" table and with a PK/FK relation to Contact. Even though I do not inherit from the EntityBaseConfiguration for Employee Configuration, it still, somehow, picks up Ignore and leaves it out.
I would assume that if we went with a TPH approach, we would simply end up with a single Contact table plus Discriminator column. TPC would obviously recreate all the Contact properties in the Employee table but I'm not sure if it would also create the DoNotMap property - will test that when I have a moment spare.
In short, going back to the original question, I'm not sure why this happens with both the NotMapped attribute and the Ignore. I was getting the error when my EmployeeConfiguration inherited from the EntityBaseConfiguration. I'm not keen on the workaround as it firstly the error is false in its statement and secondly, it's a an error that would easy to fall into again, even if the solution is now quite simple.
Hope that helps anyone that has struggled with this inheritance issue.
Regards
The fact that the primary key has to be included in the POCO class definition A already means that Ais not a POCO object. You cannot ignore properties on POCO objects
Do you have a property called InnerString in your SubSubClass? That's what the error is saying, although you don't have it listed in your example code.
Your code above worked for me, but I had to add a PK, here's the entire Console app:
public class BaseClass
{
public int Id { get; set; }
[NotMapped]
public string InnerString { get; set; }
}
public class SubClass : BaseClass
{
}
public class SubSubClass : SubClass
{
}
public class MyDbcontext : DbContext
{
public DbSet<SubSubClass> SubSubClasses { get; set; }
public DbSet<SubClass> SubClasses { get; set; }
}
class Program
{
static void Main( string[] args )
{
var context = new MyDbcontext();
context.SubSubClasses.Add(new SubSubClass());
context.SaveChanges();
}
}
And the database it created:
I have a class like this
public abstract class BaseType<T>
{
public string Name {};
public T TypedValue {
get {
return GetTypedValue(PersistedValue);
}
};
public string PersistedValue {}
public abstract T GetTypedValue(PersistedValue);
}
then many derived classes like
public class IntegerType:BaseType<int>
{
...
}
is it possible to map this hierarchy using EF 4.0 using Table per inheritance scheme ?
Currently the generated code creates has an error because it generates a property like
public <T> ObjectSet<TypedAttribute<T>> TypedAttributes
{
get
{
return _typedAttributes ?? (_typedAttributes = CreateObjectSet<TypedAttribute<T>>("TypedAttributes")); }
}
private ObjectSet<TypedAttribute> _typedAttributes;
I don't think so because:
Inheritance mapping requires the base class to be entity in EDMX.
When inheritance is used the ObjectSet is for base type. What generic argument would you use to create an instance of ObjectSet when it has to be used to retrieve any subtype?
It can be partially achieved without inheritance (at least for POCOs). Simply model your subtypes in EDMX without base type. Then manually create POCO classes and derive them from generic base types. The only rule you have to follow is that POCO class must have the same name as entity in EDMX and it must have all its properties with accessibility set in EDMX. If you want to use change tracking properties must be marked as virtual. If you want to use lazy loading navigation properties must be virtual as well.
Example:
Suppose that I have two entities in EDMX: IntegerValue and DoubleValue. Now I defined these POCOs as follows:
public abstract class BaseType<T>
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual T Value { get; set; }
}
public class IntegerValue : BaseType<int>
{ }
public class DoubleValue : BaseType<double>
{ }
It will result in single table per sub type.