Casting derived class to base class using LINQ to Entities - c#

I have two database tables, one to hold completed items and another to hold incomplete items. Both tables are identical in structure. There are some cases where I would like to quest just one of these tables, but other cases where I would want to query the concatenation of both tables.
Classes
public abstract class SomeBase
{
public int Id {set;get;}
public string Item1 {set;get;}
public string Item2 {set;get;}
}
public class A : SomeBase
{
}
public class B : SomeBase
{
}
Mapping (Fluent API)
public class SomeDatabase : DbContext
{
public DbSet<A> As {set;get;}
public DbSet<B> Bs {set;get;}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<A>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("ATable", "SomeSchema");
}
modelBuilder.Entity<B>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("BTable", "SomeSchema");
}
}
}
Using this setup, I can query both tables just fine individually like so
var db = new SomeDatabase();
var a = db.As;
var b = db.Bs;
When trying to combine the two, I can't quite figure out the syntax. One solution in the answer below involves casting it using .AsEnumerable but that isn't quite what I'm looking for since it evaluates immediately.
db.As.AsEnumerable().Concat<SomeBase>(db.Bs);
db.As.Cast<SomeBase>().Concat(db.Bs.Cast<SomeBase>());
How can I concatenate two derived classes that are identical on the database site?
EDIT:
At the lowest level, I am getting these errors
db.As.Cast<SomeBase>();
Unable to cast the type 'Test.Models.A' to type 'Test.Models.SomeBase'. LINQ to Entities only supports casting Entity Data Model primitive types.
db.As.OfType<SomeBase>();
'Test.Models.SomeBase' is not a valid metadata type for type filtering operations. Type filtering is only valid on entity types and complex types.
Related question: How to combine 2different IQueryable/List/Collection with same base class? LINQ Union and Covariance issues

Simply define a
DbSet<SomeBase> Bases { get; set;}
property to access all instances of the base class. The framework should combine the query the right way (union) to include the instances from both tables.
For more details check out e.g. this article: http://weblogs.asp.net/manavi/archive/2011/01/03/inheritance-mapping-strategies-with-entity-framework-code-first-ctp5-part-3-table-per-concrete-type-tpc-and-choosing-strategy-guidelines.aspx
(You use the TPC inheritance strategy)

maybe there is some more elegant way, but union should do it i guess:
db.As.Select(x => new { x.Id, x.Item1, x.Item2 } )
.Union(db.Bs.Select(x => new { x.Id, x.Item1, x.Item2 }));
if you want to include some fields from As and some fields from Bs then it should look like:
db.As.Select(x => new { x.Id, x.Item1, x.Item2, x.Afield, Bfield = null } )
.Union(db.Bs.Select(x => new { x.Id, x.Item1, x.Item2, AField = null, x.Bfield }));

How about:
var concatenatedSet = db.As.Local.OfType<SomeBase>().Concat(db.Bs.Local.OfType<SomeBase());

Related

How to change return type in Lambda Expression

