Hopefully someone can advise on how to resolve the following issue, I am using the Blazor Boilerplate template to create a site. I added a second data connection (different database) and generated all the code required, I also added a new Controller and Persistence Manager for the new data connection.
I then wrote code to return a list of clients from the second data connection, this is where I have fallen foul of something as I am getting the following error message from ApiClient class:
Newtonsoft.Json.JsonReaderException: Unexpected character encountered while parsing value: <. Path '', line 0, position 0.
at Newtonsoft.Json.JsonTextReader.ParseValue()
at Newtonsoft.Json.Linq.JToken.ReadFrom(JsonReader reader, JsonLoadSettings settings)
at Newtonsoft.Json.Linq.JToken.Parse(String json, JsonLoadSettings settings)
at Breeze.Sharp.EntityManager.ExecuteQuery(EntityQuery query, CancellationToken cancellationToken)
at Breeze.Sharp.EntityManager.ExecuteQuery[T](EntityQuery1 query, CancellationToken cancellationToken) at RevIntra.Shared.Services.BaseApiClient.GetItems[T](String from, Expression1 predicate, Expression1 orderBy, Expression1 orderByDescending, Nullable1 take, Nullable1 skip, Dictionary`2 parameters) in C:\Shared\Source\Repos\Revroof\RevIntra_BP\RevIntra\Shared\RevIntra.Shared\Services\BaseApiClient.cs:line 169
at RevIntra.Shared.Services.ApiClient.GetAllCustomers(CustomerFilter filter) in C:\Shared\Source\Repos\Revroof\RevIntra_BP\RevIntra\Shared\RevIntra.Shared\Services\ApiClient.cs:line 63
at RevIntra.Theme.Material.Pages.Revroof.AXCustomers.OnInitializedAsync() in C:\Shared\Source\Repos\Revroof\RevIntra_BP\RevIntra\Shared\Modules\RevIntra.Theme.MudBlazor\Pages\Revroof\AXCustomers.razor:line 22
at Microsoft.AspNetCore.Components.ComponentBase.RunInitAndSetParametersAsync()
at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle, ComponentState owningComponentState)
The following is my calling and processing code:
Page:
#code {
private CustomerFilter filter = new();
//private List<Customer> Customers = new();
protected override async Task OnInitializedAsync()
{
var items = await apiClient.GetAllCustomers(filter);
}
}
ApiClient Code:
public async Task<QueryResult<Customer>> GetAllCustomers(CustomerFilter filter)
{
return await GetItems<Customer>(from: "Customers", orderBy: i=>i.TradingName, parameters: filter?.ToDictionary());
}
BaseApiClient code:
public async Task<QueryResult<T>> GetItems<T>(string from,
Expression<Func<T, bool>> predicate = null,
Expression<Func<T, object>> orderBy = null,
Expression<Func<T, object>> orderByDescending = null,
int? take = null,
int? skip = null,
Dictionary<string, object> parameters = null)
{
try
{
var query = new EntityQuery<T>().InlineCount().From(from);
if (parameters != null)
query = query.WithParameters(parameters);
if (predicate != null)
query = query.Where(predicate);
if (orderBy != null)
query = query.OrderBy(orderBy);
if (orderByDescending != null)
query = query.OrderByDescending(orderByDescending);
if (take != null)
query = query.Take(take.Value);
if (skip != null)
query = query.Skip(skip.Value);
else
query = query.Skip(0); //query errors if skip is not defined so default to 0
var response = await entityManager.ExecuteQuery(query, CancellationToken.None); **<----- Error produced here**
QueryResult<T> result;
if (response is QueryResult<T>)
result = (QueryResult<T>)response;
else
{
result = new QueryResult<T>();
result.Results = response;
}
return result;
}
catch (Exception ex)
{
logger.LogError("GetItems: {0}", ex.GetBaseException().Message);
throw;
}
}
Any help would be greatly appreciated as I am currently stuck and cannot progress this project any further.
Regards
Peter.
I have resolved this issue with the help of Keith #enkodellc, I needed to create another Api Client for this data connection, have since run into other issues but this is now resolved.
Related
I'm trying to do a simple thing: to get the MAX of a column. Is quite simple, you know, just run SELECT MAX(column) FROM table;
The problem is when I try to do this in my .NET Core 2.1 project, using Entity Framework.
I have a function that should return the next value of a column.
private int getColumNextValue(string table, string column)
{
string query = $"SELECT MAX({column}) + 1 FROM {table};";
return base.context.Database.ExecuteSqlCommand(query);
}
The query is being generated correclty:
However, it's returning -1 instead of the real value.
But when I run the exact same query in Sql Server Management Studio, the result is correct:
What's going on?
For ExecuteSqlCommand, it only return the number of rows affected. It would not run the query and return the result.
For a workaround, you could try like:
public class ApplicationDbContext : IdentityDbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<User> User { get; set; }
public async Task<T> ExecuteScalarAsync<T>(string rawSql, params object[] parameters)
{
var conn = Database.GetDbConnection();
using (var command = conn.CreateCommand())
{
command.CommandText = rawSql;
if (parameters != null)
foreach (var p in parameters)
command.Parameters.Add(p);
await conn.OpenAsync();
return (T)await command.ExecuteScalarAsync();
}
}
}
And use ExecuteScalarAsync like
public async Task<IActionResult> Index()
{
string query = $"SELECT MAX(SEQ) + 1 FROM [User];";
var result = await _context.ExecuteScalarAsync<int>(query);
return View();
}
I used QueryFirstOrDefault<int> to solve my problem.
I wrote this helper method for reusable purpose:
private int GetColumNextValue(string table, string column)
{
using (SqlConnection conn = new SqlConnection(this.configuration.GetConnectionString("MyConnection")))
{
string query = $"SELECT MAX({column}) + 1 FROM {table};";
return conn.QueryFirstOrDefault<int>(query);
}
}
I hope it can help other people.
I have 3 Oracle databases; production, test, development. For the most part, they are all identical. In my application, I would like the changes to be applied to multiple databases. For example:
using (var context = new Context())
{
context.People.Add(new Person { name = "sean" });
context.SaveChanges();
}
I then tried to override the SaveChanges method and save to multiple databases by doing this:
public void SaveChanges(int auditPersonNumber)
{
OracleCredentials.Default.Server = "VDev";
base.SaveChanges();
OracleCredentials.Default.Server = "VTest";
base.SaveChanges();
OracleCredentials.Default.Server = "VProd";
base.SaveChanges();
}
This didn't work but should explain what I am trying to achieve.
I haven't yet used EntityFramework against an Oracle database, but it should be similar to connecting against SQL Server in that the database name is specified via a ConnectionString. Your project should have a config file (web.config, app.config, or if it's a .NET Core application it could be in appsettings.json) with that ConnectionString in it.
For example:
<add name="YourConnectionString" providerName="YourOracleProviderName" connectionString="User Id=test;Password=testpassword;Data Source=eftest" />
The DbContext base constructor accepts a string argument that specifies which ConnectionString it should use, and thus which database to connect to. If you look into your context class, the default constructor should call the base constructor with that argument.
public YourDbContext() : base("YourConnectionString") {}
In order to save to multiple databases you will need to work against different instances of DbContext each with a different ConnectionString argument. So, your config will need to list a few different connection strings for every Db and you'll probably want your DbContext class to allow the argument in its constructor as well.
Perhaps the SaveChanges method implementation could instantiate the other DbContexts you'd need to use:
public void SaveChanges(int auditPersonNumber)
{
using (var context = new Context("OtherConnectionString1"))
{
// apply same changes
context.SaveChanges();
}
using (var context = new Context("OtherConnectionString2"))
{
// apply same changes
context.SaveChanges();
}
base.SaveChanges();
}
As for the applying the same changes, I would expect you can read them out from the DbContext ChangeTracker. There's an explanation about that using EF Core here but in earlier versions it's similar: http://www.entityframeworktutorial.net/efcore/changetracker-in-ef-core.aspx
Also keep in mind that the SaveChanges call to OtherConnectionString1 could succeed while others could fail, so the data might be inconsistent in your different databases. You may have to look into using transactions across multiple databases but I haven't done this yet myself.
I was able to figure out a solution thanks to the help of Sangman.
public class Context : Shared.Data.Context
{
new public void SaveChanges(int auditPersonNumber)
{
var errors = string.Empty;
var testConnectionString = "ConnectionString";
var developmentConnectionString = "ConnectionString";
//Save to test database
if (SecurityMaintenanceUser.ApplyToTest)
errors = ApplyToDatabase(testConnectionString, auditPersonNumber, "Test");
if (!string.IsNullOrWhiteSpace(errors))
errors += "\n\n";
//Save to development database
if (SecurityMaintenanceUser.ApplyToDevelopment)
errors += ApplyToDatabase(developmentConnectionString, auditPersonNumber, "Development");
if (!string.IsNullOrWhiteSpace(errors))
MessageBox.Show(errors, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
//Save to production database
base.SaveChanges(auditPersonNumber);
}
private string ApplyToDatabase(string connectionString, int auditPersonNumber, string server)
{
try
{
using (var context = new Context(connectionString))
{
context.Configuration.ValidateOnSaveEnabled = false;
foreach (var entry in ChangeTracker.Entries())
{
var dataSet = context.Set(entry.Entity.GetType());
if (entry.State == EntityState.Added)
{
dataSet.Add(entry.Entity);
}
else if (entry.State == EntityState.Deleted)
{
var contextEntity = dataSet.Find(GetPrimaryKeyValues(entry));
context.DeleteEntity(contextEntity, auditPersonNumber);
}
else if (entry.State == EntityState.Modified)
{
var contextEntity = dataSet.Find(GetPrimaryKeyValues(entry));
context.Entry(CopyProperties(entry.Entity, contextEntity)).State = EntityState.Modified;
}
}
context.SaveChanges(auditPersonNumber);
return string.Empty;
}
}
catch (Exception e)
{
return $"Failed to apply database changes to {server}.\n{e.GetFullMessage()}";
}
}
private object CopyProperties(object source, object destination)
{
if (source == null || destination == null)
throw new Exception("Source or/and Destination Objects are null");
var typeDest = destination.GetType();
var typeSrc = source.GetType();
foreach (var srcProp in typeSrc.GetProperties())
{
if (srcProp.Name == "Type" || srcProp.Name == "AuthenticationLog")
continue;
//This blocks any complex objects attached to the entity, will need to be changed for your application
if (srcProp.PropertyType.FullName.Contains("Library.Shared"))
continue;
if (!srcProp.CanRead)
continue;
var targetProperty = typeDest.GetProperty(srcProp.Name);
if (targetProperty == null)
continue;
if (!targetProperty.CanWrite)
continue;
if (targetProperty.GetSetMethod(true)?.IsPrivate == true)
continue;
if ((targetProperty.GetSetMethod().Attributes & MethodAttributes.Static) != 0)
continue;
if (!targetProperty.PropertyType.IsAssignableFrom(srcProp.PropertyType))
continue;
targetProperty.SetValue(destination, srcProp.GetValue(source, null), null);
}
return destination;
}
private object GetPrimaryKeyValues(DbEntityEntry entry)
{
var objectStateEntry = ((IObjectContextAdapter)this).ObjectContext.ObjectStateManager.GetObjectStateEntry(entry.Entity);
return objectStateEntry.EntityKey.EntityKeyValues[0].Value;
}
public static string GetFullMessage(this Exception ex)
{
return ex.InnerException == null ? ex.Message : $"{ex.Message}\n{ex.InnerException.GetFullMessage()}";
}
public static string Replace(this string source, string oldString, string newString, StringComparison comp)
{
int index = source.IndexOf(oldString, comp);
if (index >= 0)
{
source = source.Remove(index, oldString.Length);
source = source.Insert(index, newString);
}
if (source.IndexOf(oldString, comp) != -1)
source = Replace(source, oldString, newString, comp);
return source;
}
}
I'm trying to make a UnitOfWork/Repository pattern using fastcrud.
I have created a generic repository
public interface IRepository<T> where T : BaseEntity
{
IDbTransaction Transaction { get; set; }
T Get(T entityKeys, Action<ISelectSqlSqlStatementOptionsBuilder<T>> statementOptions = null);
IEnumerable<T> Find(Action<IRangedBatchSelectSqlSqlStatementOptionsOptionsBuilder<T>> statementOptions = null);
int Count(Action<IConditionalSqlStatementOptionsBuilder<T>> statementOptions = null);
bool Delete(T entityToDelete, Action<IStandardSqlStatementOptionsBuilder<T>> statementOptions = null);
}
From the service I call
var repo = UnitOfWork.GetRepository<MyTable>();
var myList = repo.Find(statement => statement
.AttachToTransaction(repo.Transaction)
.OrderBy($"{nameof(MyTable.Name):C}")
);
This works. But I don't want the service to handle the AttachToTransaction call, instead i would like to add it to my repository
public IEnumerable<T> Find(Action<IRangedBatchSelectSqlSqlStatementOptionsOptionsBuilder<T>> statementOptions = null)
{
return Connection.Find<T>(statementOptions);
}
But here the statementOption is a delegated Action, and I can't do
statementOption.AttachToTransaction(this.Transaction)
My UnitOfWork always creates an transaction, so if I skip attaching to transaction it I will get an exception
An unhandled exception occurred while processing the request.
InvalidOperationException: ExecuteReader requires the command to have a transaction when the connection assigned to the command is in a pending local transaction. The Transaction property of the command has not been initialized.
You can do it like this:
public IEnumerable<T> Find(Action<IRangedBatchSelectSqlSqlStatementOptionsOptionsBuilder<T>> statementOptions = null)
{
statementOptions += s => s.AttachToTransaction(this.Transaction);
return Connection.Find<T>(statementOptions);
}
I had the same issue too. I have used this extension method resolved it:
internal static IRangedBatchSelectSqlSqlStatementOptionsOptionsBuilder<TEntity> AttachToTransaction<TEntity>(
this IRangedBatchSelectSqlSqlStatementOptionsOptionsBuilder<TEntity> statement,
Action<IRangedBatchSelectSqlSqlStatementOptionsOptionsBuilder<TEntity>> originalStatementOptionsBuilder,
IDbTransaction transaction)
{
if (originalStatementOptionsBuilder == null)
{
statement.AttachToTransaction(transaction);
}
else
{
originalStatementOptionsBuilder(statement);
statement.AttachToTransaction(transaction);
}
return statement;
}
Now your service must change like this:
public IEnumerable<T> Find(Action<IRangedBatchSelectSqlSqlStatementOptionsOptionsBuilder<T>> statementOptions = null)
{
return Connection.Find<T>(s => s.AttachToTransaction(statementOptions, this.Transaction));
}
I am using Dapper with the SQLinq Nuget package.
Here is some sample code I am using.
Dapper with SQLinq runs the query before I run a .ToList() (for example).
I know with Dapper you can specify "buffer" to make it run deferred, but I do not see how to apply that with the SQLinq NuGet package for Dapper.
using (var sqlCnn = base.GetConnection())
{
var viewData = sqlCnn.Query(from s in new SQLinq<Week_Returns_stats_V>(). . .
public SqlConnection GetConnection(bool mars = false)
{
if (_sqlCnn != null)
{
if (_sqlCnn.State != ConnectionState.Open) CloseConnection();
}
if (mars)
{
var scsb = new SqlConnectionStringBuilder(_cnnString)
{
MultipleActiveResultSets = true
};
}
_sqlCnn = new SqlConnection(_cnnString);
return _sqlCnn;
}
I found how to include buffered after the query.
public static IEnumerable<T> Query<T>
(this IDbConnection dbconnection,
SQLinq<T> query,
IDbTransaction transaction = null,
bool buffered = true,
int? commandTimeout = default(int?),
CommandType? commandType =
default(CommandType?)) where T : new();
var viewData = sqlCnn.Query(from s in new SQLinq<Returns_stats()
select new
{
AVGPercentReturnX100 = s.AVGPercentReturnX100,
PercentProfitableX100 = s.PercentProfitableX100
}, buffered:true).AsEnumerable()
I am brand new to both REST and RESTlet- I got everything up and communicating last night but what I found this morning is that everything I pass into the server is always becoming null.
just as a sample app i have the following - a User Objectify entity (id, emailAddress, and version), and a RESTUserProxy object (id, emailAddress) - I wasn't originally sure if i could pass Objectify Entities back and after not being able to see anything switched it to the Proxy object - if i can get it to work this way I will try switching it back
the front end is as follows:
public interface RESTUserResourceProxy extends ClientProxy {
#Get
public void find(String emailAddress, Result<RESTUserProxy> callback);
#Put
public void persist(RESTUserProxy user, Result<Void> callback);
#Delete
public void delete(RESTUserProxy user, Result<Void> callback);
}
the backend code is as follows (this is currently extremely ugly - i got a little frustrated just trying to see something and put in a ton of sysouts)
public class RESTUserServerResource extends ServerResource implements RESTUserResource {
private final UserDao userDao;
public RESTUserServerResource() {
System.out.println("CREATED USER RESOURCE IMPL");
userDao = new UserDao();
}
#Override
#Get
public RESTUserProxy find() {
System.out.println("reference = " + getReference());
Form queryParams = getReference().getQueryAsForm();
System.out.println("query params = " + queryParams);
System.out.println("query = " + getQuery());
System.out.println("query string = " + getQuery().getQueryString());
String searchQuery = (String) getRequest().getAttributes().get("searchQuery");
System.out.println("search query = " + searchQuery) ;
return null;
// if (emailAddress == null) {
// return null;
// }
// System.out.println("user resource impl find [" + emailAddress + "]");
// final User user = userDao.find(emailAddress.getText());
// if (user != null) {
// System.out.println("found user ");
// return new RESTUserProxy(user.getId(), user.getEmailAddress());
// } else {
// System.out.println("found absolutely nothing");
// return null;
// }
}
#Override
#Put
public void persist(RESTUserProxy userProxy) {
System.out.println("user proxy = " + userProxy);
if (userProxy == null) {
return;
}
final User user = userDao.find(userProxy.getId());
user.setEmailAddress(userProxy.getEmailAddress());
user.setId(userProxy.getId());
userDao.persist(user);
}
#Override
#Delete
public void delete(RESTUserProxy userProxy) {
final User user = userDao.find(userProxy.getId());
userDao.delete(user);
}
}
what im having problems with is that eerythings coming through as null - a lot of other answers on here said to get the query to get the params - but here the query is null
below is the output of calling find and persist
reference = http://127.0.0.1:8888/users/123
query params = []
query = []
query string =
search query = null
i'm sure i'm doing something stupid here i just have no idea how to proceed right now. Any help as to what i'm doing wrong would be greatly appreciated.
This is due to GAE not supporting chunked encoding. See workaround here:
http://wiki.restlet.org/docs_2.1/13-restlet/21-restlet/318-restlet/303-restlet.html#dsy303-restlet_gwt