I have the following classes:
public class Publication
{
public int Id { get; set; }
public string Title { get; set; }
public string Headline { get; set; }
public DateTime Published { get; set; }
public ProductContact Contact { get; set; }
}
public class ProductContact
{
public string FullName { get; set; }
public string JobTitle { get; set; }
public string Email { get; set; }
}
And the table associated with this structure, "Publications", has all these fields (included the properties of ProductContact).
If I try to insert a Publication row (with the ProductContact information included) the program throws an exception:
System.NotSupportedException: The member Contact of type ProductContact cannot be used as a parameter value
So, I added a mapper to map out the ProductContact properties to fields in the Properties table:
public PublicationMapper ()
{
TableName = "Publications";
Map(x => x.Contact.FullName).Column("ContactFullName");
Map(x => x.Contact.JobTitle).Column("ContactJobTitle");
Map(x => x.Contact.Email).Column("ContactEmail");
AutoMap();
}
With this mapper I get the same exception.
Then, I added the ignore statement for the Contact field, to tell Dapper to not include this element in the insert statement
Map(x => x.Contact).Ignore();
In this case, I get another exception:
System.Data.SqlClient.SqlException (0x80131904): Must declare the scalar variable "#FullName".
It indicates that Dapper is ignoring completely this property, and the mapping added in the previous step does not have effect.
Is there a way to map out the ProductContact properties to the table fields?
Thank you.
I don't think that this is possible with DapperExtensions.
In one of their issues, they say
Currently, we aren't planning to support nested objects. However, you
can create your own Mapper that will allow for skipping over the
nested object
I have tried a variety of approaches and different mapping classes and can't get anywhere - I don't think that they support any way to map nested property values and to ignore the property itself (which, if not done, will result in "cannot be used as a parameter value" error).
One approach would be to manually flatten your object into an anonymous class (as suggested by #juharr in a comment on your question), another approach would be to use something like AutoMapper to flatten your complex object into a flattened insert model.
Related
I tried this question: How to expose Foreign Key property to existing entity having navigational property using EF6 Code First and it doesn't work. I get the following error:
The index 'IX_FormEntry_Id' is dependent on column 'FormEntry_Id'.
ALTER TABLE ALTER COLUMN FormEntry_Id failed because one or more objects
access this column.
I was just trying to expose the FormEntryId on the FormReport POCO:
public class FormReport : Entity
{
public Guid? FormEntryId { get; set; } //I added this
public virtual FormEntry FormEntry { get; set; }
//other props
}
And I used this mapping as outlined in the above linked answer:
public class FormReportMapping : EntityTypeConfiguration<FormReport>
{
public FormReportMapping()
{
HasRequired(x => x.FormEntry)
.WithOptional()
.Map(p => p.MapKey("FormEntry_Id"));
new EntityMap().MapInheritedProperties(this);
}
}
I was hoping it would recognize hey that's exactly how it is, no change needed, but that's not what's happening, how can I do this?
Edit: I'd like to keep my naming conventions, which doesn't match the auto generated ones by EF. Not a single other of my FK properties use an underscore in my POCOs. But that's what the column name in the DB is.
It can easily be done with data annotations:
public class FormReport : Entity
{
[Column("FormEntry_Id")]) // Map to the existing column name
[ForeignKey("FormEntry")] // Associate with the navigation property
public Guid? FormEntryId { get; set; }
public virtual FormEntry FormEntry { get; set; }
//other props
}
What about fluent API, looks like the only way to achieve the goal is to emulate the above:
public class FormReportMapping : EntityTypeConfiguration<FormReport>
{
public FormReportMapping()
{
Property(x => x.FormEntryId)
.HasColumnName("FormEntry_Id")
.HasColumnAnnotation("ForeignKey", "FormEntry");
// ...
}
}
Using fluent api, how do I map multiple properties of the same data type to a single table conditionally.
Database Model:
ListType would include the Grouping names i.e. Allergens
ListItem would include all possible values for the given type
ProductListItem contains all "selected" values for a given product.
The goal is to use the ProductListItem table and apply it across multiple properties of a model (of the same ProductListItem type) based on the ListType (WHERE ProductListItem.ListTypeID = 1).
public class Product
{
public int ProductID { get; set; }
public List<ProductListItem> Allergens { get; set; }
public List<ProductListItem> DoesNotContain { get; set; }
}
I really don't think that you can achieve this with conditional mapping, but you can cheat.
public List<ProductListItem> Allergens
{
get { return this.ProductListItems.Where(i => i.ListType.Name=="Allergens").ToList=();}
}
Or can you can optionally create a single class for different ListItems with the same baseclass and use the TPH mapping: http://weblogs.asp.net/manavi/inheritance-mapping-strategies-with-entity-framework-code-first-ctp5-part-1-table-per-hierarchy-tph
The code would be something like this:
class Product
{
public List<AllergenProductListItem> Allergens { get; set; }
public List<DoesNotContainListItem> DoesNotContain { get; set; }
}
Its obviously not dynamic regarding the number of the item types (hard to add a new one), but neither is you desired solution, since if you want to have a new type you should modify the code.
Ok, I have done just about everthing that I can think of. I have been all over the search engine of choice for the last 2 days and have yet to find a solution for this.
UPDATE:: I have even gone so far as to flatten the classes as seen below.
Here's what I have...
NOTE:: Class names and property names changed due to sensitivity of data!
I have an entity object named "MyData". It looks like this...
public class MyData
{
public virtual int id { get; set;}
public virtual int StepId { get; set;}
public virtual Decimal ProjectedValue { get; set;}
public virtual String Stage { get; set;}
public virtual String CreatedBy { get; set;}
public virtual DateTime CreatedDate { get; set;}
public virtual int RunId { get; set;}
public virtual Int32 DataKey { get; set;}
public virtual DateTime ForecastDate { get; set;}
public virtual String UnitMeasure { get; set;}
public virtual String FixedFlag { get; set;}
public virtual String DataSource { get; set;}
public virtual String ResourceType { get; set;}
public virtual String DataType { get; set;}
public override bool Equals(object obj)
{
//Not implemented
return false;
}
public override int GetHashCode()
{
return base.GetHashCode();
}
public MyData()
{
}
}
Here is the code being used for the map.
Table("MYDATA");
CompositeId()
.KeyProperty(mtm => mtm.RunId, "RUN_ID")
.KeyProperty(mtm => mtm.DataKey, "CC_KEY")
.KeyProperty(mtm => mtm.ForecastDate, "FORECAST_DATE")
.KeyProperty(mtm => mtm.UnitMeasure, "UOM")
.KeyProperty(mtm => mtm.FixedFlag, "FIXED_FLAG")
.KeyProperty(mtm => mtm.DataSource, "DATA_SOURCE")
.KeyProperty(mtm => mtm.ResourceType, "RESOURCE_TYPE")
.KeyProperty(mtm => mtm.DataType, "DATA_TYPE")
Map(mtm => mtm.StepId, "STEP_ID").Not.LazyLoad();
Map(mtm => mtm.ProjectedValue, "PROJECTED_VALUE");
Map(mtm => mtm.Stage, "STAGE").Not.LazyLoad();
Map(audit => audit.CreatedBy, "CREATED_BY").Not.Nullable();
Map(audit => audit.CreatedDate, "CREATED_DATE");
This class has met the requirements for mapping by NHibernate and Fluent. We are converting NHibernate to Fluent.
When I add the two restrictions to the ICriteria object and call the method to get the data back, I get (in this particular example) over 15000 records back but they are all NULL. There are no properties, no values, nothing. However, the number of returned rows are the EXACT SAME as if I run the select in the database. Here is the code that I use to select the data from the app. Please keep in mind that this exact same code works fine for other objects as we are actually reusing this method!
ICriteria c = _session.CreateCriteria(typeof(T));
foreach (string searchField in searchCriteria.Keys)
{
c.Add(Restrictions.Eq(searchField, searchCriteria[searchField]));
}
IList<T> l = c.List<T>();
When I step through the code, the collection "l" has the rows, but they are all NULL objects. They are, however, of Type.
A couple of final closing thoughts...
Other objects in the code base use this same ICriteria object and return data fine.
The NHibernate mapping of this object matches the Fluent mapping identically.
This application containing this code runs quite well, especially for the amount of
data being consumed and created.
This database table in Oracle has NO PK. (I didn't design it, I just inherited it!)
HELP!! I am completely confused by this return and can't find anything wrong.
I finally figured it out after 3 days of thinking that I'd done something wrong.
Basically, the problem existed in the mapping of the CompositeId object. The objects listed above were edited in an attempt to remove the CompositeId object all together. Instead of ID being a simple INT, it was an object containing 8 other properties.
Well, the CC_KEY column (DataKey property) for the specified RunId was set to NULL in the database. When I tried to query the object and create a compositeKey, the null object was causing the failure on EACH row of the incoming data. This prevented FluentNHibernate from creating the CompositeId object. No Id, no object! This allowed the system to return me the correct number of results but, when attempting to create each object with a null CompositeKey.KeyColumn value, it failed.
Long story short, if you're creating a compositeId for a mapped object, make SURE that all of your data exists and there are no NULL values!
I'm having trouble trying to get ValueInjector to map my objects correctly. This is the code I am using for the mapping:
public IEnumerable<CategoryDTO> FindCategories(IList<object[]> criteria)
{
IEnumerable<Category> categories = _categoryRepo.Find(criteria);
IEnumerable<CategoryDTO> categoriesDto = Mapper.Map<IEnumerable<Category>, IEnumerable<CategoryDTO>>(categories);
return categoriesDto;
}
the variable categories contains a property:
IEnumerable<Standard> Standards
This property contains two Standard objects in the instance I'm calling on. The problem is when I map from my Category to my CategoryDTO. CategoryDTO is defined as this:
public class CategoryDTO : AuditableDTO
{
public Guid CategoryId { get; set; }
public string Name { get; set; }
public string MachineName { get; set; }
public string Description { get; set; }
public IEnumerable<StandardDTO> Standards { get; set; }
}
After the mapping statement is run, and I investigate the contents of categoriesDto.Standards, I can see that it is null. I would have expected my Standards to have mapped, but I'm sure I'm missing something with ValueInjector. Probably something along the lines of telling it how to map Standard to StandardDTO. Any thoughts?
EDIT: I need to clarify, I'm using this http://valueinjecter.codeplex.com/wikipage?title=Automapper%20Simulation&referringTitle=Home
EDIT 2: Digging deeper, I can see that my Iesi.Collections.HashedSet is causing the issue. Categorys' Standards property are typed as Iesi.Collections.ISet, this is turned into the HashedSet. So I guess my real question is how do I check the property for that type and how can I map?
My guess would be that the Mapper.Map doesn't know to map one level deeper than the IEnumerable. Have you tried looping though the collection, mapping it at the Category, CategoryDTO level vs the IEnumerable level?
Is there a way to avoid the explicit Id mapping in Fluent NHibernate?
I want it to somehow generate the entry id automatically so that I wouldn't have to introduce it as a part of the class.
public class HeyMapping
{
public String Name { get; set; }
public DateTime Timestamp { get; set; }
}
public class HeyMapping : ClassMap<HeyMapping>
{
public HeyMapping()
{
Not.LazyLoad();
// I'm not particularly sure how this line works, but
// it fails the mapping unit test.
CompositeId().KeyProperty(x => x.Name);
Map(x => x.Name).Not.Nullable().Length(64);
Map(x => x.Timestamp).Not.Nullable();
}
}
If you want to have no id in your entity, you still have to map an Id so NHibernate knows the database column to use.
You can call
Id<TColumnDataType>("column_name");
Please note that you will give up some NHibernate functionality (specifically cascading updates and the ability to call SaveOrUpdate()) and incur a performance penalty on the database side for having database-only identity (I believe NHibernate will have to make extra queries for comparison).
I usually concede this point and allow the Id as the one persistence concern in my domain classes, so I would do this:
public class HeyMapping
{
protected internal int Id { get; set; } // persistence concern
public virtual String Name { get; set; }
public virtual DateTime Timestamp { get; set; }
}
I realize you might not want to do this; I'm just letting you know that there is a tradeoff.
Create a base class from which all of your mapped entities inherit, then add an Id property to your base class.