I want to query data from sequence (IEnumerable and IQueryable) by nested Interface type, for example,
public interface IData
{
TypeInEnum? Value1 { get; set; }
string Value2 { get; set; }
}
public class DataModel : IData
{
public int? Value1 { get; set; }
public string Value2 { get; set; }
TypeInEnum? IData.Value1
{
get
{
return Value1.HasValue ? (TypeInEnum?)Value1.Value : null;
}
set
{
this.Commit = variable;
}
set
{
//ignore enum validation here
this.Value1 = value.HasValue ? (int?)value.Value : null;
}
}
}
public enum TypeInEnum
{
A = 1,
B,
C
}
query:
//source is IEnumerable<DataModel>
var query = source.Where(item => item.Value1 == 1); //item is DataModel
var query1 = source.Where1(item => item.Value1 == TypeInEnum.A); //item is IData
Assert.IsTrue(query.SequenceEqual(query1));
but this only works when Property in class and interface are same type. such as,
when use Where, the error is:
System.InvalidOperationException: Rewriting child expression from type 'System.Nullable<TypeInEnum>' to type 'System.Nullable<System.Int32>' is not allowed, because it would change the meaning of the operation. If this is intentional, override 'VisitUnary' and change it to allow this rewrite.
use Select, the error is:
System.ArgumentException: Expression of type 'System.Nullable<System.Int32>' cannot be used for return type 'System.Nullable<TypeInEnum>'
I don't know where to add a Convert.
all example code here
I have waste time on this for a month...
Edited
In my current project, which using EntityFramework, there are some basic columns in database of each table, but I found some basic column name are different, for example, CreatedDateTime and DateTimeCreated. There will be problems when put tables which contains basic column with different name into one Entity Data Model. Change these column name in database and project will be difficult and cause some new problems, there are a lot of svn branches, and some table are used in many modules. So I create an interface which contains all these basic columns, and change enum field from numeric type (in database) to enum type (in project), and let EF generated class implement this interface, if column name and type are not same, implement property in interface explicitly, therefore impact on the original project can be ignored.
This does solve the problem, but it's difficult to use the interface through EF, such as query data and modified value based on interface then save to database, create some common query extension based on interface. If this is possible, lots of codes can be reduced and the project will be easier to maintain.
Query data from database with same type in both entity model and interface is done, even if the field name is different.
You can use OfType to filter to only those that are assignable to a type.
Example:
var query = source.OfType<DataModel>().Where(item => item.Value1 == 1);
var query1 = source.OfType<IData>().Where(item => item.Value1 == TypeInEnum.A);
Could you try casting the item to IData in the Where predicate?
var query = source.Where(item => item.Value1 == 1); //item is DataModel
var query1 = source.Where(item => ((IData)item).Value1 == TypeInEnum.A); //item is IData
Assert.IsTrue(query.SequenceEqual(query1));
The above code works for me with casting.
A technical explanation and fix would be that that you can't compare a Nullable int and a Nullable enum without casting (this is not like comparing an int and an enum!).
At the end of this post you will find an example of how you can fix that.
However, the more interesting question is why do you have a
class DataModel with public int? Value1 member
that implements
Interface IData which declares a TypeInEnum? Value1 member?
I would argue that if DataModel implements IData it should also have Value1 of typeTypeInEnum? i.e.:
public class DataModel : IData
{
public TypeInEnum? Value1 { get; set; }
...
}
You can find an example for refactored code here
As for the technicahl solution:
Take a look at your code with a casting a fix:
https://dotnetfiddle.net/8MqXnr
var query = source.Where(item => item.Value1.HasValue).Where(item => (int)(item.Value1.Value) == 1); //item is DataModel
var query1 = source.Where(item => item.Value1.HasValue).Where(item => item.Value1.Value == (int)TypeInEnum.A); //item is IData
var eq = query.SequenceEqual(query1);
Console.WriteLine(String.Format("results: {0}",eq? "Equal": "Not equal"));

How to configure a mapping of collection types with AutoMapper?

For example, I've implemented two classes like these:
public class A
{
public List<C> Items { get; set; }
}
public class B
{
public IImmutableList<C> Items { get; set; }
}
public class C
{
}
When I try to map A to B and vice versa, I get an exception because List<string> cannot be converted to IImmutable<string>.
Probably I could provide a mapping for A<->B, but since it'll be a very common pattern in my solution, I'd like to avoid to manually mapping each class that may fall into the same case.
Is there anyway I can generalize the whole mapping using generic type definitions from a collection type to another collection type?
This is what I want to avoid
mapperConfig.CreateMap<A, B>()
.ForMember(a => a.Items, opts => opts.Ignore())
.AfterMap
(
(source, target) =>
{
target.Items = source.Items.ToImmutableList();
}
);

Entity Framework One To required Many mapping not working

I have two classes, with a 1 - 1..* relationship.
class A
{
public long Id;
public virtual ICollection<B> Bees;
}
class B
{
public long Id;
public A MyA;
}
Now, I'm trying to get my database to behave so that any instance of A has to have at least one B in its ICollection, with no upper limit. Any row of B should have a foreign key reference to an A row. My mapping looks like this:
public AMapping()
{
this.ToTable("A");
this.HasKey(x => x.Id);
}
public BMapping()
{
this.ToTable("B");
this.HasKey(x => x.Id);
this.HasRequired(x => x.MyA)
.WithMany(x => x.Bees);
}
The expected behavior when I do this:
var aaaaah = new A();
aaaaah.Bees = null;
MyDbContext.A.Add(a);
Should be an exception being thrown. But Entity Framework inserts with no complaints. What did I do wrong in my mapping?
Edit: I have made an ugly temporary solution to the problem by putting a [System.ComponentModel.DataAnnotations.Required] annotation over the Bees property. But this only checks if Bees is null or not, not if it's an empty list if it is instantiated.
There's no real native way to accomplish this with EF or SQL, i.e. you can't create an A without a B, and you can't create a B without an A. Stalemate.
You'll need to create some logic in your business layer to enforce the constraint. E.g.
class A
{
...
public A(B b)
{
this.Bees = new List<B>();
this.Bees.Add(b);
}
}
class B
{
...
public B(A a)
{
this.MyA = a;
}
}
N.B. Presuming code-first, if db-first make your customisations in a partial class.

