Cannot get cascading delete to work in EF - c#

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....

Related

Switching between staging/production databases with an environment variable in Entity Framework 6

I use Azure and slot variables to switch between staging and production databases when pushing code. The two databases will always be identical.
This is a simplified setup of how I am trying to switch between the two.
Web.config
<connectionStrings>
<add name=“StagingDatabase” connectionString=“YYY” />
<add name="ProductionDatabase” connectionString=“XXX” />
</connectionStrings>
Constants.cs
public static DbContext getDatabase()
{
if (#Environment.GetEnvironmentVariable("isDatabaseStaging"))
{
return new StagingDatabase();
}
else
{
return new ProductionDatabase();
}
}
The problem is in Code.cs, because I am returning DbContext, Entity Framework does not know any of the tables in the databases and won't compile citing that dbContext has no definition for Table.
Code.cs
public class useDatabases
{
public static useDatabasesResponse useDatabasesImplementation(useDatabasesRequest request)
{
using (var db = Constants.getDatabase())
{
var table = db.Table.SingleOrDefault(a => a.id == request.id);
if (table == null)
{
return new useDatabasesResponse
{
message = “Table not found!“,
status = 200,
};
}
// do more database stuff, etc..
}
}
}
I should also note that both my databases have seperate .edmx files, maybe it will work fine if I can get them to use the same one?
Rather than switching on one variable, you can just define one connectionString in the web.config and then make the connectionString a slot setting in Azure:
Web.Config
<connectionStrings>
<add name="Database" connectionString="YYY" />
</connectionStrings>
Azure settings:

How to insert into column where otherColumn Equal someValue using Nhibernate

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();
}
}

How can I detect relations between the tables in my UML?

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

NHibernate.Exceptions.GenericADOException : could not execute query

I have a legacy application (vfp 8) that I need to pull data from (no inserts). I am using the Accnum field as the primary key, it is defined in the table as character 11.
Factory configuration:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<reflection-optimizer use="false" />
<session-factory>
<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
<property name="dialect">NHibernate.Dialect.GenericDialect</property>
<property name="connection.driver_class">NHibernate.Driver.OleDbDriver</property>
<property name="connection.connection_string">Provider=VFPOLEDB.1;Data Source=C:\Analysis\Quantium\development\RD warehouse\_RDAUWH\Data;Collating Sequence=MACHINE</property>
<property name="show_sql">false</property>
</session-factory>
</hibernate-configuration>
This is my mapping file:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="RDLabels"
namespace="RDLabels.Domain">
<class name="CustMast">
<id name="Accnum" column="Accnum" type="string">
<generator class="assigned"/>
</id>
<property name="Fullname" />
<property name="Add" />
<property name="State" />
</class>
</hibernate-mapping>
The class:
public class CustMast
{
private string _accnum;
public virtual string Accnum
{
get { return _accnum; }
set { _accnum = value; }
}
private string _fullname;
public virtual string Fullname
{
get { return _fullname; }
set { _fullname = value; }
}
private string _add;
public virtual string Add
{
get { return _add; }
set { _add = value; }
}
private string _state;
public virtual string State
{
get { return _state; }
set { _state = value; }
}
}
Here is the code that gets the record:
public CustMast GetByAccnum(String accnum)
{
using (ISession session = NHibernateHelper.OpenSession())
{
CustMast custMast = session
.CreateCriteria(typeof(CustMast))
.Add(Restrictions.Eq("Accnum", accnum))
.UniqueResult<CustMast>();
return custMast;
}
}
The full error is:
NHibernate.Exceptions.GenericADOException : could not execute query
[ SELECT this_.Accnum as Accnum0_0_, this_.Fullname as Fullname0_0_, this_.Add as Add0_0_, this_.State as State0_0_ FROM CustMast this_ WHERE this_.Accnum = ? ]
Name:cp0 - Value:00059337444
[SQL: SELECT this_.Accnum as Accnum0_0_, this_.Fullname as Fullname0_0_, this_.Add as Add0_0_, this_.State as State0_0_ FROM CustMast this_ WHERE this_.Accnum = ?]
----> System.IndexOutOfRangeException : Invalid index 0 for this OleDbParameterCollection with Count=0. - d:\CSharp\NH\NH\nhibernate\src\NHibernate\Loader\Loader.cs:1590
Running NHibernate Profiler it shows:
WARN:
reflection-optimizer property is ignored out of application configuration file.
WARN:
System.IndexOutOfRangeException: Invalid index 0 for this OleDbParameterCollection with Count=0.
at System.Data.OleDb.OleDbParameterCollection.RangeCheck(Int32 index)
at System.Data.OleDb.OleDbParameterCollection.GetParameter(Int32 index)
at System.Data.Common.DbParameterCollection.System.Collections.IList.get_Item(Int32 index)
at NHibernate.Driver.DriverBase.ExpandQueryParameters(IDbCommand cmd, SqlString sqlString) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Driver\DriverBase.cs:line 235
at NHibernate.AdoNet.AbstractBatcher.ExpandQueryParameters(IDbCommand cmd, SqlString sqlString) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\AdoNet\AbstractBatcher.cs:line 232
at NHibernate.Loader.Loader.PrepareQueryCommand(QueryParameters queryParameters, Boolean scroll, ISessionImplementor session) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Loader\Loader.cs:line 1152
ERROR:
Invalid index 0 for this OleDbParameterCollection with Count=0.
I was struggling with my linq queries throwing the same error whenever i passed them a parameter. If i didn't pass any parameters and did a session.Query() they would work fine.
I struggled with this for days but i found this Nhibernate jira ticket here . It explains an apparent problem with SQLParameters and the Iseries Db2 provider.
I understand you're using a different provider but you might benefit from just downloading the latest Nhibernate core source code, building it, and referencing the latest version in your project. It fixed my issue.
First thing I would try is to run this in SQL and see what happens as it might be a data issue.
SELECT this_.Accnum as Accnum0_0_, this_.Fullname as Fullname0_0_,
this_.Add as Add0_0_, this_.State as State0_0_ FROM CustMast this_
WHERE this_.Accnum = '00059337444'
Is Accnum column defined as a string type (varchar, nvarchar etc) in your database?
Edit OK the next step is to actually confirm the SQL being sent to FoxPro. You will need to set up logging (or download a trial copy of NHProf) to find out if the SQL is correct. Your setup and code looks correct however I am not 100% sure of the choice of dialect as this may be causing you problems.
I take it you have seen this and this.
Edit2 The NHProf error seems to me that it thinks your Id should be a Int32 as it looks like it is calling at System.Data.OleDb.OleDbParameterCollection.GetParameter(Int32 index).
I think you need to add this to your mappings:-
<id name="Accnum" column="Accnum" type="string" >
Note the additional type="string"
If you use ODP.Net then you need an oracle client installed and configured, on your target machine, as well as your development machine.
The error message you post might be caused by having more than one Oracle client installed, or possibly trying to use something like a later version of odp.net with an earlier client version.

