I would like to use FileStream with Nhibernate.
The only post I found on stackoverflow is Sql 2008 Filestream with NHibernate
In the meantime, Nhibernate 3.0 (and 3.1) was released.
Does someone know a solution ?
It is not planned to be supported by nhibernate. (WONT FIX)
http://groups.google.com/group/nhusers/browse_thread/thread/e4a8f038f9c40a0d/a63ac4a8ce4f1244?pli=1
I hope it will change in the future ... maybe an extension (the open source magic)
Nhibernate can be used with the FILESTREAM... sort of.
Persist the object using Nhibernate as normal. You can then issue a couple of standard SQL Queries to take care of the filestream. I place the queries as named queries in my mapping file for the object.
public void Save(Photo photo){
//Save photo data
session.Save(photo);
//get path
String path;
Byte[] context;
IQuery qry1 = session.GetNamedQuery(QUERY_SET_BLANK);
qry1.SetInt64("photoId", photo.RID);
int cnt = qry1.ExecuteUpdate();
IQuery qry2 = session.GetNamedQuery(QUERY_GET_PATH);
qry2.SetInt64("photoId", photo.RID);
System.Collections.IList results = qry2.List();
object[] item = (object[]) results[0];
path = (String) item[0];
context = (Byte[])item[1];
if (context == null) throw new QueryException("Possible null transaction");
//save photo
using (SqlFileStream sqlFile = new SqlFileStream(path, context, FileAccess.Write)) {
photo.Image.Save(sqlFile, ImageFormat.Jpeg);
sqlFile.Close();
}
}
public Photo Get(Int64 rid) {
Photo result = session.Get<Photo>(rid);
if (result != null) {
IQuery qry = session.GetNamedQuery(QUERY_GET_PATH);
qry.SetInt64("photoId", result.RID);
System.Collections.IList results = qry.List();
object[] item = (object[])results[0];
var path = (String)item[0];
var context = (Byte[])item[1];
if (context == null) throw new QueryException("Possible null transaction");
using (SqlFileStream sqlFile = new SqlFileStream(path, context, FileAccess.Read))
result.Image = new System.Drawing.Bitmap(System.Drawing.Bitmap.FromStream(sqlFile));
}
return result;
}
Where Photo is an object mapped to a table. The actual column of the FILESTREAM type is not mapped to the object (excluded from mapping), the the named queries take care of the persistence for that column.
The name queries look like:
<sql-query name="PhotoPathContext">
select Photo.PathName() as path, GET_FILESTREAM_TRANSACTION_CONTEXT() as con
from Core.Photos where PhotoRID = :photoId
</sql-query>
<sql-query name="PhotoSetBlankFileStream">
update Core.Photos set Photo = Cast('' as varbinary(max)) where PhotoRID = :photoId
</sql-query>
The thing need to be wrapped in a transaction which is required by the GET_FILESTRAM_TRANSACTION_CONTEXT() method in the query.
Unit tests would look something like:
[Test]
public void TestSavePhoto() {
IList<Model.Photo> photos = repo.GetPhotos();
VegTabUtilityServices.Photo photo = new VegTabUtilityServices.Photo();
VegTabUtil.Model.Photo ph = photos[0];
photo.RowGuid = ph.GetGuid().Value;
photo.Name = ph.Name ?? photo.RowGuid.ToString();
photo.Image = ph.Image;
ISession session = SessionManager.Instance.Session;
PhotoService ps = new PhotoService(session);
using (NHibernate.ITransaction tx = session.BeginTransaction()) {
ps.Save(photo);
tx.Commit();
}
Assert.Greater(photo.RID, 0);
}
[Test]
public void TestPhotoConnection() {
ISession session = SessionManager.Instance.Session;
PhotoService ps = new PhotoService(session);
Photo p;
using (NHibernate.ITransaction tx = session.BeginTransaction()) {
p = ps.Get(6l);
tx.Commit();
}
Assert.NotNull(p);
Assert.NotNull(p.Image);
logger.Debug(String.Format("{0} by {1} pixels", p.Image.Width, p.Image.Height));
}
I have a more complete article on codeproject
Related
My ASP.NET Core MVC project has several reports. To render the reports as PDF, I'm using AspNetCore.Reporting library.
This library works fine for a single report but due to some cache issues it throws an exception while generating another report. The solution I found on the internet was to run report generation as a new process but I don't know how to implement that.
I found the suggestion to use Tmds.ExecFunction to run report generation as a seperate process. But I dont know how to pass parameters to the function.
Here is my code:
string ReportName = "invoiceDine";
DataTable dt = new DataTable();
dt = GetInvoiceItems(invoiceFromDb.Id);
Dictionary<string, string> param = new Dictionary<string, string>();
param.Add("bParam", $"{invoiceFromDb.Id}");
param.Add("gParam", $"{salesOrderFromDb.Guests}");
param.Add("tParam", $"{invoiceFromDb.Table.Table}");
param.Add("dParam", $"{invoiceFromDb.Time}");
param.Add("totalP", $"{invoiceFromDb.SubTotal}");
param.Add("t1", $"{tax1}");
param.Add("t2", $"{tax2}");
param.Add("tA1", $"{tax1Amount}");
param.Add("tA2", $"{tax2Amount}");
param.Add("AT1", $"{totalAmountWithTax1}");
param.Add("AT2", $"{totalAmountWithTax2}");
param.Add("terminalParam", $"{terminalFromDb.Name}");
param.Add("addressParam", $"{t.Address}");
param.Add("serviceParam", "Service Charges of applicable on table of " + $"{personForServiceCharges}" + " & Above");
var result = reportService.GenerateReport(ReportName, param, "dsInvoiceDine", dt);
return File(result,"application/Pdf");
This is my version of the function:
``` public byte[] GenerateReport(string ReportName, Dictionary<string,string> Parameters,string DataSetName,DataTable DataSource )
{
string guID = Guid.NewGuid().ToString().Replace("-", "");
string fileDirPath = Assembly.GetExecutingAssembly().Location.Replace("POS_Website.dll", string.Empty);
string ReportfullPath = Path.Join(fileDirPath, "\\Reports");
string JsonfullPath = Path.Join(fileDirPath,"\\JsonFiles");
string rdlcFilePath = string.Format("{0}\\{1}.rdlc", ReportfullPath, ReportName);
string generatedFilePath = string.Format("{0}\\{1}.pdf", JsonfullPath, guID);
string jsonDataFilePath = string.Format("{0}\\{1}.json", JsonfullPath, guID);
File.WriteAllText(jsonDataFilePath, JsonConvert.SerializeObject(DataSource));
FunctionExecutor.Run((string[] args) =>
{
// 0 : Data file path - jsonDataFilePath
// 1 : Filename - generatedFilePath
// 2 : RDLCPath - rdlcFilePath
ReportResult result;
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
Encoding.GetEncoding("windows-1252");
LocalReport report = new LocalReport(args[2]);
DataTable dt = JsonConvert.DeserializeObject<DataTable>(File.ReadAllText(args[0]));
report.AddDataSource(args[3], dt);
result = report.Execute(RenderType.Pdf, 1,Parameters);
using (var fs = new FileStream(args[1], FileMode.Create, FileAccess.Write))
{
fs.Write(result.MainStream);
}
}, new string[] {jsonDataFilePath, generatedFilePath, rdlcFilePath, DataSetName });
var memory = new MemoryStream();
using (var stream = new FileStream(Path.Combine("", generatedFilePath), FileMode.Open))
{
stream.CopyTo(memory);
}
File.Delete(generatedFilePath);
File.Delete(jsonDataFilePath);
memory.Position = 0;
return memory.ToArray();
}
But it throws exception "Field marshaling is not supported by ExecFunction" on line:
var result = reportService.GenerateReport(ReportName, param, "dsInvoiceDine", dt);
No Need to run report generation as a seperate process. Just Dont Pass extension as 1
in:
var result = localReport.Execute(RenderType.Pdf, 1, param);
The Solution is:
int ext = (int)(DateTime.Now.Ticks >> 10);
var result = localReport.Execute(RenderType.Pdf, ext, param);
I am trying to query on both Release and Iteration so I can fill out a drop down list with these various values. I'm not quite sure how to do this, however. What are the members of the object that come back via the query if we are able to do this? (Name, FormattedID, CreationDate, etc). Do we just create a new request of type "Release" and "Iteration" ?
Thanks!
Here is a code that queries on releases based on a project reference. If this project is not in a default workspace of the user that runs the code we either need to hardcode the workspace reference or get it from the project.
class Program
{
static void Main(string[] args)
{
RallyRestApi restApi;
restApi = new RallyRestApi("user#co.com", "TopSecret1984", "https://rally1.rallydev.com", "1.40");
var projectRef = "/project/22222222"; //use your project OID
DynamicJsonObject itemWorkspace = restApi.GetByReference(projectRef, "Workspace");
var workspaceRef = itemWorkspace["Workspace"]["_ref"];
Dictionary<string, string> result = new Dictionary<string, string>();
try
{
Request request = new Request("Release");
request.ProjectScopeDown = false;
request.ProjectScopeUp = false;
request.Workspace = workspaceRef;
request.Fetch = new List<string>()
{
"Name"
};
// request.Query = new Query("Project.ObjectID", Query.Operator.Equals, "22222222"); //also works
request.Query = new Query("Project", Query.Operator.Equals, projectRef);
QueryResult queryResult = restApi.Query(request);
foreach (var r in queryResult.Results)
{
Console.WriteLine("Name: " + r["Name"]);
}
}
catch
{
Console.WriteLine("problem!");
}
}
}
}
I know how to log the SQL to log4net/NLog/trace window at runtime with the show_sql configuration option.
What I'm looking for is a way to give a Query<T>() to NHibernate retrieve the generated SQL.
I've looked through the Persister class, the Drivers, different Interceptors and Events. There are so many places to look, even narrowing down my search would be of great help.
You can get the generated sql queries without execution with the following methods:
For the NHibernate.Linq queries:
public String GetGeneratedSql(System.Linq.IQueryable queryable, ISession session)
{
var sessionImp = (ISessionImplementor) session;
var nhLinqExpression = new NhLinqExpression(queryable.Expression, sessionImp.Factory);
var translatorFactory = new ASTQueryTranslatorFactory();
var translators = translatorFactory.CreateQueryTranslators(nhLinqExpression, null, false, sessionImp.EnabledFilters, sessionImp.Factory);
return translators[0].SQLString;
}
For Criteria queries:
public String GetGeneratedSql(ICriteria criteria)
{
var criteriaImpl = (CriteriaImpl) criteria;
var sessionImpl = (SessionImpl) criteriaImpl.Session;
var factory = (SessionFactoryImpl) sessionImpl.SessionFactory;
var implementors = factory.GetImplementors(criteriaImpl.EntityOrClassName);
var loader = new CriteriaLoader((IOuterJoinLoadable) factory.GetEntityPersister(implementors[0]), factory, criteriaImpl, implementors[0], sessionImpl.EnabledFilters);
return loader.SqlString.ToString();
}
For QueryOver queries:
public String GetGeneratedSql(IQueryOver queryOver)
{
return GetGeneratedSql(queryOver.UnderlyingCriteria);
}
For Hql queries:
public String GetGeneratedSql(IQuery query, ISession session)
{
var sessionImp = (ISessionImplementor)session;
var translatorFactory = new ASTQueryTranslatorFactory();
var translators = translatorFactory.CreateQueryTranslators(query.QueryString, null, false, sessionImp.EnabledFilters, sessionImp.Factory);
return translators[0].SQLString;
}
For NHibernate 5.2 in case you want to see actual DbCommand prepared for query (so you can check both SQL in cmd.CommandText and supplied parameters in cmd.Parameters):
//For LINQ
public IEnumerable<DbCommand> GetDbCommands<T>(IQueryable<T> query, ISession s)
{
return GetDbCommands(LinqBatchItem.Create(query), s);
}
//For HQL
public IEnumerable<DbCommand> GetDbCommands(IQuery query, ISession s)
{
return GetDbCommands(new QueryBatchItem<object>(query), s);
}
//For QueryOver
public IEnumerable<DbCommand> GetDbCommands(IQueryOver query, ISession s)
{
return GetDbCommands(query.RootCriteria, s);
}
//For Criteria (needs to be called for root criteria)
public IEnumerable<DbCommand> GetDbCommands(ICriteria rootCriteria, ISession s)
{
return GetDbCommands(new CriteriaBatchItem<object>(query), s);
}
//Adapted from Loader.PrepareQueryCommand
private static IEnumerable<DbCommand> GetDbCommands(IQueryBatchItem item, ISession s)
{
var si = s.GetSessionImplementation();
item.Init(si);
var commands = item.GetCommands();
foreach (var sqlCommand in commands)
{
//If you don't need fully prepared command sqlCommand.Query contains SQL returned by accepted answer
var sqlString = sqlCommand.Query;
sqlCommand.ResetParametersIndexesForTheCommand(0);
var command = si.Batcher.PrepareQueryCommand(System.Data.CommandType.Text, sqlString, sqlCommand.ParameterTypes);
RowSelection selection = sqlCommand.QueryParameters.RowSelection;
if (selection != null && selection.Timeout != RowSelection.NoValue)
{
command.CommandTimeout = selection.Timeout;
}
sqlCommand.Bind(command, si);
IDriver driver = si.Factory.ConnectionProvider.Driver;
driver.RemoveUnusedCommandParameters(command, sqlString);
driver.ExpandQueryParameters(command, sqlString, sqlCommand.ParameterTypes);
yield return command;
}
}
Based on the NHibernate version 3.4 the method for linq expression is:
public String GetGeneratedSql(System.Linq.IQueryable queryable, ISession session)
{
var sessionImp = (ISessionImplementor)session;
var nhLinqExpression = new NhLinqExpression(queryable.Expression,
sessionImp.Factory);
var translatorFactory = new ASTQueryTranslatorFactory();
var translators = translatorFactory.CreateQueryTranslators(nhLinqExpression.Key, nhLinqExpression, null, false,
sessionImp.EnabledFilters, sessionImp.Factory);
var sql = translators.First().SQLString;
var formamttedSql = FormatStyle.Basic.Formatter.Format(sql);
int i = 0;
var map = ExpressionParameterVisitor.Visit(queryable.Expression, sessionImp.Factory).ToArray();
formamttedSql = Regex.Replace(formamttedSql, #"\?", m => map[i++].Key.ToString().Replace('"', '\''));
return formamttedSql;
}
Here is how to get generated Sql from Hql with NH 5.2 (a breaking change in NH 4.0.4 appeared which makes the Hql part of the top voted solution obsolete):
public string HqlToSql(string hql, ISession session)
{
var sessionImp = (ISessionImplementor)session;
var translatorFactory = new ASTQueryTranslatorFactory();
var translators = translatorFactory.CreateQueryTranslators(new NHibernate.Hql.StringQueryExpression(hql),
null, false, sessionImp.EnabledFilters, sessionImp.Factory);
var hqlSqlGenerator = new HqlSqlGenerator(((QueryTranslatorImpl)translators[0]).SqlAST, sessionImp.Factory);
hqlSqlGenerator.Generate();
return hqlSqlGenerator.Sql.ToString();
}
I'm using an in-memory db for some quick unit tests, using the following code:
public class MemoryDb
{
private static Configuration configuration;
private static ISessionFactory sessionFactory;
static MemoryDb()
{
configuration = new NHibernate.Cfg.Configuration();
configuration.DataBaseIntegration(x =>
{
x.Driver<SQLite20Driver>();
x.Dialect<SQLiteDialect>();
x.ConnectionProvider<DriverConnectionProvider>();
x.KeywordsAutoImport = Hbm2DDLKeyWords.AutoQuote;
x.IsolationLevel = IsolationLevel.ReadCommitted;
x.ConnectionString = "Data Source=:memory:;";
x.Timeout = 255;
x.BatchSize = 100;
x.LogFormattedSql = true;
x.LogSqlInConsole = true;
x.AutoCommentSql = false;
});
configuration.AddMapping(DbHelper.GetAutoMappings());
sessionFactory = configuration.BuildSessionFactory();
}
public static ISession GetSession()
{
var session = sessionFactory.OpenSession();
new SchemaExport(configuration).Execute(true, true, false, session.Connection, null);
return session;
}
}
The problem is that the schema export doesn't seem to be working. On of my tests looks like this:
[Fact]
public void ShouldFindDuplicateByEmail()
{
using (var session = MemoryDb.GetSession())
{
var repo = new NHibernateCustomerRepository(session);
var customer = new Customer();
customer.EmailAddress = "test#test.com";
repo.Save(customer);
var duplicates = repo.FindDuplicates(customer);
Assert.Equal(1, duplicates.Length);
}
}
The test fails with the error no such table: Customers. This all worked with Fluent NHibernate and NHibernate 3.1. I know it's not an issue with the mappings themselves, because the actual application works when I run it against an existing SQL Server db. It only fails when running the tests. Any thoughts?
Edit: If I change only the connection string such that it writes to a file (i.e. x.ConnectionString = "data source=" + Path.GetTempFileName();, the whole thing works. I'm guessing either the schema isn't run correctly against the in-memory db, or it's getting a new in-memory db each time I execute a session command, but have no clue how to figure this out.
I found the answer here: https://forum.hibernate.org/viewtopic.php?p=2397541#p2397541
I had to add the following to the db configuration:
x.ConnectionReleaseMode = ConnectionReleaseMode.OnClose;
Otherwise, NHibernate releases the connection after each statement is flushed, thereby getting rid of the in-memory database with the schema.
It seems that altough I have been using NHibernate for a while now, I still misunderstand some basic concepts of this ORM. Let's say I have a class called "Blog" and I load a persisted instance like so:
using (var tx = Session.BeginTransaction())
{
var myBlog = Session.Get(10);
tx.Commit();
}
If I now change a property of this instance, NHibernate seems to automatically detect the unsaved changes and will produce an UPDATE on transaction-commit.
This causes, that the following statements do exactly the same:
using (var tx = Session.BeginTransaction())
{
var myBlog = Session.Get(10);
myBlog.Title = "Changed title";
tx.Commit();
}
using (var tx = Session.BeginTransaction())
{
var myBlog = Session.Get(10);
myBlog.Title = "Changed title";
Session.Update(myBlog); // why is this necessary?
tx.Commit();
}
I don't see any difference with NHProf. So why does the explicit Update-method exists and when should I use it?
Entities are not always connected with session. For example you could have webservice with method, that accepts some entity, and updates in db:
[WebMethod]
void UpdatePerson(int id, string name){
using (var tx = Session.BeginTransaction(){
var person = new Person(id, name);
Session.Update(person);
tx.Commit();
}
}
This code executes update in database without issuing select.