Using an extension method on a base class in a LINQ query

Apologies in advance for my naivety.
I am using Entity Framework to persist entities I have defined in my domain model. My domain model entities all inherit from my EntityBase class. This has properties I wish to be common to all my entities:
public class EntityBase
{
public string CreatedBy { get; set; }
public DateTime? Created { get; set; }
public int ModifiedBy { get; set; }
public DateTime? Modified { get; set; }
public bool Enabled { get; set; }
public bool Deleted { get; set; }
}
Now when I want to query EF using LINQ it would be nice if I didn't have to include elements to check if a particular entity is Enabled or Deleted. Every query would involve code, for example:
var messages = _db.Memberships.Where(m => m.UserId.Equals(userId))
.SelectMany(m => m.Group.Messages)
.Include(m => m.Group.Category)
.Select(m => m.Enabled && !m.Deleted)
.ToList();
Rather than doing this each time, I thought I would write an extension method which would act on IQueryable
public static IQueryable<EntityBase> Active(this IQueryable<EntityBase> entityCollection)
{
return entityCollection.Where(e => e.Enabled && !e.Deleted);
}
In my naivety I then thought I could just include this in any LINQ query which returns my entities which inherit from the EntityBase class - like so:
var messages = _db.Memberships.Where(m => m.UserId.Equals(userId))
.SelectMany(m => m.Group.Messages)
.Include(m => m.Group.Category)
.Active() <============================= Extension Methd
.ToList();
return Mapper.Map<List<Message>,List<MessageDto>>(messages);
However, the compiler now complains that:
Error 2 Argument 1: cannot convert from
'System.Collections.Generic.List<Diffusr.Business.Entities.EntityBase>' to
'System.Collections.Generic.List<Diffusr.Business.Entities.Message>'
Question : Can I achieve what I want to achieve, i.e. a common method for all my entities to return only Enabled and not Deleted? If so, how?
Instead of specifying a concrete class, use generics, as most extension methods do:
public static IQueryable<T> Active<T>(this IQueryable<T> entityCollection) where T:EntityBase
{
return entityCollection.Where(e => e.Enabled && !e.Deleted);
}
I assume you are using some version of .NET earlier than 4.0. Generic covariance wasn't allowed before 4.0 (ie passing an enumerable of a child type when an enumerable of the base type was expected).
Even after 4.0, it's not the absolute best idea to use covariance as the compiler ends up doing a lot of extra checks to do to ensure type safety whenever you try to store some new value to the List. Jon Skeet has a nice article about this
You can by changing the extension method:
public static IQueryable<T> Active(this IQueryable<T> entityCollection)
where T : EntityBase
{
return entityCollection.Where(e => e.Enabled && !e.Deleted);
}

Map parts of an aggregate to a List<T>

Let us say we have a simple business object:
class SimpleBO
{
public string Field1{get;set;}
public string Field2{get;set;}
}
Also we have a complex Aggregate like that:
class ComplexBO
{
public SimpleBO SimpleBOField {get;set}
public List<SomeClass> ObjectList {get;set;}
public SomeClass Object {get;set;}
}
SomeClass itself has a reference of SimpleBO:
class SomeClass
{
public SimpleBO SimpleBOField {get;set}
}
Now in some part of my program I want to get a list of all distinct simple objects met inside a certain aggreggate. We are using automapper heavily but I did not manage to map it so far. May be a LINQ query is a better option? How would you solve this?
Assuming what you have is:
ComplexBO aggregate = ...
then you should just need:
var objects = aggregate.ObjectList.Select(x => x.SimpleBOField).Concat(
new[] {aggregate.SimpleBOField, aggregate.Object.SimpleBOField }
).Distinct().ToList();
This will give you the distinct object references; if you need distinct value pairs, then either override Equals()/GetHashCode(), or cheat:
var objects = aggregate.ObjectList.Select(x => x.SimpleBOField).Concat(
new[] {aggregate.SimpleBOField, aggregate.Object.SimpleBOField }
).Select(
x => new {x.SimpleBOField.Field1, x.SimpleBOField.Field2}
).Distinct().Select(
x => new SimpleBO {Field1 = x.Field1, Field2 = x.Field2}
).ToList();

Categories