FetchExpression results appear to be cached, how do I prevent this?

I am using a FetchExpression against a RetrieveMultiple operation on a CrmOrganizationServiceContext within a windows service to fetch and process items out of a queue.
The first time this runs this fetches the items to be processed correctly. On subsequent calls using the same CrmOrganizationServiceContext instance it always retrieves zero entities with no errors thrown. I have added in new entities and reactivated existing ones that should be fetched using the FetchXml and they aren't retrieved.
As soon as I restart my service it creates a new instance of the CrmOrganizationServiceContext and fetches the new items.
What am I doing wrong here?
public CrmConnector(string connectionString)
{
Context = new CrmOrganizationServiceContext(CrmConnection.Parse(connectionString));
}
public void FetchStuff()
{
string fetchXml = "...";
FetchExpression fetchExpression = new FetchExpression(fetchXml);
EntityCollection entityCollection = Context.RetrieveMultiple(fetchExpression);
// entityCollection.Entities is always empty following first run
}
private CrmOrganizationServiceContext Context { get; set; }
Fetch Xml as requested, the only customisation is the count attribute which limits the number of items being returned (as this is a queue processor)
<fetch version="1.0" output-format="xml-platform" mapping="logical" distinct="false" count="10">
<entity name="xxx1">
<attribute name="xxx_name" />
<attribute name="createdon" />
<attribute name="xxx_1" />
<attribute name="xxx_2" />
<attribute name="xxx_3" />
<attribute name="xxx_4" />
<attribute name="statecode" />
<order attribute="createdon" descending="false" />
<filter type="and">
<condition attribute="xxx_exported" value="0" operator="eq"/>
</filter>
</entity>
</fetch>
It is the CrmOrganizationServiceContext that is doing the caching - I found the following worked a treat and the results of my RetrieveMultiple are no longer cached :)
Context = new CrmOrganizationServiceContext(CrmConnection.Parse(connectionString));
Context.TryAccessCache(cache => cache.Mode = OrganizationServiceCacheMode.Disabled);

Categories