- Home
- LLBLGen Pro
- LLBLGen Pro Runtime Framework
Migration LLBLGen Pro Runtime from 5.7 to 5.10 - No connection string specified
Joined: 02-Mar-2021
Hello After Migrating our LLBLGen Pro Runtime from 5.7 to 5.10 I have problem receiving connection string
ErrorMessage : No connection string specified. Please specify a connection string in the app/web.config file or through the RuntimeConfiguration system. StackTrace: at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterCore.CreateNewPhysicalConnection(String connectionString) at SD.LLBLGen.Pro.ORMSupportClasses.AdapterSpecific.TransactionAdapter.CreateConnection(String connectionString) at SD.LLBLGen.Pro.ORMSupportClasses.TransactionBase.AssureConnectionIsPresent() at SD.LLBLGen.Pro.ORMSupportClasses.Adapter.QueryCreationManager.CreateSelectDQ(QueryParameters parameters) at Ness.ProvidentFunds.Common.DataAccess.Extensions.Services.NessQueryCreationManager.CreateSelectDQ(QueryParameters parameters) in c:\Builds\6\Gaya\GAYA_BUILD.4.8_AUTO\Sources\DataAccess\All\Ness.ProvidentFunds.Common.DataAccess.Extensions\NessDataAccessAdapter\Services\NessQueryCreationManager.cs:line 125 at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterCore.FetchEntityCollectionInternal(QueryParameters parameters) at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterCore.FetchEntityCollection(QueryParameters parameters) at SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.<>c__DisplayClass10_0.<FetchEntityCollection>b__0() at Ness.ProvidentFunds.Common.DataAccess.Extensions.NessDataAccessAdapter.FetchEntityCollection(QueryParameters parameters) in c:\Builds\6\Gaya\GAYA_BUILD.4.8_AUTO\Sources\DataAccess\All\Ness.ProvidentFunds.Common.DataAccess.Extensions\NessDataAccessAdapter\NessDataAccessAdapter.cs:line 153 at Ness.ProvidentFunds.Common.DataAccess.Extensions.DataAccessAdapterExceptionsDecorator.FetchEntityCollection(IEntityCollection2 collectionToFill, IRelationPredicateBucket filterBucket, Int32 maxNumberOfItemsToReturn, ISortExpression sortClauses, IPrefetchPath2 prefetchPath, ExcludeIncludeFieldsList excludedIncludedFields, Int32 pageNumber, Int32 pageSize) in c:\Builds\6\Gaya\GAYA_BUILD.4.8_AUTO\Sources\DataAccess\All\Ness.ProvidentFunds.Common.DataAccess.Extensions\NessDataAccessAdapter\DataAccessAdapterExceptionsDecorator.cs:line 531 we use a custom configuration for connection string from web.config and not the default key from appSettings (<add key="Main.ConnectionString.Oracle (ODP.NET)…")
therefor on our DataAccessAdapter I have override the ConnectionString Property
public override string ConnectionString
{
get
{
return m_connectionString;
}
set
{
if (m_connectionString == null)
{
IConnectionStringProvider provider = new ConfigurationDalAppConfigConnStringProvider();
m_connectionString = provider.GetConnectionString();
}
base.ConnectionString = m_connectionString;
}
}
On previews version all CreateSelectDQ functions been at the DataAccessAdapter and received the connection string
In new version since 5.8 refactoring the CreateSelectDQ moved to the new class QueryCreationManager which not contains the property ConnectionString
I can't override it at that deriving class and there for its probably go to default config value Main.ConnectionString.Oracle (ODP.NET).
How can I set or override to the custom connection string config value for that new class as well as done for the DataAccessAdapter?
Thank you
Joined: 17-Aug-2003
The Connection string is either read from the config file (.net framework builds) or passed in using the RuntimeConfiguration class. Your adapter that fails is that your client version (which you derive yourself from dataaccessadaptercore) or the server side one? And which .net version are you using (e.g. if you use .net 6+, you have to use the RuntimeConfiguration class)
Joined: 02-Mar-2021
I am using framework 4.8 the adapter is the server side which derive from dataaccessadaptercore and override the connection string , loading connection string success while using its fetch methods . but, the class that failed to load connection string is the new class that inherit QueryCreationManager when using the CreateSelectDQ method. somehow the ConnectionString is not passes to that class. and i cant find away to do it. here's the code :
public class NessQueryCreationManager : QueryCreationManager
{
[NessDependency]
public ISelectDQDependencyBuilder SelectDQDependencyBuilder { get; set; }
#region constants
private const string _traceLLBLGENQueryesTitle = "LLBLGEN_Queries";
private const string _llblgenSaveErrorsTitle = "LLBLGEN_Save_Errors";
private const string _resultCountTraceTitle = "LLBLGEN_ResultCount";
#endregion
#region ctor
public NessQueryCreationManager(DataAccessAdapterCore containingAdapter, IPersistenceInfoProvider persistenceInfoProvider)
: base(containingAdapter, persistenceInfoProvider)
{
}
#endregion
#region private members
private readonly TraceWriterHelper _traceHelper = new TraceWriterHelper();
#endregion
#region CreateDQ override
protected override IRetrievalQuery CreateSelectDQ(QueryParameters parameters)
{
long maxNumberOfItemsToReturn = (long)parameters.RowsToTake;
IPredicateExpression filter = parameters.FilterToUseAsPredicateExpression;
if (filter == null)
{
filter = new PredicateExpression();
}
// ISelectDQDependencyBuilder selectDQDependencyBuilder = new SelectDQDependencyBuilder();
ISelectDQDependencyBuilder selectDQDependencyBuilder =
ServerFactoryBuilder.Create().Resolve<ISelectDQDependencyBuilder>();
selectDQDependencyBuilder.AddDependencyToSelectDQObjects
(
parameters.RelationsToUse,
filter,
(IEntityFields2)parameters.FieldsForQuery
);
this.InsertPersistenceInfoObjects(parameters.RelationsToUse);
this.InsertPersistenceInfoObjects(filter);
if (parameters.RowsToTake == 0)
{
parameters.RowsToTake = (int)this.GetMaxNumberOfItemsToReturn(maxNumberOfItemsToReturn);
}
IRetrievalQuery updatedQuery = base.CreateSelectDQ(parameters);
updatedQuery.Command.CommandText = updatedQuery.Command.CommandText.ToUpper();
//write to trace
this._traceHelper.TraceQuery(updatedQuery, "CreateSelectDQ-after using entity relations. User : " + GetCurrentUser() + " , ManagingOrg : " + new IrgunMenahelKodProvider().GetCurrentIrgunMenahelKod(), _traceLLBLGENQueryesTitle);
return updatedQuery;
}
#endregion CreateDQ override
#region Private methods
/// <summary>
/// get max number of items to return
/// if page size not defined ( <=0 ) , get number from
/// </summary>
private long GetMaxNumberOfItemsToReturn(long maxNumberOfItemsToReturn)
{
DataAccessConfigurationSection dalConfigurationSection =
ConfigurationServiceBuilder.GetConfigurationService().GetSection<DataAccessConfigurationSection>();
return maxNumberOfItemsToReturn == 0 ? dalConfigurationSection.MaxNumberOfItemsToReturn : maxNumberOfItemsToReturn;
}
private String GetCurrentUser()
{
IEnvironmentInformationProvider environmentInformationProvider =
ServerFactoryBuilder.Create().Resolve<IEnvironmentInformationProvider>();
string currentUser = environmentInformationProvider.GetUserName();
return currentUser;
}
#endregion
}
the Exception occurs at thtis line : IRetrievalQuery updatedQuery = base.CreateSelectDQ(parameters);
Joined: 17-Aug-2003
You have to give more context and the full classes you're using. You quote code snippets but where they're used exactly isn't clear. As from a previous thread of yours you said you use a client side adapter and a server side adapter, so which one is it where the connection string fails? and additionally, please give the full adapter class you use that fails
Joined: 02-Mar-2021
Otis wrote:
You have to give more context and the full classes you're using. You quote code snippets but where they're used exactly isn't clear. As from a previous thread of yours you said you use a client side adapter and a server side adapter, so which one is it where the connection string fails? and additionally, please give the full adapter class you use that fails
While using FetchEntityCollection
The base.FetchEntityCollection(parameters);
Is calling inside to CreateSelectDQ, wich located now at the new class QueryCreationManager
When I set this key value in config its ok <add key="Main.ConnectionString.Oracle (ODP.NET)" value="…
Buy I want to use other config in custom manner when all the data access types using same key value
The CreateSelectDQ which is now in new class NessQueryCreationManager is failed retrieve connection string since it is no longer apart of the DataAccessAdapter
How can I override its connection string to use my custom one.
This is our NessDataAccessAdapter containing the override of ConnectionString Property and the override of CreateQueryCreationManager.
public class NessDataAccessAdapter : DataAccessAdapter
{
static string m_connectionString;
#region constants
private const string _traceLLBLGENQueryesTitle = "LLBLGEN_Queries";
private const string _llblgenSaveErrorsTitle = "LLBLGEN_Save_Errors";
private const string _resultCountTraceTitle = "LLBLGEN_ResultCount";
#endregion
#region ctor
public NessDataAccessAdapter()
: base()
{
}
#endregion
#region Dependency Properties
public override string ConnectionString
{
get
{
return m_connectionString;
}
set
{
if (m_connectionString == null)
{
IConnectionStringProvider provider = new ConfigurationDalAppConfigConnStringProvider();
m_connectionString = provider.GetConnectionString();
}
base.ConnectionString = m_connectionString;
}
}
[NessDependency]
public IGetEntitySectionConfigurationValuesProvider GetEntitySectionConfigurationValuesProvider { get; set; }
[NessDependency]
public IUpdateEntityFieldsService UpdateEntityFieldsService { get; set; }
// [NessDependency]
//public INessQueryCreationManager NessQueryCreationManager { get; set; }
[NessDependency]
public ISelectDQDependencyBuilder SelectDQDependencyBuilder { get; set; }
#endregion
#region private members
private readonly TraceWriterHelper _traceHelper = new TraceWriterHelper();
#endregion
#region public override IDataAccessAdapter fetch methods
protected override QueryCreationManager CreateQueryCreationManager(IPersistenceInfoProvider persistenceInfoProvider)
{
//NessQueryCreationManager nessQueryCreationManager = ServerFactoryBuilder.Create().Resolve<NessQueryCreationManager>();
return new NessQueryCreationManager(this,persistenceInfoProvider);
//return ServerFactoryBuilder.Create().Resolve<NessQueryCreationManager>();
}
public override void FetchEntityCollection(QueryParameters parameters)
{
base.FetchEntityCollection(parameters);
this._traceHelper.TraceCount(parameters.CollectionToFetch.Count.ToString(), "result count", "FetchEntityCollection", _resultCountTraceTitle);
}
#endregion
#region public override IDataAccessAdapter Save methods
/// <summary>
/// overrides <see cref="Ness.ProvidentFunds.Server.DataAccess.DatabaseSpecific.DataAccessAdapter.SaveEntity"/>
/// </summary>
public override bool SaveEntity(IEntity2 entityToSave, bool refetchAfterSave, IPredicateExpression updateRestriction, bool recurse)
{
const string methodName = "SaveEntity";
const string messageDescription = "SaveEntityInfo : ";
Guard.ThrowIfNull(entityToSave, "entityToSave");
try
{
// update entity fields before save
this.UpdateEntityFieldsService.UpdateEntityFields(entityToSave);
bool result = base.SaveEntity(entityToSave, refetchAfterSave, updateRestriction, recurse);
this.HandleSaveEntityResult(entityToSave, refetchAfterSave, recurse, methodName, messageDescription, result);
return result;
}
catch (Exception ex)
{
this._traceHelper.TraceErrors(ex.ToString(), messageDescription, methodName, _llblgenSaveErrorsTitle);
throw new SaveEntityException(ex);
}
}
/// <summary>
/// overrides <see cref="Ness.ProvidentFunds.Server.DataAccess.DatabaseSpecific.DataAccessAdapter.SaveEntityCollection"/>
/// </summary>
public override int SaveEntityCollection(IEntityCollection2 collectionToSave, bool refetchSavedEntitiesAfterSave, bool recurse)
{
Guard.ThrowIfNull(collectionToSave, "collectionToSave");
const string methodName = "SaveEntityCollection";
const string messageDescription = " SaveEntityCollection :";
try
{
// update entity fields collection before save
this.UpdateEntityFieldsService.UpdateEntityFieldsCollection(collectionToSave);
int result = base.SaveEntityCollection(collectionToSave, refetchSavedEntitiesAfterSave, recurse);
this.HandleSaveEntityCollectionResult(collectionToSave, refetchSavedEntitiesAfterSave, recurse, methodName, messageDescription, result);
return result;
}
catch (Exception ex)
{
_traceHelper.TraceErrors(ex.ToString(), messageDescription, methodName, _llblgenSaveErrorsTitle);
throw new SaveEntityException(ex);
}
}
#endregion
#region CreateDQ override
/* Methods Moved to NessQueryCreationManager */
#endregion CreateDQ override
#region Private methods
/// <summary>
/// get max number of items to return
/// if page size not defined ( <=0 ) , get number from
/// </summary>
private long GetMaxNumberOfItemsToReturn(long maxNumberOfItemsToReturn)
{
DataAccessConfigurationSection dalConfigurationSection =
ConfigurationServiceBuilder.GetConfigurationService().GetSection<DataAccessConfigurationSection>();
return maxNumberOfItemsToReturn == 0 ? dalConfigurationSection.MaxNumberOfItemsToReturn : maxNumberOfItemsToReturn;
}
private String GetCurrentUser()
{
IEnvironmentInformationProvider environmentInformationProvider =
ServerFactoryBuilder.Create().Resolve<IEnvironmentInformationProvider>();
string currentUser = environmentInformationProvider.GetUserName();
return currentUser;
}
#endregion
#region handle Data Access Adapter override methods results
/// <summary>
/// display save errors details
/// </summary>
private void HandleSaveEntityResult(IEntity2 entityToSave, bool refetchAfterSave, bool recurse, string methodName, string messageDescription, bool result)
{
if (result)
{
return;
}
Type entityType =
entityToSave == null ? null : entityToSave.GetType();
string entityTypeName =
entityType == null ?
"null" : entityType.Name;
string message =
string.Format
(
"SaveEntity return false for entity <{0}> , refetchAfterSave = <{1}>,recurse = <{2}>",
entityTypeName,
refetchAfterSave.ToString(),
recurse.ToString()
);
this._traceHelper.TraceErrors(message, messageDescription, methodName, _llblgenSaveErrorsTitle);
}
/// <summary>
/// handle save entity collection result
/// </summary>
private void HandleSaveEntityCollectionResult(IEntityCollection2 collectionToSave, bool refetchSavedEntitiesAfterSave, bool recurse, string methodName, string messageDescription, int result)
{
if (result >= 0) return;
Type type =
collectionToSave.GetType();
string typeName =
type == null ?
"null" : type.Name;
string message = string.Format
(
"SaveEntityCollection return false for entity <{0}> , refetchAfterSave = <{1}>,recurse = <{2}>",
typeName,
refetchSavedEntitiesAfterSave.ToString(),
recurse.ToString()
);
this._traceHelper.TraceErrors(message, messageDescription, methodName, _llblgenSaveErrorsTitle);
}
#endregion
}
Joined: 17-Aug-2003
The connection string is read in the generated method ReadConnectionStringFromConfig
in DataAccessAdapter. This method is called from the default ctor, so if you do:
var a = new NessDataAccessAdapter();
it will end up in that method. However if you generate code for netstandard or .net 6+ the ReadConnectionStringFromConfig
method will have a call to RuntimeConfiguration. You can easily check this in the debugger. Please create a simple line of code like the one above and set a breakpoint at the DataAccessAdapter.ReadConnectionStringFromConfig method in the generated DataAccessAdapter class. This should then take you through the read action for the connection string.
Joined: 02-Mar-2021
Otis wrote:
The connection string is read in the generated method
ReadConnectionStringFromConfig
in DataAccessAdapter. This method is called from the default ctor, so if you do:var a = new NessDataAccessAdapter();
it will end up in that method. However if you generate code for netstandard or .net 6+ the
ReadConnectionStringFromConfig
method will have a call to RuntimeConfiguration. You can easily check this in the debugger. Please create a simple line of code like the one above and set a breakpoint at the DataAccessAdapter.ReadConnectionStringFromConfig method in the generated DataAccessAdapter class. This should then take you through the read action for the connection string.
Hi Otis I am using Target Platform .NET 4.8 the Adapter using the base ctor in generated DataAccesAdapter with DataAccessAdapter.ReadConnectionStringFromConfig, that read the connection string from config file - key value is declared in that generated adapter as public static string ConnectionStringKeyName= "Main.ConnectionString.Oracle (ODP.NET)"; and in debug it is going through the read action for the connection string in DataAccessAdapter ctor its right and working when using this default key . but, we don't want to use that key and at some point we removed it from config and used overriding the property ConnectionString in our adapter which replaced the default one. but now its seems like its not using that property . is there any change have been made in the last versions related the ConnectionString? that its not using the override property of ConnectionString? or any way to override the DataAccessAdapter.ReadConnectionStringFromConfig
Joined: 17-Aug-2003
Ah right.
The generated adapter has a constructor public DataAccessAdapter(string connectionString) : this(connectionString, false, null, null) { }
which ends up in InitClassPhase2 where it passes the connection string to the constructor of the contained TransactionAdapter
The connectionstring property of the base class sets the connectionstring on the contained TransactionAdapter instance, but it's not used if there's already an open connection. I think you don't use a TransactionAdapter on the client (as that was the reason the dispose failed with a null reference exception IIRC) so it's not used.
I'm honestly confused what the real problem is however, as you use a customized class on the client but also need the full adapter code there?
Joined: 02-Mar-2021
Otis wrote:
Ah right. The generated adapter has a constructor
public DataAccessAdapter(string connectionString) : this(connectionString, false, null, null) { }
which ends up in InitClassPhase2 where it passes the connection string to the constructor of the contained TransactionAdapterThe connectionstring property of the base class sets the connectionstring on the contained TransactionAdapter instance, but it's not used if there's already an open connection. I think you don't use a TransactionAdapter on the client (as that was the reason the dispose failed with a null reference exception IIRC) so it's not used.
I'm honestly confused what the real problem is however, as you use a customized class on the client but also need the full adapter code there?
the other post with the disposed from clientAdapter is ok . This post is related to the server Adapter NessDataAccessAdapter. where the ConnectionString got overrided and QuaryManager got separated from DataAccessAdapter.
Joined: 17-Aug-2003
Ok, so if you want to set the connection string on the server side, you have to pass in the connectionstring through the constructor that accepts the connection string. Did you try that?
Joined: 02-Mar-2021
Otis wrote:
Ok, so if you want to set the connection string on the server side, you have to pass in the connectionstring through the constructor that accepts the connection string. Did you try that?
OK, I did try it now , passed in the connectionString through a (new ) constructor,
Now its working , thanks!!
I had to refracture a bit my Factory to create the Adapter with new CTOR having the paramter ConnectionString and pass it to the base one.
public NessDataAccessAdapter(string connectionString) :base(connectionString) {}
just for thoughts ,it was better to save backward compatibly, if it was still able to use the overridden property ConnectionString from DataAccessAdapter as it was in previous versions.
Joined: 17-Aug-2003
guyronen wrote:
just for thoughts ,it was better to save backward compatibly, if it was still able to use the overridden property ConnectionString from DataAccessAdapter as it was in previous versions.
I see we have a design flaw indeed. While setting the connectionstring property does set the connectionstring in the transaction object, the adapter creates the transaction object at creation time so it immediately opens the connection. Setting the connectionstring afterwards has then no effect.
We'll look into changing this behavior.
Joined: 17-Aug-2003
I can't reproduce it actually.
[Test]
public void DifferentConnectionStringTest()
{
IEntity2 c = new CustomerEntity("ALFKI");
// set the connectionstring to an invalid one on the adapter, it should throw an exception
using(var adapter = new DataAccessAdapter())
{
adapter.ConnectionString = "data source=server;initial catalog=Northwind;user id=someuser;pwd=somepassword;persist security info=False;packet size=4096;Encrypt=false";
Assert.Throws(typeof(SqlException), ()=>adapter.FetchEntity(c));
}
}
The connection string set here in my test is wrong, it has a bad userid/password. This is immediately used in the fetch because what I initially thought was the case isn't actually: the transaction isn't opening a connection immediately. That is, if you follow the call path from the default constructor ( new DataAccessAdapter()
).
So I have the feeling when you set the connection string property the connection is already open. Is that the case? Please post a code snippet that reproduces the problem
Joined: 02-Mar-2021
Hi Otis I am not initiating ConnectionString by setting into Adapter.ConnectionString Property. I have overrides the inner Adapter DataAccessAdapterCore.ConnectionString and setting it from there base.ConnectionString = m_connectionString; this propert getreturn the value like I posted earlier in my inheritor class NessDataAccessAdapter
public NessDataAccessAdapter()
: base()
{
}
private static string m_connectionString;
public override string ConnectionString
{
get
{
return m_connectionString;
}
set
{
if (m_connectionString == null)
{
IConnectionStringProvider provider = new ConfigurationDalAppConfigConnStringProvider();
m_connectionString = provider.GetConnectionString();
}
base.ConnectionString = m_connectionString;
}
}
So, in llblGenRuntime version 5.7.2 your infrastructure supported it and once you create an instance of DataAccessAdapter its hit this property ..
in version 5.10 it is not ..
that's was my problem
you can try it by create class Adapter that inherited DataAccessAdapter as I did : public class NessDataAccessAdapter : DataAccessAdapter
using the empty ctor :
public NessDataAccessAdapter()
: base()
{
}
then override the property ConnectionString
when create new yourDataAccessAdapter you will see in 5.7.2 , when debug it will go through the property and in version 5.10 it wont
Joined: 02-Mar-2021
Walaa wrote:
I see you have created NessQueryCreationManager, inheriting from QueryCreationManager, how exactly are you using this class, and do you pass an object of NessAdapter to its CTor?
have a look up on the NessDataAccessAdapter there is an override for CreateQueryCreationManager as requested by the migration of 5.8 change
protected override QueryCreationManager CreateQueryCreationManager(IPersistenceInfoProvider persistenceInfoProvider)
{
return new NessQueryCreationManager(this,persistenceInfoProvider);
}
Joined: 17-Aug-2003
It's indeed an internal refactoring that causes the flow of the connectionstring no longer go through the property, we directly set it on the transaction object (the wrapper which controls it); and setting the property does the same thing. Previously (before 5. we had all that code in the DataAccessAdapter base class so we set the property indeed, hence it worked in your case.
I don't think we should refactor it to go through the property, but it's good to know why your code failed, so if another person runs into this we know why Please use the constructor which is meant for this situation instead.