Mapping list of strings in nhibernate - c#

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);
// ...
});

Related

Linq to Entities could not be translated

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

Get type of property being configured from PrimitivePropertyConfiguration using entity framework

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.

Automapper projection (EF) with encapsulated child collections

I use Automapper to map from EF entities to view models.
I now have this entity
public class MenuGroup : IEntity
{
public int MenuGroupId { get; set; }
protected ICollection<MenuGroupItem> _menuGroupItems { get; set; }
public IEnumerable<MenuGroupItem> MenuGroupItems { get { return _menuGroupItems; } }
public void AddMenuItem(MenuGroupItem menuGroupItem)
{
_menuGroupItems.Add(menuGroupItem);
}
}
That is an encapsulated collection, I followed instructions here to make this work: http://lostechies.com/jimmybogard/2014/05/09/missing-ef-feature-workarounds-encapsulated-collections/
So I configure it like so this.HasMany(x => x.MenuGroupItems).WithRequired(x => x.BelongsTo).WillCascadeOnDelete(true);
Now the problem I get is when I try to use automapper to map my MenuGroup into a viewmodel.
I run this code: menuGroup = _context.MenuGroups.Project().To<MenuGroupEditModel>().Single(x => x.UniqueUrlFriendlyName == request.UniqueUrlFriendlyName);
and get this error: The specified type member 'MenuGroupItems' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.
Now I can work with the collection, it saves correctly to the database and all is well there it's only when i want to user automapper here that it fails.
If I replace the protected ICollection and public IEnumerable with simply: public ICollection<MenuGroupItem> MenuGroupItems { get; set; } it works right away so the problem lies in automapping with my encapsulated collection.
Update: I also tried this menuGroup = _context.MenuGroups.Include(x => x.MenuGroupItems).Where(x => x.UniqueUrlFriendlyName == request.UniqueUrlFriendlyName).Project().ToSingleOrDefault<MenuGroupEditModel>(); with no difference other than that it errored in the ToSingleOrDefault instead.
Your problem is that Automapper can't modify MenuGroupItems because there is no public setter.
Your solution is changing it to this:
public IEnumerable<MenuGroupItem> MenuGroupItems { get; set; }
public void AddMenuItem(MenuGroupItem menuGroupItem)
{
MenuGroupItems.Add(menuGroupItem);
}
After some more debugging I figured out the Config file looking like this
public MenuGroupConfiguration()
{
this.HasMany(x => x.MenuGroupAssigments).WithRequired(x => x.BelongTo).WillCascadeOnDelete(true);
this.HasMany(x => x.MenuGroupItems).WithRequired(x => x.BelongsTo).WillCascadeOnDelete(true);
}
had not been included leading to that error that now makes sense.
I can add as a general tip that if you don't use auto-mapper for a query but still use your encapsulated collection remember that you have to call decompile for it to work.
like so
var menuGroupsWithType =
_context.MenuGroups.Include(x => x.MenuGroupItems).Include(x => x.MenuGroupAssigments).Where(x => x.MenuGroupAssigments.Any(y => y.AssignToAll == selectedStructureType))
.OrderBy(x => x.Name).Decompile().ToList();

Fluent Nhibernate List<string> mapping

I have a simple class with a IList<string> property. How to map this property in Fluent Nhibernate ?
[Serializable]
public class ExportTask
{
private IList<string> _csvExportList = new List<string>();
public ExportTask()
{}
public virtual IList<string> CsvExportList
{
get { return _csvExportList; }
set { _csvExportList = value; }
}
}
public class ExportTaskMap : SubclassMap<ExportTask>
{
public ExportTaskMap()
{
HasMany(x => x.CsvExportList)
.Element("CsvExportList")
.Cascade
.AllDeleteOrphan();
}
}
Following error occurs:
Initializing -failed to lazily initialize a collection of role: MyApp.Tasks.ExportTask.CsvExportList, no session or session was closed
When calling addrange on the collection:
var exportList = new List<string>()
{
{"item1"},
{"item2"}
};
CsvExportList.AddRange(exportList);
It truns out we can use AsList mapping with a column for the list index and allworks great. I wonder why there are no answers out there for this simple usecase. Hope it helps out someone.
public class ExportTaskMap : SubclassMap<ExportTask>
{
public ExportTaskMap()
{
HasMany(x => x.CsvExportList)
.Element(#"CsvProperty")
.KeyColumn(#"ExportTask_id")
.Table(#"CsvExportProperties")
.AsList(x => x.Column(#"CsvPropertyListIndex"))
.Not.LazyLoad();
}
}
And the mapped table will look like the following in the database.
Would be helpful to see the error you get, but one thing seems to be obvious: you are missing setter of the IList<string> CsvExportList. So, mapping should target the field
HasMany<string>(Reveal.Property<string>("_csvExportList"))
Check these how to handle field mapping:
Private collection mapping in fluent nhibernate
How do I map a protected collection in Fluent NHibernate?
Or change your IList<string> to have at least protected setter (I personally would go this way) and remove the readonly setting.
private IList<string> _csvExportList;
public virtual IList<string> CsvExportList
{
get { return _csvExportList ?? (_csvExportList = new List<string>(); }
protected set { _csvExportList = value; }
}
These are hints, the exception or error you get could tell us more

How can I Map a substring to a property with a Fluent Nhibernate Mapping Override?

I have a string property on a class that I would like mapped to a Substring of another column.
Lets say this is my class:
public class MyClass
{
public virtual string PartNumber { get; set; }
public virtual string PartNumberPortion { get; set; }
}
And this is my MappingOverride:
public void Override(AutoMapping<MyClass> mapping)
{
mapping.Map(x => x.PartNumberPortion, "PartNumber").Formula("SUBSTRING(4,20, PartNumber)");
}
The .Formula() piece doesn't work like I had hoped. Is it possible to map a field to a substring of another field?
FYI, I wouldn't need to do this if I could run this query:
PartNumber.Substring(3).Contains("12345")
Unfortunately, having a Substring in a query results in:
Cannot use subqueries on a criteria
without a projection.
I successfully got something like this to work in my solution
public override(AutoMapping<MyClass> mapping)
{
mapping.Map(x=>x.PartNumberPortion).Formula("SUBSTRING(PartNumber, 4, 20)");
}

Categories

Resources