How to handle error using TemData[ ] in asp.net mvc? - sql-server

I want to handle following errors using TempData:
1) My custom Error defined by me if certain condition is not fulfilled.
2) To display the exact SQL-server error.
Note: I am Using Redirect after the code.

May be you need like this: In the controller
public ActionResult SaveProduct(Product model)
{
var ErrorString = null;
// your custom validations for example,
if(model.Name == null) ErrorString = "Name is empty";
try
{
// your db save operations
}
catch (Exception exception)
{
ErrorString = exception.Message;
}
if(ErrorString != null) TempData["Error"] = ErrorString;
return Redirect("YourAction");
}
And in View:
#{
var error = (string)TempData["Error"];
}
#if (!string.IsNullOrEmpty(error))
{
<p>#Html.Raw(error)</p>
}

Related

Symfony/Monolog: Log into database --> Serialization Error

Background
I want to save symfony / monolog Logs into my database. I followed this guide which is acutally giving a good solution.
Problem
My solution is working in general, but I get errors (Serialization of 'Closure' is not allowed) while trying to save the CONTEXT variable. I have no idea, what is the problem or how to fix it.
Any idea? Many Thanks!
class WebsiteLogger {
[...]
/**
* #ORM\Column(type="string", type="array", nullable=true)
*/
private $context;
/**
* #ORM\Column(type="string", type="array", nullable=true)
*/
private $extra;
[...]
}
class DatabaseHandler extends AbstractProcessingHandler {
private $emi;
public function __construct(EntityManagerInterface $emi) {
$this->emi = $emi;
parent::__construct();
}
protected function write(array $record): void {
try {
// store into database
$logItem = new WebsiteLogger();
$logItem->setChannel($record['channel']);
$logItem->setLevel($record['level']);
$logItem->setLevelName($record['level_name']);
$logItem->setMessage($record['message']);
$logItem->setLoggedAt($record['datetime']);
$logItem->setExtra($record['extra']);
$logItem->setContext(["initialization" => "not replaced yet"]);
$this->emi->persist($logItem);
$this->emi->flush();
} catch ( Exception $exception ) {
return;
}
try {
$logItem->setContext($record['context']);
$this->emi->persist($logItem); // this is causing the issue
$this->emi->flush();
} catch (Exception $exception) {
dump("save CONTEXT - exception");
dump($exception);
}
}
}
My Solution
There is a nice class in symfony called FlattenException. This is helping to serialize the exception and make it storable into the database.
Have fun!
// ...
protected function write(array $record): void {
try {
// store into database
$logItem = new WebsiteLogger();
$logItem->setChannel($record['channel']);
$logItem->setLevel($record['level']);
$logItem->setLevelName($record['level_name']);
$logItem->setMessage($record['message']);
$logItem->setLoggedAt($record['datetime']);
$logItem->setExtra($record['extra']);
// set a default value that shall be replaced but will be kept in case an exception will be thrown.
$logItem->setContext(["initialization" => "not replaced (= exception thrown while trying to save the context)"]);
$this->emi->persist($logItem);
$this->emi->flush();
} catch ( Exception $exception ) {
return;
}
try {
$exception = $record['context']['exception'] ?? null;
if ($exception && $exception instanceof Throwable ) {
$flattenException = FlattenException::createFromThrowable($exception);
$context = $flattenException;
} else {
$context = $record['context'];
}
$logItem->setContext($context);
$this->emi->persist($logItem);
$this->emi->flush();
} catch (Exception $exception) {
// do nothing
}
}
You can unset all closures in an array:
$context = $record['context'];
foreach ($context as $key => $value) {
if ($value instanceof \Closure) {
unset($context[$key]);
}
}
But it not support multi dimensional array. You must determine where the closure come from and prevent it to be in context.

How to commit programmatically via a managed bean?

