I have this as my model (modified for obvious reasons)
public class Model
{
public int Id {get; set;}
public string Data {get; set;}
public TypeDomainValue ModelType {get; set;}
}
ModelType is known in the database as a string value only (no tables connected).
However if I want to filter on the value of TypeDomainValue in a linq statement
models.Where(c => c.ModelType.Value.Contains(searchString));
I get the error that the Linq expression cannot be translated.
I already tried using EF.Functions.Like which creates a similar error.
How can I get this to be translated properly as I do not want to load the entire table into memory.
Edit:
I use the following ValueConverter
public class ModelTypeDomainValueConverter : ValueConverter<ModelTypeDomainValue, string>
{
public ModelTypeDomainValueConverter([CanBeNull] ConverterMappingHints mappingHints = null) : base(ConvertToString(), ConvertToDomainValue(), mappingHints)
{
}
private static Expression<Func<ModelTypeDomainValue, string>> ConvertToString()
{
return x => x.Value;
}
private static Expression<Func<string, ModelTypeDomainValue>> ConvertToDomainValue()
{
return x => ModelTypeDomainValue.CreateByValue(x);
}
}
Which gets added with the following extension:
public static ModelBuilder UseValueConverter(this ModelBuilder modelBuilder, ValueConverter converter)
{
var type = converter.ModelClrType;
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
var properties = entityType.ClrType.GetProperties().Where(p => p.PropertyType == type);
foreach (var property in properties)
{
modelBuilder.Entity(entityType.Name).Property(property.Name).HasConversion(converter);
}
}
return modelBuilder;
}
Make ur life easy and Use owned properties in Entity framework core.
bcs TypeDomainValue smells like that one.
it has not its own table.
it don't have any primary key.
it resides in same table as Mode
so its own property.
I m not saying own properties can not have those but typically they are like them
and we use them as Value objects
Related
We have a database first approach using EF.
We use EntityTypeConfiguration<T> to configure the mapping where T is the entity.
We often do things like Property(t => t.EntityType).HasColumnName("EntityType_ID"); What I wish is to have an extension method on the return type of Property(), which is PrimitivePropertyConfiguration that does it for me. Like so.
public static class EntityTypeConfigurationExtension
{
public static PrimitivePropertyConfiguration IsForeignKey(this PrimitivePropertyConfiguration propertyConfiguration)
{
Type typeOfEntity; //The problem is, how to know this type without passing it to this method?
return propertyConfiguration.HasColumnName(typeOfEntity.Name + "_ID");
}
}
The problem is, as you can see in the commented line, I do not see a way to get the type of the property being configured. How can I get the type of property being configured?
If you need to add "_ID" to all your foreign keys you could make your own custom convention. Like this:
public class SuffixForeignKeyNameConvention : IStoreModelConvention<AssociationType>
{
public SuffixForeignKeyNameConvention()
{
}
public void Apply(AssociationType association, DbModel model)
{
if (association.IsForeignKey)
{
AddSuffix(association.Constraint.ToProperties);
}
}
private void AddSuffix(IEnumerable<EdmProperty> properties)
{
string result;
foreach (var property in properties)
{
result = property.Name;
property.Name = $"{result}_ID";
}
}
}
And then apply it:
modelBuilder.Conventions.Add(new SuffixForeignKeyNameConvention());
After using this convention all your foreign keys should have _ID suffix. Hope it helps.
I have an object with two objects as properties (User, PrimaryNode), both could potentially be null, see below:
public class Item
{
[Key]
public int ItemId { get; set; }
public string ItemName { get; set; }
public Node PrimaryNode { get; set; }
public User User { get; set; }
}
I'm using Entity Framework 6 to populate the Item object and using chained includes to populate the PrimaryNode and User objects within it.
When the first chained Include has a null object then the whole object returns as null, for example:
using (var db = new MyContext())
{
var item = db.Items.Include(i => i.User).Include(n => n.PrimaryNode).FirstOrDefault(i => i.ItemId == id);
}
If in the above example i.User is null then the item variable is null. Whats the best way of populating both the sub-objects in a way that if a sub-object is null then the parent object and the other sub-object will still be populated?
I don't think your issue is due to the Include calls. According with the documentation:
This extension method calls the Include(String) method of the
IQueryable source object, if such a method exists. If the source
IQueryable does not have a matching method, then this method does
nothing.
In other words is going to be translated to:
var item = db.Items.Include("User").Include("PrimaryNode").FirstOrDefault(i => i.ItemId == id);
My question is, are you sure you have an Item with that id properly related with existing rows in Users and PrimaryNodes tables in your DB?. When you call Include method at the end is going to be translated to a join, so if the FK of your relationship doesn't match with the PK that reference, your query should not return what you are expecting.
Anyways, if you want to try another variant to load related properties you can use Explicit Loading:
var item = db.Items.FirstOrDefault(i => i.ItemId == id);
context.Entry(item).Reference(p => p.PrimaryNode).Load();
context.Entry(item).Reference(p => p.User).Load();
I think it would be better if you use Lazy loading int his situation. Just make the User and PrimaryNode virtual:
public class Item
{
[Key]
public int ItemId { get; set; }
public string ItemName { get; set; }
public virtual Node PrimaryNode { get; set; }
public virtual User User { get; set; }
}
And then:
var db = new MyContext();
var item = db.Items.FirstOrDefault(i => i.ItemId == id);
As others have mentioned, I think your issue is not due to the Includes. However, I think the following method has value. It is functionally equivalent to what you are already doing with the chained includes, but I think it has several benefits including making the intention of the code clear to the user.
The includes can be placed in Extension methods:
using System.Data.Entity;
using System.Linq;
namespace Stackoverflow
{
public static class EntityExtensions
{
public static IQueryable<Item> IncludePrimaryNode(this IQueryable<Item> query)
{
// eager loading if this extension method is used
return query.Include(item => item.PrimaryNode);
}
public static IQueryable<Item> IncludeUser(this IQueryable<Item> query)
{
// eager loading if this extension method is used
return query.Include(item => item.User);
}
}
}
Then, you can use the extensions as follows:
using (var db = new MyContext())
{
var itemQuery = db.Items.IncludeUser();
itemQuery = itemQuery.IncludePrimaryNode();
var item = itemQuery.FirstOrDefault(i => i.Id == 1);
}
It's just another way of doing the same thing, but I like the clarity it adds to the code.
So I'm using the 'CodeFirst' methodology of Entity Framework and I have mapping files to map the table information and add in things such as validation so for instance:
this.Property(t => t.AccountName)
.IsRequired()
.HasMaxLength(25);
This is using the Fluent API and I'm wondering how to get the property name by string instead of t.AccountName. I'm wanting to dynamically set these properties and I just don't know how to do that programmatically.
Without commenting on whether this is advisable or not(!), you can achieve what you need because the Property() method takes an expression tree as its parameter. Consider the following:
public class MyEntity
{
[Key]
public int MyEntityId { get; set; }
public string MyProperty { get; set; }
}
public class MyContext : DbContext
{
public DbSet<MyEntity> MyEntities
{
get { return this.Set<MyEntity>(); }
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
var param = Expression.Parameter(typeof(MyEntity));
var propertyExpression = Expression.Lambda(Expression.Property(param, "MyProperty"), param);
modelBuilder.Entity<MyEntity>()
.Property((Expression<Func<MyEntity, string>>)propertyExpression)
.HasColumnName("Fish");
}
}
Here I build configuration for the MyProperty column, which I refer to by name in a lambda expression.
The above code works for string properties, but would require some modification to work for any property type. The cast to Expression<Func<MyEntity, string>> hard-codes the property type, but we can eliminate the cast using the dynamic language feature.
var param = Expression.Parameter(typeof(MyEntity));
dynamic propertyExpression = Expression.Lambda(Expression.Property(param, "MyProperty"), param);
modelBuilder.Entity<MyEntity>()
.Property(propertyExpression)
.HasColumnName("FishFace");
My entity has besides other properties Keyword property which is of type list of strings.
public virtual IList<string> Keywords { get; set; }
so I tried to map this property using conformist mapping by code approach simple as possible like this
Property(x => x.Keywords);
but I'm getting following exception
NHibernate.MappingException : Could not determine type for:
System.Collections.Generic.IList`1[[System.String, mscorlib,
Version=4.0.0.0,.....
You could map this to a private string field and then use string.Split in your Keywords getter to get a list.
public class MyClass {
private string _keywords;
public virtual IEnumerable<string> Keywords {
get { return _keywords.Split(','); }
set { _keywords = string.Join(value, ","); }
}
}
I am not familiar with mapping by code that NH uses (I use FluentNH) but your mapping would probably be something like this:
Map("_keywords", map => {
map.Access(Access.Field);
// ...
});
i defined an entity called Variable and derived classes by using Table Per Hierarchy (TPH). The Base class "Variable" contains a collection of PropertyValues:
private ICollection<PropertyValue> propertyValues;
public const string DiscriminatorColumn = "Discriminator";
public const string Table = "Variables";
public VariableType VariableType { get; set; }
public string Name { get; set; }
[NotMapped]
public string Discriminator { get; set; }
public virtual ICollection<PropertyValue> PropertyValues
{
get { return this.propertyValues ?? (this.propertyValues = new ObservableCollection<PropertyValue>()); }
set { SetProperty(ref this.propertyValues, value, () => PropertyValues); }
}
Now, i want to derive a SpecialVariable class (or more than one), which define some SpecialProperties (e.g. HighLimit) which should be mapped to an entry in the PropertyValues (table).
public class MySpecialVariabe : Variable
{
public double HighLimit { get; set; }
}
My OnModelCreating function looks currently like this:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Variable>().HasKey(x => new { x.Id });
modelBuilder.Entity<Variable>()
.Map<MySpecialVariabe>(m => m.Requires(Variable.DiscriminatorColumn).HasValue(typeof(MySpecialVariabe).Name))
.Map<MySpecialVariabe2>(m => m.Requires(Variable.DiscriminatorColumn).HasValue(typeof(MySpecialVariabe2).Name)).ToTable(Variable.Table);
}
Can someone give me some tips how to realize this, without writing tons of bad looking code in the derived class. (Performance is not that important.)
best regards,
Chris
You can't map properties to records. That is how I understand your question. You have some PropertyValues table which is most probably some Key/Value pair and you want to map entity properties as records (data) to this table. This is not something which EF do for you. You must provide not mapped properties which will work with correct record in propertyValues collection.
Something like:
[NotMapped]
public double HighLimit
{
get
{
var current = propertyValues.SingleOrDefault(p => p.Key == "HighLimit");
return current != null ? current.Value : 0.0;
}
set
{
var current = propertyValues.SingleOrDefault(p => p.Key == "HighLimit");
if (current != null)
{
current.Value = value;
}
else
{
propertyValues.Add(new PropertyValue { Key = "HighLimit", Value = value });
}
}
}
The problem with this approach is that you can't use HighLimit in Linq-to-entities queries - you must always use PropertyValues.
Moreover TPH in EF requires that properties of derived entity (MySpecialVariable) are mapped to the same table as parent entity (Variable). You can't map properties of derived entity into data stored in other table (PropertyValues).