I'm developing an application in C# using Visual Studio 2012 that must generate, among other things, a generic data model, an *.edmx file. This program receives a UML model and transforms it into C# classes and data models in an MVC architecture, so, I haven't got the relations, PK's, FK's between tables.
My problem is to generate the relation between tables (Navigation Properties?). For each data resource (table), I must check if it has foreign keys and, if it does, generate the right code to create an edmx file correctly. Here's what I've got so far:
My edmxCodeGen class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DataComponent;
namespace codeGenAppMM2XulRdf
{
public class edmxCodeGen
{
public static string toCS(AppDataModel appRDF)
{
StringBuilder csResult = new StringBuilder();
csResult.AppendLine("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
csResult.AppendLine("<edmx:Edmx Version=\"3.0\" xmlns:edmx=\"http://schemas.microsoft.com/ado/2009/11/edmx\">");
csResult.AppendLine("<!-- EF Runtime content -->");
csResult.AppendLine("<edmx:Runtime>");
csResult.AppendLine("<!-- SSDL content -->");
csResult.AppendLine("<edmx:StorageModels>");
csResult.AppendLine(" <Schema Namespace=\"BDClinicModel.Store\" Alias=\"Self\" Provider=\"System.Data.SqlClient\" ProviderManifestToken=\"2005\" xmlns:store=\"http://schemas.microsoft.com/ado/2007/12/edm/EntityStoreSchemaGenerator\" xmlns=\"http://schemas.microsoft.com/ado/2009/11/edm/ssdl\">");
csResult.AppendLine(" <EntityContainer Name=\"BDClinicModelStoreContainer\">");
// ver namespace e container name nas duas linhas acima
foreach (DataResource dr in appRDF.dataResources)
{
csResult.AppendLine(" <EntitySet Name=" + dr.ResourceClassName + " EntityType=\"BDClinicModel.Store." + dr.ResourceClassName + " store:Type=\"Tables\" Schema=\"dbo\" />");
}
csResult.AppendLine(" <EntitySet Name=\"sysdiagrams\" EntityType=\"BDClinicModel.Store.sysdiagrams\" store:Type=\"Tables\" Schema=\"dbo\" />");
// sysdiagrams
foreach (DataResource dr in appRDF.dataResources)
{
foreach (NavigationProperty np in dr.NavigationAttrs)
{
}
}
csResult.AppendLine("");
csResult.AppendLine("");
csResult.AppendLine("");
csResult.AppendLine("");
return csResult.ToString();
}
}
}
I intend to use the two final "foreach" cycles to write what I'm missing, something like this (from some other example in which I imported a database and the edmx was generated from it automatically):
<AssociationSet Name="FK__consulta__id_mar__3493CFA7" Association="BDClinicModel.Store.FK__consulta__id_mar__3493CFA7">
<End Role="marcacao" EntitySet="marcacao" />
<End Role="consulta" EntitySet="consulta" />
</AssociationSet>
<AssociationSet Name="FK__consulta__id_pro__3587F3E0" Association="BDClinicModel.Store.FK__consulta__id_pro__3587F3E0">
<End Role="processo" EntitySet="processo" />
<End Role="consulta" EntitySet="consulta" />
</AssociationSet>
...
Problem is, how do I detect the relations? I think I must iterate through the data resources and check (maybe?) the navigation properties, but I can't see how.
Can someone help please? Any ideas?
Thanks, Chiapa
I managed to detect the relations this way:
foreach (UMLRelationship rel in umlDomainModel.UMLClassRelationships)
{
if (rel.relType == Enum_TypeOfRelation.aggregation || rel.relType == Enum_TypeOfRelation.association || rel.relType == Enum_TypeOfRelation.composition)
{
csResult.AppendLine("");
...
This way I iterate through all the relations of the UML domain model to check afterwards if the relation type is one of the three (aggregation, association or composition). If it is, then the relation exists and I can use the roles and classes associated.
Thanks
Related
First of all , excuse my low knowledge in Hibernate (I'm a sql fan) , I've done many research and I just can't find how to properly do this below
What I would like to do :I Have a table called ClassCodes and I want to insert each Equivalence code next to his original code
So my 2 columns are Originalcode and Equivalencecodes (Let's assume Originalcode are already filled)
Here is the function I would love to have help
public void addEquivalenceCodes(string Code, string EquivalenceCode)
try
{
using (ISession session = OpenSession())
{
using (ITransaction transaction =session.BeginTransaction())
{
//**Here is what I don't know how to write properly in hibernate
String hql = "INSERT INTO ClassCodes(CodeEquiv)" + "VALUES ("+EquivalenceCode+") WHERE Originalcode = "+Code+";
Query query = session.createQuery(hql);
transaction.Commit();
}
}
}
catch (Exception e) { Console.WriteLine(e); }
}
Here is my mapping , to add more visual help
<class name="ClassCodes, table="[T0101_ClassCode]" lazy="false">
<id name="Id" column="[Id]">
<generator class="native" />
</id>
<property name="OriginalCode" column="[OriginalCode]" />
<property name="EquivalanceCode" column="[EquivalanceCode]" />
etc...
I appreciate all guides , tips and explication I can get !
it's not an insert it's an update.
using (var session = new Configuration().Configure().BuildSessionFactory().OpenSession())
{
ClassCodes classcodes=session.Get<ClassCodes>(Originalcode);
classcodes.EquivalanceCode="EquivalanceCode value";
using (ITransaction transaction = session.BeginTransaction())
{
session.SaveOrUpdate(classcodes);
transaction.Commit();
}
}
I have an entity framework model on top of a SQL server backend.
I have two tables, Treatment, and Segment. Treatment has a one-to-many relationship with Segment. In the DB, I have a cascading delete setup on the foreign key relationship between the tables so that when a Treatment is deleted, all of it's corresponding Segments are also deleted. This work when I delete a treatment from the DB directly.
In the EF model, I have changed the "End1 OnDelete" property to "Cascade" as well. However, whenever I try to delete a Treatment, I get the following error:
"The operation failed: The relationship could not be changed because one or more of the foreign-key properties is non-nullable. When a change is made to a relationship, the related foreign-key property is set to a null value. If the foreign-key does not support null values, a new relationship must be defined, the foreign-key property must be assigned another non-null value, or the unrelated object must be deleted."
Here is the code where I delete the Treatment:
public bool Delete(Treatment myTreatment)
{
bool result = false;
using (myEntities myObjectContext = new myEntities())
{
if (myTreatment.Treatment_ID == 0)
{
result = true;
}
else
{
if (myTreatment.EntityState == System.Data.EntityState.Detached)
{
myObjectContext.Treatments.Attach(myTreatment);
}
myObjectContext.ObjectStateManager.ChangeObjectState(myTreatment, System.Data.EntityState.Deleted);
}
result = (myObjectContext.SaveChanges() != 0);
}
return result;
}
What am I doing wrong?
EDIT
Here is the CSDL for the Association per #Fauxtrot's request.
<Association Name="TreatmentSegment">
<End Type="tamcModel.Treatment" Role="Treatment" Multiplicity="1" >
<OnDelete Action="Cascade" />
</End>
<End Type="tamcModel.Segment" Role="Segment" Multiplicity="*" >
</End>
<ReferentialConstraint>
<Principal Role="Treatment">
<PropertyRef Name="Treatment_ID" />
</Principal>
<Dependent Role="Segment">
<PropertyRef Name="Treatment_ID" />
</Dependent>
</ReferentialConstraint>
</Association>
After playing around with it a bit, I stumbled upon the following solution. I'm posting it here so others in the future who might have this problem can view a solution.
I changed the following line:
myObjectContext.ObjectStateManager.ChangeObjectState(myTreatment, System.Data.EntityState.Deleted);
To this:
myObjectContext.DeleteObject(myTreatment);
And now it works....
hmmmm....
In general, I tend to name my sql database columns using the following camel case convention:
camelCase (notice that the first letter is in lower case).
But when working with C#, I like to name my object's public properties in the following convention:
PascalCase (notice the first is in uppwer case).
Entity Framework's default behaviour is to name the created classes' properties to match their relative column names as they are in the database.
Is there any property in the project/solution level which can be changed in order to solve this issue?
Yes there is. Here you can see the full example:
using System;
using System.Data.Entity;
namespace ConsoleApplication1
{
class MyDbContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Properties().Configure(c =>
{
var name = c.ClrPropertyInfo.Name;
var newName = char.ToLower(name[0]) + name.Substring(1);
c.HasColumnName(newName);
});
}
public MyDbCondenxt(string cs) : base(cs)
{
}
public DbSet<MyModel> MyModels { get; set; }
}
class Program
{
static void Main(string[] args)
{
var context = new MyDbContext ("DefaultConnection");
context.MyModels.Add(new MyModel{SomeText = "hello"});
context.SaveChanges();
Console.ReadLine();
}
}
class MyModel
{
public int Id { get; set; }
public string SomeText { get; set; }
}
}
The property name is "SomeText" and the column name is "someText".
I dont know of a solution level but you can set an attribute on your entity
[Table("myEntity")]
public class MyEntity{}
Achieving this is not impossible, but it's not going to be easy. Some of it depends which type of ef model you're working with, either code first or database/model first (they are similar in this regard), or if you're using the old ObjectContext based methods.
In general, EF uses T4 templating to create the actual classes and models in all but code first, so it's possible to edit the T4 templates and generate whatever you want, such as automatically generating properties with PascalCasing.
If you're using Code first (which doesn't really require that you code first, it's a terrible name) then you can use the Entity Framework power tools to reverse engineer your database to code first models, and again it uses T4 to do this.
If you're using actual code first (ie you create your models and generate your database from the model), then it may not be possible in the existing EF5 or lower. EF6 (currently in alpha) has something known as custom conventions that you could probably use for this, but it's still a long way from production quality.
Earlier I also had that kind of problem. So I simply write a tool in c# to rename the existing edmx file and then after the renaming each section of edmx file,Next using T4 template regenerate the Poco classes. It resolved my problem. It generates the expected POCO classes with Camel Case properties. Basically in the edmx we have 3 layers. So we need to modify 2 layers of them.
MappingsSection
ConceptualModelsSection
Please find the following class to do that.
namespace Edmx_Manager_V1._0
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
public static class RenameManager
{
public static XmlDocument Document = new XmlDocument();
public static string FilePath;
public static XmlNamespaceManager nsmgr;
/// <summary>
/// Updates the conceptual models section.
/// </summary>
public static void UpdateConceptualModelsSection()
{
///////////////////////update ConceptualModels section//////////////////////////////////////////////////////////
XmlNodeList Schema = Document.SelectNodes("/edmx:Edmx/edmx:Runtime/edmx:ConceptualModels/edm:Schema", nsmgr);
XmlNode SchemaNode = Schema[0];
XmlElement SchemaNodeXmlElement = SchemaNode as XmlElement;
//get all EntitySet nodes under EntityContainer node
XmlNodeList EntitySetlist = SchemaNodeXmlElement.GetElementsByTagName("EntitySet");
//get all EntityType nodes under SchemaNode
XmlNodeList EntityTypelist = SchemaNodeXmlElement.GetElementsByTagName("EntityType");
foreach (XmlNode EntityTypenode in EntityTypelist)
{
//to call GetElementsByTagName we need XmlElement object
XmlElement EntityTypenodeelement = EntityTypenode as XmlElement;
//get all PropertyRef nodes under EntityType node
XmlNodeList PropertyReflist = EntityTypenodeelement.GetElementsByTagName("PropertyRef");
foreach (XmlNode PropertyRefnode in PropertyReflist)
{
//update name attribute of Key/PropertyRef nodes
XmlAttribute PropertyRef_nameAttribute = PropertyRefnode.Attributes["Name"];
PropertyRef_nameAttribute.Value = UppercaseFirst(PropertyRef_nameAttribute.Value);
}
//get all Property nodes under EntityType node
XmlNodeList Propertylist = EntityTypenodeelement.GetElementsByTagName("Property");
foreach (XmlNode Propertynode in Propertylist)
{
//update name attribute of PropertyRef nodes
XmlAttribute Property_nameAttribute = Propertynode.Attributes["Name"];
Property_nameAttribute.Value = UppercaseFirst(Property_nameAttribute.Value);
}
//get all NavigationProperty nodes under EntityType node
XmlNodeList NavigationPropertylist = EntityTypenodeelement.GetElementsByTagName("NavigationProperty");
foreach (XmlNode NavigationPropertynode in NavigationPropertylist)
{
//update name attribute of NavigationProperty nodes
XmlAttribute NavigationPropertynode_nameAttribute = NavigationPropertynode.Attributes["Name"];
NavigationPropertynode_nameAttribute.Value = UppercaseFirst(NavigationPropertynode_nameAttribute.Value) + "s";// we append "s" for nav properties
}
}
//get Association node under Schema node
XmlNodeList Associationlist = SchemaNodeXmlElement.GetElementsByTagName("Association");
//get all Association nodes and process
foreach (XmlNode AssociationNode in Associationlist)
{
if (AssociationNode != null)
{
XmlElement AssociationNodeXmlElement = AssociationNode as XmlElement;
//get all end nodes under Association
XmlNodeList EndNodelist2 = AssociationNodeXmlElement.GetElementsByTagName("End");
//get all PropertyRef nodes under Association
XmlNodeList PropertyReflist2 = AssociationNodeXmlElement.GetElementsByTagName("PropertyRef");
foreach (XmlNode PropertyRefNode2 in PropertyReflist2)
{
//update Type attribute
XmlAttribute PropertyRefNode2Attribute = PropertyRefNode2.Attributes["Name"];
PropertyRefNode2Attribute.Value = UppercaseFirst(PropertyRefNode2Attribute.Value);
}
}
}
Console.WriteLine("ConceptualModelSection updated..");
}
/// <summary>
/// Updates the mappings section.
/// </summary>
public static void UpdateMappingsSection()
{
///////////////////////update edmx:Mappings section//////////////////////////////////////////////////////////
XmlNodeList EntityContainerMapping = Document.SelectNodes("/edmx:Edmx/edmx:Runtime/edmx:Mappings/cs:Mapping", nsmgr);
XmlNode EntityContainerMapping_Node = EntityContainerMapping[0];
XmlElement EntityContainerMappingNode_XmlElement = EntityContainerMapping_Node as XmlElement;
// update name attribute of all EntitySetMapping nodes
//get all EntitySetMapping nodes
XmlNodeList EntitySetMappinglist = EntityContainerMappingNode_XmlElement.GetElementsByTagName("EntitySetMapping");
//get all EntityTypeMapping nodes
XmlNodeList EntityTypeMappinglist = EntityContainerMappingNode_XmlElement.GetElementsByTagName("EntityTypeMapping");
//get all ScalarProperty nodes
XmlNodeList ScalarPropertyist = EntityContainerMappingNode_XmlElement.GetElementsByTagName("ScalarProperty");
foreach (XmlNode ScalarPropertyNode in ScalarPropertyist)
{
XmlAttribute nameAttribute = ScalarPropertyNode.Attributes["Name"];
nameAttribute.Value = UppercaseFirst(nameAttribute.Value);
}
Console.WriteLine("MappingSection updated..");
}
/// <summary>
/// Uppercases the first.
/// </summary>
/// <param name="name">The name.</param>
/// <returns></returns>
private static string UppercaseFirst(string name)
{
return char.ToUpper(name[0]) + name.Substring(1);
}
}
}
Usage :
RenameManager.FilePath = #"C:\Users\therath\testApp\Model1.edmx";
// Path of edmx file in the your solution
RenameManager.Document.Load(#RenameManager.FilePath);
RenameManager.nsmgr = new XmlNamespaceManager(RenameManager.Document.NameTable);
RenameManager.nsmgr.AddNamespace("edmx", "http://schemas.microsoft.com/ado/2008/10/edmx");
RenameManager.nsmgr.AddNamespace("edm", "http://schemas.microsoft.com/ado/2008/09/edm");
//nsmgr.AddNamespace("ssdl", "http://schemas.microsoft.com/ado/2009/02/edm/ssdl");
RenameManager.nsmgr.AddNamespace("cs", "http://schemas.microsoft.com/ado/2008/09/mapping/cs");
try
{
RenameManager.UpdateConceptualModelsSection();
RenameManager.UpdateMappingsSection();
RenameManager.Document.Save(#RenameManager.FilePath);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message.ToString());
}
If you generate the edmx again you may need to run this tool again.
there are ways to do that , some of are already pointed out by others..
i found one class which does that ...
namespace System.Data.Entity.ModelConfiguration.Conventions
{
/// <summary>
/// Convention to convert any data types that were explicitly specified, via data annotations or <see cref="T:System.Data.Entity.DbModelBuilder"/> API,
/// to be lower case. The default SqlClient provider is case sensitive and requires data types to be lower case. This convention
/// allows the <see cref="T:System.ComponentModel.DataAnnotations.ColumnAttrbiute"/> and <see cref="T:System.Data.Entity.DbModelBuilder"/> API to be case insensitive.
///
/// </summary>
public sealed class ColumnTypeCasingConvention : IDbConvention<DbTableColumnMetadata>, IConvention
{
internal ColumnTypeCasingConvention()
{
}
[SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase")]
void IDbConvention<DbTableColumnMetadata>.Apply(DbTableColumnMetadata tableColumn, DbDatabaseMetadata database)
{
if (string.IsNullOrWhiteSpace(tableColumn.TypeName))
return;
tableColumn.TypeName = tableColumn.TypeName.ToLowerInvariant();
}
}
}
explicit implementation of idbconvertion does that thing which you can implement
another one is to
go to solution => and find folder obj/debug/edmxresourcestoembed
there are three files db.csdl , db.msl , db.ssdl
edit msl file => you 'll see mapping for each table like as under.
<EntitySetMapping Name="Address">
<EntityTypeMapping TypeName="IsTypeOf(AdventureWorksLTModel.Address)">
<MappingFragment StoreEntitySet="Address">
<ScalarProperty Name="AddressID" ColumnName="AddressID" />
<ScalarProperty Name="AddressLine1" ColumnName="AddressLine1" />
<ScalarProperty Name="AddressLine2" ColumnName="AddressLine2" />
<ScalarProperty Name="City" ColumnName="City" />
<ScalarProperty Name="StateProvince" ColumnName="StateProvince" />
<ScalarProperty Name="CountryRegion" ColumnName="CountryRegion" />
<ScalarProperty Name="PostalCode" ColumnName="PostalCode" />
<ScalarProperty Name="rowguid" ColumnName="rowguid" />
<ScalarProperty Name="ModifiedDate" ColumnName="ModifiedDate" />
</MappingFragment>
</EntityTypeMapping>
</EntitySetMapping>
You can change it in the .edmx file. Just click on the property name and rename it to camel case and the same will be reflected whenever you try to access it using the object.
You can use external tools like
http://www.devart.com/entitydeveloper/
With the inbuild EDMX desinger this isnt possible, since the "update from database" routine doesnt have such a function.
The class code is generated from a T4 template. Depending on your configuration, this may already be in your project, or the EDMX may be using a built-in one, in which case you would need to add your own and set the "code generation strategy" to "None" in the EDMX properties (through the model browser). From this file it is fairly easy to find and modify the class and property name generation.
The default is calling an Escape function, which is defined in include files at "IDE/Extensions/Microsoft/Entity Framework Tools/Templates/Includes" under the Visual Studio folder, and ultimately just calls CreateEscapedIdentifier. Simply call this with the capitalized version of the string.
FYI: These names are coming from the EntityType and NavigationProperty objects as defined in the EDMX, not directly from the database. If you are using the "generate from database" feature for the EDMX, the names may have already gone through a transformation and the original table names are not retained in the model. This probably won't be an issue, though.
Well you can edit the names in the edmx actually, but everytime you refresh from database it's back to doing it again.
The only viable approach when using edmx type datatypes is to have the correct names (with capital letters) in the tables of the database or it will be to tedious.
You can of cause use link to sql instead, in which case you define your data classes and just supply a name property. But be warned this approach is significantly more manual and is being aborted most places because it requires much more thinking to set up that edmx autogeneration which is a click, click, next approach.
So yes you can edit the names in the edmx, but consider abandoning your camelCasing for tables instead, in hommage to edmx which saves you for a ton on work in return, or your .net autogenerated proxy classes will look wierd, as you know.
My goal is to pull XML data from the API and load it to a sql server database. The first step I'm attempting here is to access the data and display it. Once I get this to work I'll loop through each row and insert the values into a sql server database. When I try to run the code below nothing happens and when I paste the url directly into the browser I get this error
"2010-03-08 04:24:17 Wallet exhausted: retry after 2010-03-08 05:23:58. 2010-03-08 05:23:58"
To me it seems that every iteration of the foreach loop makes a call to the site and I get blocked for an hour. Am I retrieving data from the API in an incorrect manner? Is there some way to load the data into memory or an array then loop through that?
Here's the bit of code I hacked together.
using System;
using System.Data.SqlClient;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Xml;
using System.Data;
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
try
{
string userID = "123";
string apiKey = "abc456";
string characterID = "789";
string url = "http://api.eve-online.com/char/WalletTransactions.xml.aspx?userID=" + userID + "&apiKey=" + apiKey + "&characterID=" + characterID;
XmlDocument xmldoc = new XmlDocument();
xmldoc.Load(url);
XmlNamespaceManager xnm1 = new XmlNamespaceManager(xmldoc.NameTable);
XmlNodeList nList1 = xmldoc.SelectNodes("result/rowset/row", xnm1);
foreach (XmlNode xNode in nList1)
{
Response.Write(xNode.InnerXml + "<br />");
}
}
catch (SqlException em)
{
Response.Write(em.Message);
}
}
}
Here's a sample of the xml
<eveapi version="2">
<currentTime>2010-03-06 17:38:35</currentTime>
<result>
<rowset name="transactions" key="transactionID" columns="transactionDateTime,transactionID,quantity,typeName,typeID,price,clientID,clientName,stationID,stationName,transactionType,transactionFor">
<row transactionDateTime="2010-03-06 17:16:00" transactionID="1343566007" quantity="1" typeName="Co-Processor II" typeID="3888" price="1122999.00" clientID="1404318579" clientName="unseenstrike" stationID="60011572" stationName="Osmeden IX - Moon 6 - University of Caille School" transactionType="sell" transactionFor="personal" />
<row transactionDateTime="2010-03-06 17:15:00" transactionID="1343565894" quantity="1" typeName="Co-Processor II" typeID="3888" price="1150000.00" clientID="1404318579" clientName="unseenstrike" stationID="60011572" stationName="Osmeden IX - Moon 6 - University of Caille School" transactionType="sell" transactionFor="personal" />
</rowset>
</result>
<cachedUntil>2010-03-06 17:53:35</cachedUntil>
</eveapi>
Some quick searching (google) shows that this is the EVE API cache mechanism kicking in. If you query the same data from the same key and IP, it tells you to re-use what you already have. So make sure you write any results you do get to disk or database straight away. Some cache times are 1 day...
#Jarek
You could just store the xml file as a local cache to your webservers file structure like .../Users/thisusersid/mycustomfile_02102011.xml, then when the user logs in again just pull the file, split the file name, check the dates and use the most recent file as the default. Then you can allow the user to update their data from the eveapi manually by clicking a button/link to retrieve the latest & greatest.
(Follow-Up-Question to How to change LINQ O/R-M table name/source during runtime?)
I need to change the table source of a LINQ 2 SQL O/R-Mapper table during runtime. To achieve this, I need to create an XmlMappingSource. On command line, I could use SqlMetal to create this mapping file, but I would like to create the mapping file during runtime in memory.
The XmlMappingSource is a simple xml file, looking something like this:
<?xml version="1.0" encoding="utf-8"?>
<Database Name="MyDatabase" xmlns="http://schemas.microsoft.com/linqtosql/mapping/2007">
<Table Name="dbo.MyFirstTable" Member="MyFirstTable">
<Type Name="MyFirstTable">
<Column Name="ID" Member="ID" Storage="_ID" DbType="UniqueIdentifier NOT NULL" IsPrimaryKey="true" IsDbGenerated="true" AutoSync="OnInsert" />
<Association Name="WaStaArtArtikel_WaVerPreisanfragen" Member="WaStaArtArtikel" Storage="_WaStaArtArtikel" ThisKey="ArtikelID" OtherKey="ID" IsForeignKey="true" />
</Type>
</Table>
<Table Name="dbo.MySecondTable" Member="MySecondTable">
<Type Name="MySecondTable">
<Column Name="ID" Member="ID" Storage="_ID" DbType="UniqueIdentifier NOT NULL" IsPrimaryKey="true" IsDbGenerated="true" AutoSync="OnInsert" />
<Column Name="FirstTableID" Member="FirstTableID" Storage="_FirstTableID" DbType="UniqueIdentifier NOT NULL" />
<Association Name="MySecondTable_MyFirstTable" Member="MyFirstTable" Storage="_MyFirstTable" ThisKey="FirstTableID" OtherKey="ID" IsForeignKey="true" />
</Type>
</Table>
</Database>
This should be possible to create using reflection, for example I can get the database name from a data context like this:
using System.Data.Linq.Mapping;
using System.Xml.Linq;
XDocument mapWriter = new XDocument();
DatabaseAttribute[] catx = (DatabaseAttribute[])typeof(WcfInterface.WaDataClassesDataContext).GetCustomAttributes(typeof(DatabaseAttribute), false);
XElement xDatabase = new XElement("Database");
xDatabase.Add(new XAttribute("Name", catx[0].Name));
mapWriter.Add(xDatabase);
My problem: I can't find good documentation of the mapping, so extracting the necessary information is quite error-prone - maybe someone can point me to good docs of the mapping, or, even better, to a code example how to create the mapping file?
Have you considered using LINQ to Entities, the mapping formats for LINQ to Entities are documented.
Use Damien Guard's Open Source T4 templates. They do everything SQLMetal can do and more, and you'll have the full T4 engine behind you.
I just had same problem, also I had no option to change project as its too late to do this.
I needed to update database name in mapping file in my solution.
This solution works.
My database mapping
<?xml version="1.0" encoding="utf-8"?>
<Database Name="DatabaseName" xmlns="http://schemas.microsoft.com/linqtosql/mapping/2007">
<Table Name="dbo.tblDictionary" Member="TblDictionary">
<Type Name="TblDictionary">
<Column Name="lngRecordID" Member="LngRecordID" Storage="_LngRecordID" DbType="Int NOT NULL IDENTITY" IsPrimaryKey="true" IsDbGenerated="true" AutoSync="OnInsert" />
<Column Name="txtWord" Member="TxtWord" Storage="_TxtWord" DbType="VarChar(50) NOT NULL" CanBeNull="false" />
</Type>
</Table>
</Database>
and finally the code:
class Program
{
static void Main(string[] args)
{
// to get embeded file name you have to add namespace of the application
const string embeddedFilename = "ConsoleApplication3.FrostOrangeMappings.xml";
// load file into stream
var embeddedStream = GetEmbeddedFile(embeddedFilename);
// process stream
ProcessStreamToXmlMappingSource(embeddedStream);
Console.ReadKey();
}
private static void ProcessStreamToXmlMappingSource(Stream stream)
{
const string newDatabaseName = "pavsDatabaseName";
var mappingFile = new XmlDocument();
mappingFile.Load(stream);
stream.Close();
// populate collection of attribues
XmlAttributeCollection collection = mappingFile.DocumentElement.Attributes;
var attribute = collection["Name"];
if(attribute==null)
{
throw new Exception("Failed to find Name attribute in xml definition");
}
// set new database name definition
collection["Name"].Value = newDatabaseName;
//display xml to user
var stringWriter = new StringWriter();
using (var xmlTextWriter = XmlWriter.Create(stringWriter))
{
mappingFile.WriteTo(xmlTextWriter);
xmlTextWriter.Flush();
stringWriter.GetStringBuilder();
}
Console.WriteLine(stringWriter.ToString());
}
/// <summary>
/// Loads file into stream
/// </summary>
/// <param name="fileName"></param>
/// <returns></returns>
private static Stream GetEmbeddedFile(string fileName)
{
var assembly = Assembly.GetExecutingAssembly();
var stream = assembly.GetManifestResourceStream(fileName);
if (stream == null)
throw new Exception("Could not locate embedded resource '" + fileName + "' in assembly");
return stream;
}`