My site is using enterprise library v 5.0. Mainly the DAAB. Some functions such as executescalar, executedataset are working as expected. The problems appear when I start to use Readers
I have this function in my includes class:
Public Function AssignedDepartmentDetail(ByVal Did As Integer) As SqlDataReader
Dim reader As SqlDataReader
Dim Command As SqlCommand = db.GetSqlStringCommand("select seomthing from somewhere where something = #did")
db.AddInParameter(Command, "#did", Data.DbType.Int32, Did)
reader = db.ExecuteReader(Command)
reader.Read()
Return reader
End Function
This is called from my aspx.vb like so:
reader = includes.AssignedDepartmentDetail(Did)
If reader.HasRows Then
TheModule = reader("templatefilename")
PageID = reader("id")
Else
TheModule = "#"
End If
This gives the following error on db.ExecuteReader line:
Unable to cast object of type 'Microsoft.Practices.EnterpriseLibrary.Data.RefCountingDataReader' to type 'System.Data.SqlClient.SqlDataReader'.
Can anyone shed any light on how I go about getting this working. Will I always run into problems when dealing with readers via entlib?
I would be careful with this implementation.
There is a thread on the Enterprise Library Codeplex site that explains the backgound for this:
http://entlib.codeplex.com/Thread/View.aspx?ThreadId=212973
Chris Tavares explains that it's not good to just return the .InnerReader, because then the connection tracking by Enterprise Library is thrown off (his response from May 20, 5:39PM):
"That approach will completely screw up your connection management. The whole reason for the wrapper is so that we could execute extra code to clean stuff up at dispose time. Grabbing the inner reader and throwing out the outer will leak connections!
"
So yes, this is a bit of a pain to deal with, we're in the same situation.
Regards,
Mike
ExecuteReader in Enterprise Library wraps IDataReader into RefCountingDataReader that as SqlDataReader implements IDataReader interface.
RefCountingDataReader has InnerReader property that you can cast to SqlDataReader. The sample below is in C# but you can easily convert it to VB.NET.
SqlDataReader reader;
reader = ((RefCountingDataReader)db.ExecuteReader(command)).InnerReader as SqlDataReader;
if (reader != null)
reader.Read();
return reader;
Hope it helps
I am having leaking connections because all my DA methods require a SqlDataReader.
Now I have to return the inner RefCountingDataReader and can never close the outer reader.
The old Enterprise Library was working fine with returning a SqlDataReader.
I've taken into account the comments and code posted by ctavars at http://entlib.codeplex.com/discussions/212973 and http://entlib.codeplex.com/discussions/211288, resulting in the following generic approach to obtaining an SQL data reader.
In general you use IDataReader in the using statement, then use that reference directly when you can. Call AsSqlDataReader on it when you need something SQL-specific.
Add this extension class somewhere:
/// <summary>
/// Obtains an <see cref="SqlDataReader"/> from less derived data readers in Enterprise Library
/// </summary>
/// <remarks>
/// See http://entlib.codeplex.com/discussions/212973 and http://entlib.codeplex.com/discussions/211288
/// for a discussion of why this is necessary
/// </remarks>
public static class SqlDataReaderExtension
{
/// <summary>
/// Allows the internal <see cref="SqlDataReader"/> of a <see cref="RefCountingDataReader"/> to be accessed safely
/// </summary>
/// <remarks>
/// To ensure correct use, the returned reference must not be retained and used outside the scope of the input
/// reference. This is so that the reference counting does not get broken. In practice this means calling this method
/// on the base reader every time a reference to it is required.
/// </remarks>
public static SqlDataReader AsSqlDataReader(this RefCountingDataReader reader)
{
return (SqlDataReader)(reader.InnerReader);
}
/// <summary>
/// Allows the internal <see cref="SqlDataReader"/> of a <see cref="IDataReader"/> to be accessed safely
/// </summary>
/// <remarks>
/// To ensure correct use, the returned reference must not be retained and used outside the scope of the input
/// reference. This is so that the reference counting does not get broken. In practice this means calling this method
/// on the base reader every time a reference to it is required.
/// </remarks>
public static SqlDataReader AsSqlDataReader(this IDataReader reader)
{
return (SqlDataReader)(((RefCountingDataReader)(reader)).InnerReader);
}
}
... then to read data with an SQLReader, do something like this:
using (IDataReader reader = db.ExecuteReader(command))
{
while (reader.Read())
{
reader.GetInt32(reader.GetOrdinal("SomeColumn")),
reader.GetInt32(reader.GetOrdinal("SomeOtherColumn")),
reader.GetInt32(reader.GetOrdinal("SomeFurtherColumn")),
// Obtain the SQL data reader each time it is used
// (Note that GetDateTimeOffset is not on the standard IDataReader)
reader.AsSqlDataReader().GetDateTimeOffset(reader.GetOrdinal("SQLSpecificColumn"))
reader.AsSqlDataReader().GetDateTimeOffset(reader.GetOrdinal("AnotherSQLSpecificColumn"))
reader.GetString(reader.GetOrdinal("SomeAdditionalColumn"))
}
}
I think I have a working solution.
enter code here
' Create the Database object, using the default database service. The
' default database service is determined through configuration.
Dim db As Microsoft.Practices.EnterpriseLibrary.Data.Database = EnterpriseLibraryContainer.Current.GetInstance(Of Microsoft.Practices.EnterpriseLibrary.Data.Database)(DatabaseName)
Dim dbCommand As DbCommand
dbCommand = db.GetStoredProcCommand(StoredProcedureName)
'create a new database connection based on the enterprise library database connection
Dim dbConnection As System.Data.Common.DbConnection
dbConnection = db.CreateConnection
dbConnection.Open()
'set the dbCommand equal to the open dbConnection
dbCommand.Connection = dbConnection
'return a ADO sqlDatareader but still managed by the EnterpriseLibrary
Return dbCommand.ExecuteReader(CommandBehavior.CloseConnection)
You should use the interface, not the concrete class.
Public Function AssignedDepartmentDetail(ByVal Did As Integer) As IDataReader
Dim reader As IDataReader
Dim Command As SqlCommand = db.GetSqlStringCommand("select seomthing from somewhere where something = #did")
db.AddInParameter(Command, "#did", Data.DbType.Int32, Did)
reader = db.ExecuteReader(Command)
reader.Read()
Return reader
End Function
and the usage. Personally, I would never use a datareader in a presentation layer page, but to each his/her own I guess.
Private Const TemplateFileName_Select_Column_Ordinal As Integer = 0
Private Const Id_Select_Column_Ordinal As Integer = 1
Private Sub DoSomething()
dim reader as IDataReader
reader = includes.AssignedDepartmentDetail(Did)
If reader.HasRows Then
TheModule = reader(TemplateFileName_Select_Column_Ordinal)
PageID = reader(Id_Select_Column_Ordinal)
Else
TheModule = "#"
reader.Close() ''Dude, close your reader(s)
End If
Related
in this tutorial about the EventCloud example app:
https://aspnetboilerplate.com/Pages/Documents/Articles/Developing-MultiTenant-SaaS-ASP.NET-CORE-Angular/index.html
the text states: the creation of a new entity must be done using the static method "Create" in the "Event" class (not using "new Entity(....)")
1) so my first question is: which design pattern is this? Factory? Builder? other?
[Table("AppEvents")]
public class Event : FullAuditedEntity<Guid>, IMustHaveTenant
{
......
....
...
/// <summary>
/// We don't make constructor public and forcing to create events using <see cref="Create"/> method.
/// But constructor can not be private since it's used by EntityFramework.
/// Thats why we did it protected.
/// </summary>
protected Event()
{
}
public static Event Create(int tenantId, string title, DateTime date, string description = null, int maxRegistrationCount = 0)
{
var #event = new Event
{
Id = Guid.NewGuid(),
TenantId = tenantId,
Title = title,
Description = description,
MaxRegistrationCount = maxRegistrationCount
};
#event.SetDate(date);
#event.Registrations = new Collection<EventRegistration>();
return #event;
}
....
...
2) the second question:
than the article says...
Event Manager .... All Event operations should be executed using this class... (EventManager)
ok, the CreateAsync method call the repository insert method, is the static "Event.Create" internaly called from the repository insert method? if yes, could you indicate me the point in the abp source code?
or is it an internal matter of EntityFramework?
public class EventManager : IEventManager
{
......
....
..
public async Task CreateAsync(Event #event)
{
await _eventRepository.InsertAsync(#event);
}
Here are my answers:
1-) Event is being created with a static factory method. There are 2 ways to create an entity in Domain Driven Design.
Creating with static factory methods: It's a convenient way of creating business entities. And this method is being used in EventCloud. The only downside of this method is it's static! If your entity is holding state it's not good for testability. But there are 3 advantages of this approach;
They have names: for example Event.CreatePublicEvent(), Create.PrivateEvent()
They can cache: You can cache them in a private static HashSet or Dictionary.
They can subtype.
Creating with a constructor: If you have only one constructor then creating an object through its public constructor is the most convenient approach in Domain Driven Design. As long as you make parameterless constructor protected or private. Besides, an entity should be responsible for its own data integrity and validity so you have to set all business related public properties as private setter and you should allow them to change through public methods.
Further information, see https://www.yegor256.com/2017/11/14/static-factory-methods.html
2-) EventManager is a domain service that is used for business logic. And Event.Create() is being used in the EventAppService class. Click here to see where exactly is being executed. Even Event.Create() method consists of a single line of code but it's open for extension.
I hope that will be useful ;)
Happy coding...
I currently have a project that's being migrated away from Parse Server but needs to maintain backwards compatibility.
Since Parse Server generates it's own object Ids, rather than using Mongo's I'd like to know:
How does Parse Server generate it's objectIds?
and why does it do this when MongoDB has great objectId generation natively?
Will parse be able to work with objects with non-Parse generated IDs?
An example:
_id: "LvIzxv5spL" // created by Parse Server
_id: "507f1f77bcf86cd799439011" // BSON.ObjectId created by MongoDB directly
Thanks for reading, any help would be greatly appreciated!
Cheers :)
edited for brevity
I found how the Parse Server generates a new Id on creation here.
The comment documentation above states that the below function is getting called to generate a new id for Parse Server.
I still don't know why it has to create an id in its way rather than using Mongo's native one. It shall help to remove Parse Server dependency easily.
Please find the code below in c# that I am using to generate a new id like the parse server. I have not tested it with all aspects but, I think it will pass most if not all test cases of others.
/// <summary>
/// Randoms the string.
/// </summary>
/// <param name="length">The length.</param>
/// <returns></returns>
public static string RandomString(int length)
{
string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789";
StringBuilder res = new();
using (RNGCryptoServiceProvider rng = new())
{
byte[] uintBuffer = new byte[sizeof(uint)];
while (length-- > 0)
{
rng.GetBytes(uintBuffer);
uint num = BitConverter.ToUInt32(uintBuffer, 0);
res.Append(chars[(int)(num % (uint)chars.Length)]);
}
}
return res.ToString();
}
/// <summary>
/// Gets the new object identifier.
/// </summary>
/// <param name="size">The size.</param>
/// <returns></returns>
public static string GetNewObjectId(int size = 10)
{
return RandomString(size);
}
I hope this sample code helps recreate the logic in your preferred language.
I had a similar problem as the user posting at the following location:
Cross-Process Drag and Drop of custom object type in WinForms C#
Luckily, I have figured out how to serialize almost all parts of my custom object except for a SortedList object.
I need this object because it contains some pretty crucial information to my application, and the Xml nesting is pretty messy.
When I comment out the line adding the SortedList in the ISerializable member GetObjectData(), the object makes it across to the new app. When I leave it in, it doesn't, and I can't figure out how to get it serialized.
I have done some looking both here on StackOverflow and on the web, but have found nothing of use.
I am using the following code to check if my object is serializable so it can be drag & dropped to another application:
/// <summary>
/// Determine if object can be fully serializable to binary format.
/// </summary>
/// <param name="obj"></param>
/// <param name="errorMsg">If return value false, contains reason for failure.</param>
/// <returns></returns>
public static bool IsSerializable(object obj, out string errorMsg)
{
errorMsg = "";
using (MemoryStream mem = new MemoryStream())
{
BinaryFormatter bin = new BinaryFormatter();
try
{
bin.Serialize(mem, obj);
return true;
}
catch (Exception ex)
{
errorMsg = string.Format("Object cannot be serialized: {0}", ex.ToString());
return false;
}
}
}
Does anyone have any suggestions that can help me out? I would like to keep my list of XmlNodes intact during the drag/drop if possible, but wouldn't be opposed to doing some additional coding to break it up into serialize-able pieces and reconstructing it on the other side. The important thing is that the end result must contain a SortedList.
If necessary, I can provide the contents of my custom object I am serializing for drag and drop if it will help.
Thanks,
Kyle K.
I finally figured out how to correctly serialize my object. I was using a SortedList of XmlNodes, which I found out the XmlNode object is not serializable. I switched my implementation to contain a SortedList of strings, and now everything works just fine.
Thanks,
Kyle
I am getting started with Entity Framework 4, and I an creating a demo app as a learning exercise. The app is a simple documentation builder, and it uses a SQL CE store. Each documentation project has its own SQL CE data file, and the user opens one of these files to work on a project.
The EDM is very simple. A documentation project is comprised of a list of subjects, each of which has a title, a description, and zero or more notes. So, my entities are Subject, which contains Title and Text properties, and Note, which has Title and Text properties. There is a one-to-many association from Subject to Note.
I am trying to figure out how to open an SQL CE data file. A data file must match the schema of the SQL CE database created by EF4's Create Database Wizard, and I will implement a New File use case elsewhere in the app to implement that requirement. Right now, I am just trying to get an existing data file open in the app.
I have reproduced my existing 'Open File' code below. I have set it up as a static service class called File Services. The code isn't working quite yet, but there is enough to show what I am trying to do. I am trying to hold the ObjectContext open for entity object updates, disposing it when the file is closed.
So, here is my question: Am I on the right track? What do I need to change to make this code work with EF4? Is there an example of how to do this properly?
Thanks for your help.
My existing code:
public static class FileServices
{
#region Private Fields
// Member variables
private static EntityConnection m_EntityConnection;
private static ObjectContext m_ObjectContext;
#endregion
#region Service Methods
/// <summary>
/// Opens an SQL CE database file.
/// </summary>
/// <param name="filePath">The path to the SQL CE file to open.</param>
/// <param name="viewModel">The main window view model.</param>
public static void OpenSqlCeFile(string filePath, MainWindowViewModel viewModel)
{
// Configure an SQL CE connection string
var sqlCeConnectionString = string.Format("Data Source={0}", filePath);
// Configure an EDM connection string
var builder = new EntityConnectionStringBuilder();
builder.Metadata = "res://*/EF4Model.csdl|res://*/EF4Model.ssdl|res://*/EF4Model.msl";
builder.Provider = "System.Data.SqlServerCe";
builder.ProviderConnectionString = sqlCeConnectionString;
var entityConnectionString = builder.ToString();
// Connect to the model
m_EntityConnection = new EntityConnection(entityConnectionString);
m_EntityConnection.Open();
// Create an object context
m_ObjectContext = new Model1Container();
// Get all Subject data
IQueryable<Subject> subjects = from s in Subjects orderby s.Title select s;
// Set view model data property
viewModel.Subjects = new ObservableCollection<Subject>(subjects);
}
/// <summary>
/// Closes an SQL CE database file.
/// </summary>
public static void CloseSqlCeFile()
{
m_EntityConnection.Close();
m_ObjectContext.Dispose();
}
#endregion
}
Here is the answer. I simplified my code and ran it on simpler EDM model, Disney Characters. Model has two entities, Character and Child, with a 1:* association between Character and Child. Children are character's kids--pretty simple stuff. I wrote the demo as a console app to keep it as simple as possible.
Complete code in Program.cs is as follows:
class Program
{
static void Main(string[] args)
{
/* See http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/8a89a728-6c8d-4734-98cb-11b196ba11fd */
// Configure an SQL CE connection string
var filePath = #"D:\Users\dcveeneman\Documents\Visual Studio 2010\Demos\SqlCeEf4Demo\SqlCeEf4Demo\DisneyChars.sdf";
var sqlCeConnectionString = string.Format("Data Source={0}", filePath);
// Create an EDM connection
var builder = new EntityConnectionStringBuilder();
builder.Metadata = "res://*/DisneyChars.csdl|res://*/DisneyChars.ssdl|res://*/DisneyChars.msl";
builder.Provider = "System.Data.SqlServerCe.3.5";
builder.ProviderConnectionString = sqlCeConnectionString;
var edmConnectionString = builder.ToString();
var edmConnection = new EntityConnection(edmConnectionString);
// Build and query an ObjectContext
using (var context = new DisneyCharsContainer(edmConnection))
{
var chars = context.Characters;
foreach(var character in chars)
{
Console.WriteLine("Character name: {0}", character.Name);
foreach(var child in character.Children)
{
Console.WriteLine("Child name: {0}", child.Name);
}
}
Console.ReadLine();
}
}
}
Link at the top of the code is to a forum thread that I used to write the code.
Here is the walkthrough: First, create a database connection. Since I am using SQL CE, I don't have a connection string builder--the connection string is simply a path, so I don't need one. Then I use an EntityConnectionStringBuilder to build an entity connection string, and then I use that to build an EntityConnection. Finally, I pass the connection to the constructor for my ObjectContext. I can then use the ObjectContext to query the EDM.
Finding / opening a SQL Server CE database is, for some weird reason, hard to do. Make sure you can make any kind of connection to the DB at all before trying to get it to work with the EF.
Wondering if anybody out there has any success in using the JDEdwards XMLInterop functionality. I've been using it for a while (with a simple PInvoke, will post code later). I'm looking to see if there's a better and/or more robust way.
Thanks.
As promised, here is the code for integrating with JDEdewards using XML. It's a webservice, but could be used as you see fit.
namespace YourNameSpace
{
/// <summary>
/// This webservice allows you to submit JDE XML CallObject requests via a c# webservice
/// </summary>
[WebService(Namespace = "http://WebSite.com/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class JdeBFService : System.Web.Services.WebService
{
private string _strServerName;
private UInt16 _intServerPort;
private Int16 _intServerTimeout;
public JdeBFService()
{
// Load JDE ServerName, Port, & Connection Timeout from the Web.config file.
_strServerName = ConfigurationManager.AppSettings["JdeServerName"];
_intServerPort = Convert.ToUInt16(ConfigurationManager.AppSettings["JdePort"], CultureInfo.InvariantCulture);
_intServerTimeout = Convert.ToInt16(ConfigurationManager.AppSettings["JdeTimeout"], CultureInfo.InvariantCulture);
}
/// <summary>
/// This webmethod allows you to submit an XML formatted jdeRequest document
/// that will call any Master Business Function referenced in the XML document
/// and return a response.
/// </summary>
/// <param name="Xml"> The jdeRequest XML document </param>
[WebMethod]
public XmlDocument JdeXmlRequest(XmlDocument xmlInput)
{
try
{
string outputXml = string.Empty;
outputXml = NativeMethods.JdeXmlRequest(xmlInput, _strServerName, _intServerPort, _intServerTimeout);
XmlDocument outputXmlDoc = new XmlDocument();
outputXmlDoc.LoadXml(outputXml);
return outputXmlDoc;
}
catch (Exception ex)
{
ErrorReporting.SendEmail(ex);
throw;
}
}
}
/// <summary>
/// This interop class uses pinvoke to call the JDE C++ dll. It only has one static function.
/// </summary>
/// <remarks>
/// This class calls the xmlinterop.dll which can be found in the B9/system/bin32 directory.
/// Copy the dll to the webservice project's /bin directory before running the project.
/// </remarks>
internal static class NativeMethods
{
[DllImport("xmlinterop.dll",
EntryPoint = "_jdeXMLRequest#20",
CharSet = CharSet.Auto,
ExactSpelling = false,
CallingConvention = CallingConvention.StdCall,
SetLastError = true)]
private static extern IntPtr jdeXMLRequest([MarshalAs(UnmanagedType.LPWStr)] StringBuilder server, UInt16 port, Int32 timeout, [MarshalAs(UnmanagedType.LPStr)] StringBuilder buf, Int32 length);
public static string JdeXmlRequest(XmlDocument xmlInput, string strServerName, UInt16 intPort, Int32 intTimeout)
{
StringBuilder sbServerName = new StringBuilder(strServerName);
StringBuilder sbXML = new StringBuilder();
XmlWriter xWriter = XmlWriter.Create(sbXML);
xmlInput.WriteTo(xWriter);
xWriter.Close();
string result = Marshal.PtrToStringAnsi(jdeXMLRequest(sbServerName, intPort, intTimeout, sbXML, sbXML.Length));
return result;
}
}
}
You have to send it messages like the following one:
<jdeRequest type='callmethod' user='USER' pwd='PWD' environment='ENV'>
<callMethod name='GetEffectiveAddress' app='JdeWebRequest' runOnError='no'>
<params>
<param name='mnAddressNumber'>10000</param>
</params>
</callMethod>
</jdeRequest>
To anyone trying to do this, there are some dependencies to xmlinterop.dll.
you'll find these files on the fat client here ->c:\E910\system\bin32
this will create a 'thin client'
PSThread.dll
icudt32.dll
icui18n.dll
icuuc.dll
jdel.dll
jdeunicode.dll
libeay32.dll
msvcp71.dll
ssleay32.dll
ustdio.dll
xmlinterop.dll
I changed our JDE web service to use XML Interop after seeing this code, and we haven't had any stability problems since. Previously we were using the COM Connector, which exhibited regular communication failures (possibly a connection pooling issue?) and was a pain to install and configure correctly.
We did have issues when we attempted to use transactions, but if you're doing simple single business function calls this shouldn't be an problem.
Update: To elaborate on the transaction issues - if you're attempting to keep a transaction alive over multiple calls, AND the JDE application server is handling a modest number of concurrent calls, the xmlinterop calls start returning an 'XML response failed' message and the DB transaction is left open with no way to commit or rollback. It's possible tweaking the number of kernels might solve this, but personally, I'd always try to complete the transaction in a single call.