I have a managed bean that changes an attribute value and calls a popup.
I need also to commit the changes made (without having to click a commit button), I tried some code but it does nothing.
Help me, please.
DCBindingContainer bindings = (DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry();
OperationBinding operationBinding = bindings.getOperationBinding("Commit");
operationBinding.execute();
You can use the following functions to commit the change made to an Iterator programmatically (In an action listener for example) :
public static ViewObjectImpl getViewObjectFromIterator(String nomIterator) {
ViewObjectImpl returnVO = null;
DCBindingContainer dcb = (DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry();
if (dcb != null) {
DCIteratorBinding iter = dcb.findIteratorBinding(nomIterator);
if (iter != null) {
returnVO = (ViewObjectImpl)iter.getViewObject();
}
}
return returnVO;
}
private void commit(String IteratorName) {
ViewObject vo = this.getViewObjectFromIterator(IteratorName);
try {
vo.getApplicationModule().getTransaction().validate();
vo.getApplicationModule().getTransaction().commit();
} catch (ValidationException e) {
String validationErrorMessage = e.getDetailMessage();
//Occur when some committed data is rejected due to validation error.
//log it : log(Level.WARNING, " " + validationErrorMessage);
}
catch (Exception e) {
//Log it and warn something unexpected occured
}
}
//In your action listener simply call the commit function as follow
//You can Find YOUR_ITERATOR_NAME the your PageDef Binding file in the Executables Column
commit("YOUR_ITERATOR_NAME");
See more : https://gist.github.com/CedricL46/04570c1f078583321ad680ee8ba28f72

Save same changes to multiple databases with Entity Framework

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

How to know SSIS run package issue when trying to execute

I try to run SSIS with this piece of code:
public class EjecutaPaquete {
private Microsoft.SqlServer.Dts.Runtime.Package pkgPaquete;
private Application appAplicacion;
public DTSExecResult EjecucionPaquete(string str_Paquete, List < CatVariablesEtl > Vars = null) {
DTSExecResult respuesta;
try {
appAplicacion = new Application();
appAplicacion.PackagePassword = "mypass";
pkgPaquete = appAplicacion.LoadPackage(str_Paquete, null);
foreach(CatVariablesEtl item in Vars) {
pkgPaquete.Variables[item.str_NombreVariable.ToString()].Value = item.str_ValorVariable.ToString();
}
respuesta = pkgPaquete.Execute();
return respuesta;
} catch (Exception ex) {
throw new NotImplementedException();
}
}
It read all variables correctly into foreach, problem is when try to execute package respuesta = pkgPaquete.Execute(); first it return succeeded but when it return "respuesta" it get failure
You can read errors from package.Errors property:
msdn - Package.Errors Property
msdn - DtsErrors.Item Property
Code example:
foreach(DtsError item in package.Errors) {
Console.Writeline(item.description);
}

How to resolve Object Manager has been closed error?

I'll appreciate if someone can point me to a tutorial or best practice on how to close
JDO connection.
I constantly get javax.jdo.JDOUserException: Object Manager has been closed error whenever I include the finally block.
My code is below:
public static List<AgentEntity> findAgentEntityByString(String id) {
List<AgentEntity> agententity = new ArrayList<AgentEntity>();
if (id == null) {
return null;
}
try {
Query q = pm.newQuery("select id from " + AgentEntity.class.getName());
agententity = (List<AgentEntity>) q.execute();
} catch(Exception ex) {
log.warning(ex.getMessage());
}
return agententity;
}
Regards
One possible solution to avoid this lazy loading problem is to use the size() method forcing the PersistenceManager object to load the result list from datastore before being closed.
public static List<AgentEntity> findAgentEntityByString(String id) {
List<AgentEntity> agententity = new ArrayList<AgentEntity>();
if (id == null) {
return null;
}
try {
Query q = pm.newQuery("select id from " + AgentEntity.class.getName());
agententity = (List<AgentEntity>) q.execute();
agententity.size() //Should populate the returned list
return agententity;
} finally {
pm.close();
}
}
Reference here.
Why do you want to close your PersistenceManager here ?
If you want to close the Query you should use either
javax.jdo.Query.closeAll() or javax.jdo.Query.close(Object result).
So you can do either a transient copy of the result and than close the query and its result:
public static List<AgentEntity> findAgentEntityByString(String id) {
if (id == null) {
return null;
}
Query q = null;
try {
q = pm.newQuery("select id from " + AgentEntity.class.getName());
return new ArrayList<AgentEntity>((List<AgentEntity>) q.execute());
} finally {
if(q!= null){
q.closeAll();
}
}
}
or you can close the result later explicitly:
public static List<AgentEntity> findAgentEntityByString(String id) {
if (id == null) {
return null;
}
Query q = pm.newQuery("select id from " + AgentEntity.class.getName());
return (List<AgentEntity>) q.execute();
}
}
....
List agents = X.findAgentEntityByString("Foobar");
....
pm.close(agents);
Try setting the fetch plan just after getting the PM, then setting it to all before you perform your query:
import javax.jdo.FetchPlan;
pm = PMF.get().getPersistenceManager();
FetchPlan fp = pm.getFetchPlan();
fp.setGroup(FetchPlan.ALL);
Actually, what fixed this for me is answered here:
Why do I get "Persistence Manager has been closed" exception
I had an instance reference to the persistence manager, I just made a local instance and all my errors fixed

Resources