NHibernate Criterion Problem - nhibernate

I have a query in hibernate that looks like:
string[] NewsId = new string[] {"ABC","DEF","GHI"};
public IEnumerable<News> NewsSelected(string[] NewsId)
{
List<ICriterion> criteria = new List<ICriterion>();
criteria.Add(Restrictions.In("Id", NewsId));
var results = _repository.GetByCriteria(criteria);
return results;
}
It returns zero, but
public IEnumerable<News> NewsSelected(string[] NewsId)
{
List<ICriterion> criteria = new List<ICriterion>();
criteria.Add(Restrictions.In("Id", new[]{"ABC","DEF","GHI"}));
var results = _repository.GetByCriteria(criteria);
return results;
}
works perfectly. What am I missing here?

You need copy query parameters to a local variable like that:
string[] NewsId = new string[] {"ABC","DEF","GHI"};
public IEnumerable<News> NewsSelected(string[] NewsId)
{
var newsId = NewsId;
List<ICriterion> criteria = new List<ICriterion>();
criteria.Add(Restrictions.In("Id", newsId));
var results = _repository.GetByCriteria(criteria);
return results;
}

Related

Is there a way to improve the code? .Net Core 6.0 and Cosmos Database with SQL API

It's my first time using .Net Core 6.0 WebAPI and Cosmos Database with SQL API, so I'm concerned that I've done things correctly.
I have used /address/zipcode as a partition key in the below sample
public class FamilyService : IFamilyService
{
public FamilyService(CosmosClient dbClient, string databaseName, string containerName)
{
this._container = dbClient.GetContainer(databaseName, containerName);
}
public Task<Family> GetFamilyDataAsync(string id, string zipcode)
{
return Task.FromResult(_container.GetItemLinqQueryable<Family>(allowSynchronousQueryExecution: true)
.Where(p => p.Id == id && p.Address.ZipCode == zipcode)
.ToList().First());
}
public async Task AddFamilyDataAsync(Family family)
{
await this._container.CreateItemAsync<Family>(family, new PartitionKey(family.Address.ZipCode));
}
public async Task UpdateFamilyDataAsync(Family family)
{
await this._container.ReplaceItemAsync<Family>(family, family.Id, new PartitionKey(family.Address.ZipCode));
}
public async Task DeleteFamilyDataAsync(string id, string zipcode)
{
await this._container.DeleteItemAsync<Family>(id, new PartitionKey(zipcode));
}
private async Task<bool> GetFamilyDataFromId(string id, string zipcode)
{
string query = $"select * from c where c.id=#familyId";
QueryDefinition queryDefinition = new QueryDefinition(query).WithParameter("#familyId", id);
List<Family> familyResults = new List<Family>();
FeedIterator streamResultSet = _container.GetItemQueryStreamIterator(
queryDefinition,
requestOptions: new QueryRequestOptions()
{
PartitionKey = new PartitionKey(zipcode),
MaxItemCount = 10,
MaxConcurrency = 1
});
while (streamResultSet.HasMoreResults)
{
using (ResponseMessage responseMessage = await streamResultSet.ReadNextAsync())
{
if (responseMessage.IsSuccessStatusCode)
{
dynamic streamResponse = FromStream<dynamic>(responseMessage.Content);
List<Family> familyResult = streamResponse.Documents.ToObject<List<Family>>();
familyResults.AddRange(familyResult);
}
else
{
return false;
}
}
}
if (familyResults != null && familyResults.Count > 0)
{
return true;
}
return familyResults;
}
Especially I want to focus more on those methods that retrieves the data from the database.
Update#1 : I have updated the code as suggested by #Mark Brown.
public class FamilyService : IFamilyService
{
private Container _container;
private static readonly JsonSerializer Serializer = new JsonSerializer();
public FamilyService(CosmosClient dbClient, string databaseName, string containerName)
{
this._container = dbClient.GetContainer(databaseName, containerName);
}
public async Task<Family> GetFamilyDataAsync(string id, string zipCode)
{
return await _container.ReadItemAsync<Family>(id, new PartitionKey(zipCode));
}
public async Task<List<Family>> GetAllFamilyDataAsync(string zipCode)
{
string query = $"SELECT * FROM Family";
QueryDefinition queryDefinition = new QueryDefinition(query);
List<Family> familyResults = new List<Family>();
FeedIterator<Family> feed = _container.GetItemQueryIterator<Family>(
queryDefinition,
requestOptions: new QueryRequestOptions()
{
PartitionKey = new PartitionKey(zipCode),
MaxItemCount = 10,
MaxConcurrency = 1
});
while (feed.HasMoreResults)
{
FeedResponse<Family> response = await feed.ReadNextAsync();
foreach (Family item in response)
{
familyResults.Add(item);
}
}
return familyResults;
}
public async Task<List<Family>> GetAllFamilyDataByLastNameAsync(string lastName, string zipCode)
{
string query = $"SELECT * FROM Family where Family.lastName = #lastName";
QueryDefinition queryDefinition = new QueryDefinition(query).WithParameter("#lastName", lastName);
List<Family> familyResults = new List<Family>();
FeedIterator<Family> feed = _container.GetItemQueryIterator<Family>(
queryDefinition,
requestOptions: new QueryRequestOptions()
{
PartitionKey = new PartitionKey(zipCode),
MaxItemCount = 10,
MaxConcurrency = 1
});
while (feed.HasMoreResults)
{
FeedResponse<Family> response = await feed.ReadNextAsync();
foreach (Family item in response)
{
familyResults.Add(item);
}
}
return familyResults;
}
public async Task<List<Family>> GetAllFamilyDataPaginatedAsync(int pageNumber, string zipCode)
{
int pageSize = 2;
int itemsToSkip = (pageSize * pageNumber) - pageSize;
QueryRequestOptions options = new QueryRequestOptions { MaxItemCount = pageSize };
string continuationToken = null;
List<Family> familyResults = new List<Family>();
IQueryable<Family> query = this._container
.GetItemLinqQueryable<Family>(false, continuationToken, options)
.Where(i => i.Address.ZipCode == zipCode)
.Skip(itemsToSkip);
using (FeedIterator<Family> iterator = query.ToFeedIterator<Family>())
{
FeedResponse<Family> feedResponse = await iterator.ReadNextAsync();
foreach (Family item in feedResponse)
{
familyResults.Add(item);
}
}
return familyResults;
}
public async Task AddFamilyDataAsync(Family family)
{
await this._container.CreateItemAsync<Family>(family, new PartitionKey(family.Address.ZipCode));
}
public async Task UpdateFamilyDataAsync(Family family)
{
await this._container.ReplaceItemAsync<Family>(family, family.Id, new PartitionKey(family.Address.ZipCode));
}
public async Task DeleteFamilyDataAsync(string id, string zipCode)
{
await this._container.DeleteItemAsync<Family>(id, new PartitionKey(zipCode));
}
}
If you are searching for something with the partition key and id then that can only return a single item as id must be unique within a pk. So GetFamilyDataAsync() can be implemented using ReadItemAsync() rather than a query. PS: Not sure why you would ever do this, allowSynchronousQueryExecution: true
GetFamilyDataFromId() also can be implemented using ReadItemAsync(). Even if this did require a query, it doesn't make sense to implement to return a stream, then deserialize into an object. Just use the variant that can deserialize directly into a POCO.
Can't say if this is completely optimal for everything, but you can go here to find more things as a place to start, https://learn.microsoft.com/en-us/azure/cosmos-db/sql/best-practice-dotnet
The other thing to call out is zip code as a partition key. This might work at small scale, but it could run into issues at larger scale depending on how many families and how many records for each family are stored within a single partition. Max size for a single partition key value is 20 GB. Fine for small workloads but possibly an issue at larger scale.

Sending emaills with template MVC using Razor

I want to send some mails from my site.
I've created a template: OrderPlacedEmail.cshtml
#model OnlineCarStore.Models.PurchaseVM
<h1>Order Placed Email Notification</h1>
<p>#Model.Comments</p>
Dear #Model.Name,
<h2>Thank you.</h2>
<p>
You’ve made a purchase on #Model.Comments
</p>....and so on...
I've created a view model, and I use it like this:
var template = Server.MapPath("~/Templates/OrderPlaced.cshtml");
var viewModel = new PurchaseVM
{
GuId = new Guid(guidValue),
Name = name,
Address = address,
Phone = phone,
Email = email,
Comments = comments,
Date = DateTime.Now,
CartList = cartList
};
var body = Razor.Parse(template, viewModel);
As I understood, the Razor.Parse method, should replace all the details from my template with the values from view model. But, the body gets the value of the location of the template, as you can see below:
Can you please advise what I'm doing wrong.
If you wish there is a helper that i use
public static class HtmlOutputHelper
{
public static string RenderViewToString(ControllerContext context,
string viewPath,
object model = null,
bool partial = false)
{
// first find the ViewEngine for this view
ViewEngineResult viewEngineResult = null;
if (partial)
viewEngineResult = ViewEngines.Engines.FindPartialView(context, viewPath);
else
viewEngineResult = ViewEngines.Engines.FindView(context, viewPath, null);
if (viewEngineResult == null)
throw new FileNotFoundException("View cannot be found.");
// get the view and attach the model to view data
var view = viewEngineResult.View;
context.Controller.ViewData.Model = model;
string result = null;
using (var sw = new StringWriter())
{
var ctx = new ViewContext(context, view,
context.Controller.ViewData,
context.Controller.TempData,
sw);
view.Render(ctx, sw);
result = sw.ToString();
}
return result;
}
}
On your controller
var viewModel = new PurchaseVM
{
GuId = new Guid(guidValue),
Name = name,
Address = address,
Phone = phone,
Email = email,
Comments = comments,
Date = DateTime.Now,
CartList = cartList
};
var emailTemplate = "~/Views/Templates/OrderPlaced.cshtml";
var emailOutput = HtmlOutputHelper.RenderViewToString(ControllerContext, emailTemplate, emailModel, false);
You also can use ActionMailerNext lib from NuGet Gallery for this scenario.
public class EmailController : MailerBase
{
//...
public EmailResult OrderPlaced(Order order)
{
MailAttributes.To.Add(new MailAddress("to#email.com"));
MailAttributes.From = new MailAddress("from#email.com");
return Email("OrderPlaced", new PurchaseVM
{
//...
});
}
//...
}
You can leave your View unchanged.

How to use Scroll while passing raw json Query to ElasticSearch using NEST

var query = #"
{
""query"": {
""match_all"": { }
}
}";
Func<SearchRequestParameters, SearchRequestParameters> requestParameters = a =>
a.SearchType(SearchType.Scan).Scroll(TimeSpan.FromSeconds(60));
var searchResult = await client.LowLevel.SearchAsync<SearchResponse<T>>(indexName, mappingName, query , requestParameters)
if (searchResult.Body.IsValid)
{
var scrollNo = 0;
var results = await client.ScrollAsync<T>("10s", searchResult.Body.ScrollId);
while (results.Documents.Any())
{
documents.AddRange(results.Documents);
scrollNo++;
results = await client.ScrollAsync<T>("10s", results.ScrollId);
return new Customresponse<T>
{
Documents = documents,
total = result.Body.Total
};
}
Would like to pull all data using scroll while passing raw json query. but scroll is not working properly while passing json raw query. Can anyone help on this ?.
Your example is nearly there but not quite; you're missing a closing brace for the while loop to collect all documents before returning the custom response.
Here's an example I just ran on the Stackoverflow data set, to return all questions tagged with nest
private IElasticClient _client;
void Main()
{
var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
var defaultIndex = "default-index";
var connectionSettings = new ConnectionSettings(pool);
_client = new ElasticClient(connectionSettings);
var query = #"
{
""query"": {
""term"": {
""tags"": {
""value"": ""nest""
}
}
}
}";
var result = RunScrollAsync(query).Result.Dump();
}
private async Task<Customresponse<Question>> RunScrollAsync(string query)
{
var scrollTime = "10s";
// omit the .SearchType(Scan) which is deprecated. Not
// specifying means the first response contains the first set
// of documents
var esResponse = await _client.LowLevel.SearchAsync<SearchResponse<Question>>(
"posts",
"question",
query, r => r.Scroll(TimeSpan.FromSeconds(10))).ConfigureAwait(false);
if (esResponse.Body.IsValid && esResponse.Body.Documents.Any())
{
// assume you have less than 2,147,483,647 documents to return?
var documents = new List<Question>((int)esResponse.Body.Total);
documents.AddRange(esResponse.Body.Documents);
var scrollNo = 0;
var response = await _client.ScrollAsync<Question>(scrollTime, esResponse.Body.ScrollId).ConfigureAwait(false);;
// keep scrolling until no more documents are returned
while (response.Documents.Any())
{
documents.AddRange(response.Documents);
scrollNo++;
response = await _client.ScrollAsync<Question>(scrollTime, response.ScrollId).ConfigureAwait(false);;
}
return new Customresponse<Question>
{
Documents = documents,
total = response.Total
};
}
// return an empty result.
// Or throw an exception, or log - whatever you need to do
return new Customresponse<Question>
{
Documents = Enumerable.Empty<Question>(),
total = 0
};
}
public class Customresponse<T>
{
public IEnumerable<T> Documents { get; set; }
public long total { get; set; }
}
This returns all 342 questions, with a total of 342 (Data set is from June 2016).

Am doing addcart functionality in mvc4?If i remove the single item in the cart it is not removing anything?

Am doing addcart functionality in mvc4?If i remove the single item in the cart it is not removing anything?
My controller action is.......
ManageDatabase _db = new ManageDatabase();
List<Product> cartList = new List<Product>();
public ActionResult Deletecart(int id)
{
var prod = _db.product.Find(id);
int CartLen = 0;
cartList.Remove(prod);
cartList = (List<Product>)System.Web.HttpContext.Current.Application["cartList"];
CartLen = cartList.Count;
System.Web.HttpContext.Current.Application["CartLen"] = CartLen;
return RedirectToAction("Details", "Home");
}
As you've requested to be guided by code, please do the following:
ManageDatabase _db = new ManageDatabase();
List<Product> cartList = new List<Product>();
public ActionResult Deletecart(int id) // tell me the value of id
{
// put breakpoint on next line and click DEBUG
var prod = _db.product.Find(id); // tell me the value of prod
int CartLen = 0;
cartList.Remove(prod);
// tell me the value of cartList
cartList = (List<Product>)System.Web.HttpContext.Current.Application["cartList"];
CartLen = cartList.Count;
System.Web.HttpContext.Current.Application["CartLen"] = CartLen;
return RedirectToAction("Details", "Home");
}
Once we know what your variables are, we can tell you what's wrong.

How can I have NHibernate only generate the SQL without executing it?

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