Visual studio is giving me a trail of errors that I have hit a dead end on. I am trying to add an existing table in a database to my data model. I understand that the table should have a key, but it doesn't and I can't fix that; it isn't my database to re-design.
When I first try to add the table, I get this error:
The table/view
'BT8_GC.dbo.SAVED_QUERY_CATEGORY' does
not have a primary key defined and no
valid primary key could be inferred.
This table/view has been excluded. To
use the entity, you will need to
review your schema, add the correct
keys, and uncomment it.
Ok, fine, I'll define the keys for it and uncomment. Here is the block I uncomment, after manually defining the keys (I also had to add the nullable=false part):
<EntityType Name="SAVED_QUERY_CATEGORY">
<Key>
<PropertyRef Name="SQC_CAT_ID"/>
<PropertyRef Name="SQC_USER_ID"/>
</Key>
<Property Name="SQC_CAT_ID" Type="int" Nullable="false" />
<Property Name="SQC_USER_ID" Type="int" Nullable="false" />
<Property Name="SQC_CAT_DSCR" Type="varchar" MaxLength="50" />
<Property Name="SQC_SEQ_NO" Type="int" />
</EntityType>
Nope, now the designer won't open. Go back into the file, and Intellisense shows this error:
Error 11002: Entity Type 'SAVED_QUERY_CATEGORY' has no entity set.
Ok... Uncommenting created a new error. After adding an entity set:
<EntitySet Name="SAVED_QUERY_CATEGORY" EntityType="IssueModel.Store.SAVED_QUERY_CATEGORY" store:Type="Tables" Schema="dbo" />
Yay! The designer opens! Still not there yet, because while it shows up under the store's Tables/Views folder, it has not Entity Type in the model. Can't call it from my code. I don't have any errors to work off though, so I tried creating an entity set mapping, but that results in the error "does not exist in MetadataWorkspace".
So that's what I have tried. How do I get this poorly designed table into my data model?
By modifying XML you have only added information about database table. Now you have to open toolbox and add entity to your model in the designer. Configure the entity to have properties as you need. Then open mapping details and map the new entity to your table.
Btw. once you modify the XML describing database manually you cannot use update from database anymore - VS designer will always delete your changes and you will have to do them again.
Related
I am pretty new to NHibernate and I'm trying to create a mapping file to extend a data model project. The particular table I am mapping is called AttributeDef in the following image, the column ControlType actually relates to a lookup in the table called Code (yes, I know - there should be an FK constraint but this sort of thing is quite common in this project so please ignore the obvious howlers and focus on the question). In most cases tables which reference Code also have a column which contains the ID from the table CodeSet as the key in Code is, almost inevitably, a composite key, but not in this case presumably because the original author figured "Hey they're all from the same codeset so what's the point?".
Now if there was a column in AttributeDef which contained the CodeSet value then the mapping wouldn't be much of a problem. The mapping for the Code entity looks like this:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Activus.DataModel" namespace="Activus.DataModel">
<class name="Code" table="Code" mutable="false">
<composite-id name="CompositeCodeId" class="CompositeCodeId">
<key-property name="CodeId" column="CodeId"/>
<key-property name="CodeSet" column="CodeSet"/>
</composite-id>
<property name="Description" column="Description" type="string" length="100" not-null="true"/>
<property name="ExternalRef" column ="ExternalRef" type ="string" length ="256" not-null ="true"/>
<property name="InternalRef" column ="InternalRef" type ="string" not-null ="false"/>
<many-to-one name="CodeSet" class="CodeSet" column="CodeSet" not-null="true" insert="false" update="false"/>
</class>
</hibernate-mapping>
Therefore if there was a column in AttributeDef for the CodeSet value (notionally called FormControlCodeSet in this example) then in my AttributeDef mapping file I would include
<many-to-one name="ControlType" class="Code" not-null="false">
<column name="ControlType" />
<column name="FormControlCodeSet" />
</many-to-one>
And all should be well. The problem is that to add that column to AttributeDef would be very invasive as I would then have to make a LOT of other changes to accommodate this and that would increase the risk factor of the change I'm making to a point which might well be unacceptable (from the client's point of view given their time frame).
So, much as it's a horrible, horrible thing to contemplate, Is it possible to substitute the line
<column name="FormControlCodeSet" />
With a (whisper it) hard coded value? That value hasn't changed in a decade and isn't likely to anytime soon but it would get us past this change and would highlight the need to scope out and implement the inclusion of the extra column. I recognise how dreadful this is but unfortunately a lot of this database isn't really that well suited to ORM despite it's being shoe-horned in anyway.
You do not have to whisper requirements, when working with NHiberante. Because cooperation with legacy DB (i.e. fixed DB schema) is pretty standard, NHibernate does support many different settings.
One of these is a pair of (substitutional) settings: "column" vs "formula". The first takes the column as is, the second could do whatever we need. Take column, columns, pass a constant. So:
<many-to-one name="ControlType" class="Code" not-null="false">
<column name="ControlType" />
<!-- let's replace this -->
<!--<column name="FormControlCodeSet" />-->
<!-- 1) with a constant number 123-->
<formula>123</formula>
<!-- 2) with a constant string 'constantString' -->
<formula>'constantString'</formula>
</many-to-one>
Not sure if the FormControlCodeSet constant should be int or string, but as shown above, either option is possible.
All of my datamodels have field DateAdded. When the user (through MVC Web API) sends a request to save a model, the serverside populates this field with a DateTime object, and then proceeds to save the object via hibernate.
Now consider the case of updating. Even if the user modifies the DateAdded field manually, it should not change the value in the database. Is there a way that I can specify for this field to NOT be updated, regardless of what the user provides? If the field is not populated by the user, the datetime field becomes null, which is also no good.
Turns out there's a property for this you can set in the hbm file!
Simply set update="false" on all properties that you don't want to be updateable. Sorry for not doing more research before wasting precious SO resources.
Change all your mappings to:
<property name="DateAdded" update="false">
<column name="DateAdded" sql-type="smalldatetime" not-null="true" />
</property>
Still a n00b in NHibernate :(
I want to add a composite index to the hbm.xml of one of my POCOs, for performance purposes. It does not relate directly to a class, but rather two common values I will be querying against.
I think I need to do a <map></map> entry in the XML, but the XSD is asking for a class name on the composite-key element, and there is no direct relationship, per se... plus it's asking for more information than I think I would need to provide. How do I do this?
<map name="PropertyKeys">
<key>
<column name="StockID" />
<column name="PropertyName" />
</key>
<composite-index class="Something?">
<key-property name="What goes here?" />
</composite-index>
<what else goes here?>
</map>
Have you read this http://ayende.com/blog/4045/nhibernate-mapping-map It explains when you would use a map and further on it explains the composite-index
However without more background info it is not 100% certain that you need a map!
Is it possible to set type to just date (NOT datetime) via entity framework designer?
I had a look around and the only answer that I've found is a post from MSDN forum from a year ago...
http://social.msdn.microsoft.com/Forums/en/adodotnetentityframework/thread/28e45675-f64b-41f0-9f36-03b67cdf2e1b
I'm very new here and I don't really understand the instructions where they talk about structural annotations...
I can go through the generated SQL script and change each line but I rather not do that...
Structural annotation - nice. It is the first time I heard about this feature but it works. I just tried it. I will try to explain it little bit.
Structural annotations are just random xml added to EDMX file. EDMX file is in fact just XML wich has 4 parts - CSDL, MSL, SSDL and part related to positioning elements in the designer.
CSDL describes entities and associations among entities (defined in the designer)
SSDL describes tables and relations
MSL describes mapping between CSDL and SSDL
If you start with model first (you want to generate database from your model), you have only CSDL part and both SSDL and MSL will be generated by some automatic process (T4 templates executed in workflow) once SSDL is created another T4 template will generate SQL script for database creation.
Structural annotation described in linked MSDN forum's thread is a hint. You will place structural annotation into CSDL part of the EDMX (you must open EDMX as XML - click on the file in solution explorer and choose Open with). My test CSDL describes single User entity with three properties (entity is visible on screenshot later in the answer):
<!-- CSDL content -->
<edmx:ConceptualModels>
<Schema xmlns="http://schemas.microsoft.com/ado/2008/09/edm"
xmlns:cg="http://schemas.microsoft.com/ado/2006/04/codegeneration"
xmlns:store="http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator"
xmlns:annotation="http://schemas.microsoft.com/ado/2009/02/edm/annotation"
xmlns:custom="http://tempuri.org/custom"
Namespace="Model" Alias="Self" >
<EntityContainer Name="ModelContainer" annotation:LazyLoadingEnabled="true">
<EntitySet Name="UsersSet" EntityType="Model.User" />
</EntityContainer>
<EntityType Name="User">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Type="Int32" Name="Id" Nullable="false" annotation:StoreGeneratedPattern="Identity" />
<Property Type="String" Name="Login" Nullable="false" />
<Property Type="DateTime" Name="CreatedAt" Nullable="false">
<custom:SqlType edmx:CopyToSSDL="true">Date</custom:SqlType>
</Property>
</EntityType>
</Schema>
</edmx:ConceptualModels>
I have added custom namespace definition in Schema element: xmlns:custom="http://tempuri.org/custom" and defined custom structural annotation for CreatedAt property:
<Property Type="DateTime" Name="CreatedAt" Nullable="false">
<custom:SqlType edmx:CopyToSSDL="true">Date</custom:SqlType>
</Property>
The name of the namespace or element used for structural annotation are not important - it is absolutely up to you what names do you use. The only important thing is edmx:CopyToSSDL="true" attribute. This attribute is recognized by T4 template used for SSDL creation and it just takes this element and places it to SSDL. Generated SSDL looks like:
<Schema Namespace="Model.Store" Alias="Self"
Provider="System.Data.SqlClient" ProviderManifestToken="2008"
xmlns:store="http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator"
xmlns="http://schemas.microsoft.com/ado/2009/02/edm/ssdl">
<EntityContainer Name="ModelStoreContainer">
<EntitySet Name="UsersSet" EntityType="Model.Store.UsersSet" store:Type="Tables" Schema="dbo" />
</EntityContainer>
<EntityType Name="UsersSet">
<Key>
<PropertyRef Name="Id" />
</Key>
<Property Name="Id" Type="int" StoreGeneratedPattern="Identity" Nullable="false" />
<Property Name="Login" Type="nvarchar(max)" Nullable="false" />
<Property Name="CreatedAt" Type="datetime" Nullable="false">
<custom:SqlType xmlns:custom="http://tempuri.org/custom">Date</custom:SqlType>
</Property>
</EntityType>
</Schema>
The only point was moving the structural annotation to SSDL. All annotations are accessible in metadata through some name value collection. Now you need to modify T4 template responsible for SQL script generation to recognize this annotation and use the value defined in the annotation instead of type defined in the property. You can find the template in:
C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\Extensions\Microsoft\Entity Framework Tools\DBGen\SSDLToSQL10.tt
Copy template file to new location (so that you don't modify the original one) and replace default table creation with this:
-- Creating table '<#=tableName#>'
CREATE TABLE <# if (!IsSQLCE) {#>[<#=schemaName#>].<#}#>[<#=tableName#>] (
<#
for (int p = 0; p < entitySet.ElementType.Properties.Count; p++)
{
EdmProperty prop = entitySet.ElementType.Properties[p];
#>
[<#=Id(prop.Name)#>] <#
if (prop.MetadataProperties.Contains("http://tempuri.org/custom:SqlType"))
{
MetadataProperty annotationProperty = prop.MetadataProperties["http://tempuri.org/custom:SqlType"];
XElement e = XElement.Parse(annotationProperty.Value.ToString());
string value = e.Value.Trim();
#>
<#=value#> <# } else { #> <#=prop.ToStoreType()#> <# } #> <#=WriteIdentity(prop, targetVersion)#> <#=WriteNullable(prop.Nullable)#><#=(p < entitySet.ElementType.Properties.Count - 1) ? "," : ""#>
<#
}
#>
);
GO
Now the last point is changing the template used for SQL script generation. Open EDMX file in the designer and go to model's properties (just click somewhere in the designer while you have properties window opened). Change DDL Generation Template to the template you modified.
Run Generate Database from Model and it will create SQL script containing:
-- Creating table 'UsersSet'
CREATE TABLE [dbo].[UsersSet] (
[Id] int IDENTITY(1,1) NOT NULL,
[Login] nvarchar(max) NOT NULL,
[CreatedAt] Date NOT NULL
);
GO
This is probably the most advanced and hidden feature of EDMX I have seen yet. Annotations together with custom T4 templates can get you a lot of control over both class and SQL generation. I can imagine using this to define for example database indexes or unique keys when using model first or add selectively some custom attributes to generated POCO classes.
The reason why this is so hidden is that there is no tooling support in VS out-of-the box to use this.
From NuGet look for TiraggoEdmx, it serves up all the low level information from your EDMX files in a very nice way. See http://brewdawg.github.io/Tiraggo.Edmx/
I have been successfully using NHibernate for quite some time now and
have been able to solve a lot of pitfalls with an application that I
developed with it and that is running in production. The recent hurdle
really has me scratching my head, though.
Recently I had to expand the class library with some new classes that
are nested as children to some already existing classes. I just copied
the same model for aggregate mapping that I already was successfully
using, but this time it does not work.
Now when I use the following in the parent mapping file:
<bag name="SeaInfoItems" table="EDIImport_SeaInfo" lazy="false" cascade="save-update">
<key column="EDI_FK_OWNERID"/>
<one-to-many class="FargoGate.AppLib.EdiImportSeaInfo, FargoGate.AppLib"/>
</bag>
I can choose to, in the child class, either use:
<property name="EDI_FK_OWNERID" column="EDI_FK_OWNERID" />
...which gives me the infamous "Invalid Index n for this
SqlParameterCollection with Count=n" error.
OR I try with this solution I found after some Googling:
<property name="EDI_FK_OWNERID" column="EDI_FK_OWNERID" insert="false" update="false" />
...which gives me a "Cannot insert the value NULL into column
'EDI_FK_OWNERID'... column does not allow nulls." error.
So basically I have to choose between pest and cholera.
What I don't get is that it works flawlessly for the already existing
aggregate classes, and I really cannot spot the difference. The only
thing is that this foreign key (EDI_FK_OWNERID) could refer to two
different parent tables. Bad database design, I know, but I didn't
design it, and it is my task to develop up to it for better or worse. I cannot change the database design.
The other difference is that I totally removed the foreign key reference from the already existing child classes (the mappings as well as the class members). I tried to emulate that of course, but of no avail.
Also I discovered that one of the new classes (which is quite small) also works fine. But I cannot see what the difference is here either. I am stumped!
Anyone has a clue?
Aaargh! I was put so much on a wrong leg with this infamous “Invalid Index n for this SqlParameterCollection with Count=n” error that I overlooked the obvious: A duplicate mapping of a field for ONE of the classes.
In that particular mapping I left this error, where the primary key is also defined as a property:
<id name="ID" column="ID">
<generator class="guid" />
</id>
<property name="ID" column="ID" />
Now that was a waste of time trying to debug that!