.net mvc 5 C# with crystal report - c#

I use .net mvc 5 c# repository pattern with database first approach, In my Service layer i calculate and apply group by condition on there and pass this data into viewmodel and razor view,
my question is can i used this viewmodel (with data) for creating the crystal report from this viewmodel ? crystal report is installed on visual studio (2015). code information are
code on controller are
public ActionResult Top20SupplierReport()
{
var AllSupplier = _supplier.Top20Supplier();
}
Service layer code are
public List<GroupBySupplierVM> Top20Supplier()
{
var AllSupplier = //code for get all supplier list from database
var groupByData = from sup in AllSupplier
group sup by sup .cf02supplier_Name into g
let TotalVol = g.Sum(x => x.cf08collection_Received_Volume)
let TotalAmount = g.Sum(x => x.cf08collection_Balance)
orderby TotalVol descending
select new GroupBySupplierVM
{
Key = g.Key,
Values = g.ToList(),
TotalReceivedVolume = Convert.ToDouble(TotalVol),
TotalBalance = TotalAmount
};
return groupByData.Take(20).ToList();
}
ViewModel are
public class GroupBySupplierVM
{
public string Key;
public List<SupplierVM> Values;
[Display(Name = "Total")]
public double TotalReceivedVolume { get; set; }
public double? TotalBalance { get; set; }
}
and
public class SupplierVM
{
public int cf02supplier_Id { get; set; }
public string cf02supplier_Address { get; set; }
public string cf02supplier_Name { get; set; }
public string cf02supplier_City_Id { get; set; }
public string cf02supplier_Telephone { get; set; }
public string cf02supplier_MobileNo { get; set; }
public decimal cf02supplier_Balance { get; set; }
......
// other Entity are also there
}
can i create crystal report from the GroupBySupplierVM ? if yes how to use on crystal report and how to show on view page ?
anybody have knowledge about this how to use on crystal report. Please help me...

Yes you can.
-Create a Data Table
Sample DataTable
-and my code
//domResult -> List of your View Model
DataTable dt = ReportHelper.ToDataTable(domResult);
// LCDraft_Domestic--> Crystal Report
LCDraft_Domestic rpt = new LCDraft_Domestic();
//My Data Table
rpt.Database.Tables["DraftData"].SetDataSource(dt);
Use to Convert List to DataTable
-- My ReportHelper --
Create a static class a put this code
public static class ReportHelper
{
public static DataTable ToDataTable<T>(this IList<T> items)
{
var tb = new DataTable(typeof(T).Name);
PropertyInfo[] props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (PropertyInfo prop in props)
{
Type t = GetCoreType(prop.PropertyType);
tb.Columns.Add(prop.Name, t);
}
foreach (T item in items)
{
var values = new object[props.Length];
for (int i = 0; i < props.Length; i++)
{
values[i] = props[i].GetValue(item, null);
}
tb.Rows.Add(values);
}
return tb;
}
/// <summary>
/// Determine of specified type is nullable
/// </summary>
public static bool IsNullable(Type type)
{
return !type.IsValueType || (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>));
}
/// <summary>
/// Return underlying type if type is Nullable otherwise return the type
/// </summary>
public static Type GetCoreType(Type type)
{
if (type != null && IsNullable(type))
{
if (!type.IsValueType)
{
return type;
}
else
{
return Nullable.GetUnderlyingType(type);
}
}
else
{
return type;
}
}
static TableLogOnInfo crTableLogonInfo;
static ConnectionInfo crConnectionInfo;
static Tables crTables;
static Database crDatabase;
public static void ReportLogin(ReportDocument crDoc, string Server, string Database, string UserID, string Password)
{
crConnectionInfo = new ConnectionInfo();
crConnectionInfo.ServerName = Server;
crConnectionInfo.DatabaseName = Database;
crConnectionInfo.UserID = UserID;
crConnectionInfo.Password = Password;
crDatabase = crDoc.Database;
crTables = crDatabase.Tables;
foreach (CrystalDecisions.CrystalReports.Engine.Table crTable in crTables)
{
crTableLogonInfo = crTable.LogOnInfo;
crTableLogonInfo.ConnectionInfo = crConnectionInfo;
crTable.ApplyLogOnInfo(crTableLogonInfo);
}
}
//No Login
public static void ReportLogin(ReportDocument crDoc, string Server, string Database)
{
crConnectionInfo = new ConnectionInfo();
crConnectionInfo.ServerName = Server;
crConnectionInfo.DatabaseName = Database;
crConnectionInfo.IntegratedSecurity = true;
crDatabase = crDoc.Database;
crTables = crDatabase.Tables;
foreach (CrystalDecisions.CrystalReports.Engine.Table crTable in crTables)
{
crTableLogonInfo = crTable.LogOnInfo;
crTableLogonInfo.ConnectionInfo = crConnectionInfo;
crTable.ApplyLogOnInfo(crTableLogonInfo);
}
}}

Crystal reports really only understands DataSets or DataTables. You have to convert your object collection to DataTable or DataSet and then setting it as the report's data source. see .NET - Convert Generic Collection to DataTable and Convert generic List/Enumerable to DataTable? for tips on creating your table.
Once you have your dataset/table, you need to create the xml schema required to design your report. This can be done with the WriteXml method. You only need this while designing the report. Locate this xml file, and create your report.
var table = groupByData.ToDataTable(); // using your extension method
// snippet for creating schema from table
using (var fs = new StreamWriter(xmlFile)) // XML File Path
{
table.WriteXml(fs, XmlWriteMode.WriteSchema);
}
As for Crystal and MVC, not really. Crystal Reports is WebForms technology, so you'll have to have an aspx page to host the report viewer in your MVC application. You can use Microsoft.Aspnet.FriendlyUrl and routes.EnableFriendlyUrls(); to hide the extension.
The actual databinding is quite simple:
var table = groupByData.ToDataTable(); // using your extension method
report.SetDataSource(table);
viewer.ReportSource = report;

Related

C# generic insert method to insert object into Sqlite3 table?

I am trying to make a generic method where I can insert any object into a sqlite3 database.
User class:
public class Users : IClassModel<Users>
{
public int Id { get; set; }
public string UserName { get; set; }
public string UserAddress { get; set; }
public string OtherUserDetails { get; set; }
public decimal AmountOfFine { get; set; }
public string Email { get; set; }
public string PhoneNumber { get; set; }
}
Interface class:
public interface IClassModel<T>
{
public int Id { get; set; }
}
QueryBuilder class:
public class queryBuilder : IDisposable
{
private SqliteConnection _connection;
public queryBuilder(string connectionString)
{
_connection = new SqliteConnection(connectionString);
_connection.Open();
}
public void Dispose()
{
_connection.Close();
}
public void Create<T>(T obj) where T : IClassModel<T>
{
// insert into tableName values()
Type myType = obj.GetType();
IList<PropertyInfo> props = new List<PropertyInfo>(myType.GetProperties());
ArrayList valueArray = new ArrayList();
ArrayList nameArray = new ArrayList();
var questionString = "";
var nameString = "";
foreach (PropertyInfo prop in props)
{
object propValue = prop.GetValue(obj, null);
object propName = prop.Name;
valueArray.Add(propValue);
nameArray.Add(propName);
questionString += "?, ";
nameString += $"{propName}, " ;
}
var newNameString = nameString.Trim();
var newerNameString = newNameString.TrimEnd(',');
var newQuestionString = questionString.Trim();
var newerQuestionString = newQuestionString.TrimEnd(',');
SqliteCommand insertSQL = new SqliteCommand($"INSERT INTO {typeof(T).Name} ({newerNameString}) VALUES ({newerQuestionString})", _connection);
foreach (var item in valueArray)
{
insertSQL.Parameters.Add(item);
}
insertSQL.ExecuteNonQuery();
//Console.WriteLine("Successfully added the thing.");
}
}
Driver:
using Microsoft.Data.Sqlite;
using QueryBuilder.Models;
using System.Reflection;
using (var query = new queryBuilder(#"Data Source=C:\path\to\database"))
{
// con
var user = new Users();
user.UserName = "username";
user.UserAddress = "some_address";
user.OtherUserDetails = "details";
user.AmountOfFine = 90;
user.Email = "something#email.com";
user.PhoneNumber = "5555555555";
query.Create<Users>(user);
}
I know my code is bit messy, but the idea is to somehow create an object and then be able to insert it into the already made table, no matter what object it is. I keep getting invalid cast exceptions.
I need to be able to iterate through the values and properties and add them to the sqlite insert command but it doesn't seem to be working. Any help is appreciated.

Using Reflection to create extensible charts for a website

I’m trying to implement a website where new charts can be added by just dropping a DLL into folder. At the time of writing it is not clear what charts are going to be needed and this provides a simple way of deploying new charts without having to redeploy the website in its entirety. I’m using Google Charts to provide the Google functionality and each chart will be displayed
Each type of chart inherits from the following Interface
public enum ChartType
{
BAR,
COLUMN,
PIE,
TABLE
}
public class DataColumn
{
public String ColumnType { get; set; }
public String ColumnValue { get; set; }
}
public interface IChart
{
/// <summary>
/// Dictionary of Columns, Each column is defined as a type and title
/// </summary>
List<DataColumn> Columns { get; set; }
/// <summary>
/// ChartType, What type of Chart; possible values BAR, COLUMN, PIE, TABLE
/// </summary>
ChartType ChartType { get; }
/// <summary>
/// Data - data for the chart
/// </summary>
String Data { get; }
/// <summary>
/// Name of the chart, must be unique used to identify each chart stub
/// </summary>
String Name { get; }
/// <summary>
/// Title - the title that will be displayed above the chart
/// </summary>
String Title { get; }
/// <summary>
/// What position will the legend of there is one.
/// </summary>
String LegendPosition { get; }
}
The following uses the above interface
public class ReferralDownloadCount : IChart, IDisposable
{
List<ChartDemo.DataColumn> columns = null;
public ReferralDownloadCount()
{
columns = new List<ChartDemo.DataColumn>()
{
new ChartDemo.DataColumn() { ColumnType = "String" , ColumnValue = "Date" },
new ChartDemo.DataColumn() { ColumnType = "Number" , ColumnValue = "Referral Count"}
};
}
/// <summary>
/// Returns the chart data
/// </summary>
public String Data
{
get
{
String sql = String.Empty;
String jsonResult = String.Empty;
DataSet ds = null;
DataTable dt = null;
List<ReferralCountData> results = null;
JsonSerializer serializer = null;
try
{
sql = "Select * From[Portal].[ReferralCount] Where DATEDIFF(d, [Download Date], Convert(Date, GETDATE())) < 8 Order By[Download Date] Asc";
ds = DataToolbox.Execute(new SqlConnection(Properties.Settings.Default.DataConnection), sql, CommandType.Text);
if (ds.Tables.Count > 0)
{
dt = ds.Tables[0]; // we really are only expecting one table
results = new List<ReferralCountData>();
serializer = new JsonSerializer();
serializer.Converters.Add(new JavaScriptDateTimeConverter());
foreach ( DataRow dr in dt.Rows)
{
using (ReferralCountData rcd = new ReferralCountData()
{
Label = ((System.DateTime)dr.ItemArray[0]).ToString("dd/MM/yyyy"),
Value = Convert.ToInt32(dr["Referral Count"])
})
{
results.Add(rcd);
}
}
jsonResult = JsonConvert.SerializeObject(results);
}
}
catch ( System.Exception ex)
{
throw ex;
}
finally
{
}
return jsonResult;
}
}
public List<ChartDemo.DataColumn> Columns {
get
{
return columns;
}
set
{
columns = value;
}
}
public ChartType ChartType => ChartType.COLUMN;
public string Name => "REFERRALCOUNT";
public string Title => "Referral Download Count";
public string LegendPosition => "None";
public void Dispose()
{
}
}
The site traverses a directory containing DLLs, and searches for any classes such as the one above to create Charts
Classes which have inherited from IChart are then extracted as each DLL is checked by GetChartPlugins
static List<IChart> GetChartPlugins(List<Assembly> assemblies)
{
List<Type> availableTypes = new List<Type>();
List<Type> alertList = null;
try
{
foreach (Assembly currentAssembly in assemblies)
availableTypes.AddRange(currentAssembly.GetTypes());
alertList = availableTypes.FindAll(delegate (Type t)
{
List<Type> interfaceTypes = new List<Type>(t.GetInterfaces());
return interfaceTypes.Contains(typeof(IChart));
});
}
catch (ReflectionTypeLoadException ex)
{
StringBuilder sb = new StringBuilder();
foreach (Exception exSub in ex.LoaderExceptions)
{
sb.AppendLine(exSub.Message);
FileNotFoundException exFileNotFound = exSub as FileNotFoundException;
if (exFileNotFound != null)
{
if (!string.IsNullOrEmpty(exFileNotFound.FusionLog))
{
sb.AppendLine("Fusion Log:");
sb.AppendLine(exFileNotFound.FusionLog);
}
}
sb.AppendLine();
}
string errorMessage = sb.ToString();
}
catch ( System.Exception ex)
{
throw ex;
}
finally
{
}
// convert the list of Objects to an instantiated list of ICalculators
return alertList.ConvertAll<IChart>(delegate (Type t) { return Activator.CreateInstance(t) as IChart; });
}
However when this runs the attempt to Call currentAssembly.GetTypes() falls over throwing a ReflectionTypeLoadException
Which eventually becomes;
Method 'get_Columns' in type 'ReferralManagementCharts.ReferralDownloadCount' from assembly 'ReferralManagementCharts, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' does not have an implementation.
Can anyone see why since the Columns property of ReferralDownlodCount
public List<ChartDemo.DataColumn> Columns {
get
{
return columns;
}
set
{
columns = value;
}
}
Does have a get.

C# parse SQL statement to find all INSERT/UPDATE/DELETE tables used in stored procedures

As the title says my intention is to find all tables participating in either INSERT/UPDATE/DELETE statements and produce a structured format. So far this is what I've come up with -
void Main()
{
string DBName = "Blah";
string ServerName = #"(localdb)\MSSQLLocalDB";
Server s = new Server(ServerName);
Database db = s.Databases[DBName];
ConcurrentDictionary<string, SPAudit> list = new ConcurrentDictionary<string, SPAudit>();
var sps = db.StoredProcedures.Cast<StoredProcedure>()
.Where(x => x.ImplementationType == ImplementationType.TransactSql && x.Schema == "dbo")
.Select(x => new
{
x.Name,
Body = x.TextBody
}).ToList();
Parallel.ForEach(sps, item =>
{
try
{
ParseResult p = Parser.Parse(item.Body);
IEnumerable<SqlInsertStatement> insStats = null;
IEnumerable<SqlUpdateStatement> updStats = null;
IEnumerable<SqlDeleteStatement> delStats = null;
var listTask = new List<Task>();
listTask.Add(Task.Run(() =>
{
insStats = FindBatchCollection<SqlInsertStatement>(p.Script.Batches);
}));
listTask.Add(Task.Run(() =>
{
updStats = FindBatchCollection<SqlUpdateStatement>(p.Script.Batches);
}));
listTask.Add(Task.Run(() =>
{
delStats = FindBatchCollection<SqlDeleteStatement>(p.Script.Batches);
}));
Task.WaitAll(listTask.ToArray());
foreach (var ins in insStats)
{
var table = ins?.InsertSpecification?.Children?.FirstOrDefault();
if (table != null)
{
var tableName = table.Sql.Replace("dbo.", "").Replace("[", "").Replace("]", "");
if (!tableName.StartsWith("#"))
{
var ll = list.ContainsKey(item.Name) ? list[item.Name] : null;
if (ll == null)
{
ll = new SPAudit();
}
ll.InsertTable.Add(tableName);
list.AddOrUpdate(item.Name, ll, (key, old) => ll);
}
}
}
foreach (var ins in updStats)
{
var table = ins?.UpdateSpecification?.Children?.FirstOrDefault();
if (table != null)
{
var tableName = table.Sql.Replace("dbo.", "").Replace("[", "").Replace("]", "");
if (!tableName.StartsWith("#"))
{
var ll = list.ContainsKey(item.Name) ? list[item.Name] : null;
if (ll == null)
{
ll = new SPAudit();
}
ll.UpdateTable.Add(tableName);
list.AddOrUpdate(item.Name, ll, (key, old) => ll);
}
}
}
foreach (var ins in delStats)
{
var table = ins?.DeleteSpecification?.Children?.FirstOrDefault();
if (table != null)
{
var tableName = table.Sql.Replace("dbo.", "").Replace("[", "").Replace("]", "");
if (!tableName.StartsWith("#"))
{
var ll = list.ContainsKey(item.Name) ? list[item.Name] : null;
if (ll == null)
{
ll = new SPAudit();
}
ll.DeleteTable.Add(tableName);
list.AddOrUpdate(item.Name, ll, (key, old) => ll);
}
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
});
}
IEnumerable<T> FindBatchCollection<T>(SqlBatchCollection coll) where T : SqlStatement
{
List<T> sts = new List<T>();
foreach (var item in coll)
{
sts.AddRange(FindStatement<T>(item.Children));
}
return sts;
}
IEnumerable<T> FindStatement<T>(IEnumerable<SqlCodeObject> objs) where T : SqlStatement
{
List<T> sts = new List<T>();
foreach (var item in objs)
{
if (item.GetType() == typeof(T))
{
sts.Add(item as T);
}
else
{
foreach (var sub in item.Children)
{
sts.AddRange(FindStatement<T>(item.Children));
}
}
}
return sts;
}
public class SPAudit
{
public HashSet<string> InsertTable { get; set; }
public HashSet<string> UpdateTable { get; set; }
public HashSet<string> DeleteTable { get; set; }
public SPAudit()
{
InsertTable = new HashSet<string>();
UpdateTable = new HashSet<string>();
DeleteTable = new HashSet<string>();
}
}
Now I'm facing two problems
First, its is taking hell lot of a time to complete, given that there are around 841 stored procedures in the database.
Second, if there are statements like the following the table name is not being captured properly, meaning that the table is being captured as w instead of SomeTable_1 or SomeTable_2.
CREATE PROCEDURE [dbo].[sp_blah]
#t SomeTableType READONLY
AS
DELETE w
FROM SomeTable_2 w
INNER JOIN (Select * from #t) t
ON w.SomeID = t.SomeID
DELETE w
FROM SomeTable_1 w
INNER JOIN (Select * from #t) t
ON w.SomeID = t.SomeID
RETURN 0
Any help would be greatly appreciated.
Edit
Using the following dll from this location C:\Program Files (x86)\Microsoft SQL Server\140\DTS\Tasks-
Microsoft.SqlServer.ConnectionInfo.dll
Microsoft.SqlServer.Management.SqlParser.dll
Microsoft.SqlServer.Smo.dll
Microsoft.SqlServer.SqlEnum.dll
Finally I got it to work like I wanted the output to look like using #dlatikay answer. I'm posting this here more for documentation purposes than anything else.
I'm using the following nuget packages -
https://www.nuget.org/packages/Microsoft.SqlServer.SqlManagementObjects/
https://www.nuget.org/packages/Microsoft.SqlServer.TransactSql.ScriptDom/
and removed all other local dependencies. I hope this helps someone out there.
void Main()
{
string DatabaseName = "Blah";
string ServerIP = #"(localdb)\MSSQLLocalDB";
List<string> ExcludeList = new List<string>()
{
"sp_upgraddiagrams",
"sp_helpdiagrams",
"sp_helpdiagramdefinition",
"sp_creatediagram",
"sp_renamediagram",
"sp_alterdiagram",
"sp_dropdiagram"
};
List<string> StringDataTypes = new List<string>()
{
"nvarchar",
"varchar",
"nchar",
"char",
};
Server s = new Server(ServerIP);
s.SetDefaultInitFields(typeof(StoredProcedure), "IsSystemObject");
Database db = s.Databases[DatabaseName];
Dictionary<string, SPAudit> AuditList = new Dictionary<string, SPAudit>();
var sps = db.StoredProcedures.Cast<StoredProcedure>()
.Where(x => x.ImplementationType == ImplementationType.TransactSql && x.Schema == "dbo" && !x.IsSystemObject)
.Select(x => new
{
x.Name,
Body = x.TextBody,
Parameters = x.Parameters.Cast<StoredProcedureParameter>().Select(t =>
new SPParam()
{
Name = t.Name,
DefaultValue = t.DefaultValue,
DataType = $"{t.DataType.Name}{(StringDataTypes.Contains(t.DataType.Name) ? $"({(t.DataType.MaximumLength > 0 ? Convert.ToString(t.DataType.MaximumLength) : "MAX")})" : "")}"
})
}).ToList();
foreach (var item in sps)
{
try
{
TSqlParser parser = new TSql140Parser(true, SqlEngineType.Standalone);
IList<ParseError> parseErrors;
TSqlFragment sqlFragment = parser.Parse(new StringReader(item.Body), out parseErrors);
sqlFragment.Accept(new OwnVisitor(ref AuditList, item.Name, item.Parameters));
}
catch (Exception ex)
{
//Handle exception
}
}
}
public class OwnVisitor : TSqlFragmentVisitor
{
private string spname;
private IEnumerable<SPParam> parameters;
private Dictionary<string, SPAudit> list;
public OwnVisitor(ref Dictionary<string, SPAudit> _list, string _name, IEnumerable<SPParam> _parameters)
{
list = _list;
spname = _name;
parameters = _parameters;
}
public override void ExplicitVisit(InsertStatement node)
{
NamedTableReference namedTableReference = node?.InsertSpecification?.Target as NamedTableReference;
if (namedTableReference != null)
{
string table = namedTableReference?.SchemaObject.BaseIdentifier?.Value;
if (!string.IsNullOrWhiteSpace(table) && !table.StartsWith("#"))
{
if (!list.ContainsKey(spname))
{
SPAudit ll = new SPAudit();
ll.InsertTable.Add(table);
ll.Parameters.AddRange(parameters);
list.Add(spname, ll);
}
else
{
SPAudit ll = list[spname];
ll.InsertTable.Add(table);
}
}
}
base.ExplicitVisit(node);
}
public override void ExplicitVisit(UpdateStatement node)
{
NamedTableReference namedTableReference;
if (node?.UpdateSpecification?.FromClause != null)
{
namedTableReference = node?.UpdateSpecification?.FromClause?.TableReferences[0] as NamedTableReference;
}
else
{
namedTableReference = node?.UpdateSpecification?.Target as NamedTableReference;
}
string table = namedTableReference?.SchemaObject.BaseIdentifier?.Value;
if (!string.IsNullOrWhiteSpace(table) && !table.StartsWith("#"))
{
if (!list.ContainsKey(spname))
{
SPAudit ll = new SPAudit();
ll.UpdateTable.Add(table);
ll.Parameters.AddRange(parameters);
list.Add(spname, ll);
}
else
{
SPAudit ll = list[spname];
ll.UpdateTable.Add(table);
}
}
base.ExplicitVisit(node);
}
public override void ExplicitVisit(DeleteStatement node)
{
NamedTableReference namedTableReference;
if (node?.DeleteSpecification?.FromClause != null)
{
namedTableReference = node?.DeleteSpecification?.FromClause?.TableReferences[0] as NamedTableReference;
}
else
{
namedTableReference = node?.DeleteSpecification?.Target as NamedTableReference;
}
if (namedTableReference != null)
{
string table = namedTableReference?.SchemaObject.BaseIdentifier?.Value;
if (!string.IsNullOrWhiteSpace(table) && !table.StartsWith("#"))
{
if (!list.ContainsKey(spname))
{
SPAudit ll = new SPAudit();
ll.DeleteTable.Add(table);
ll.Parameters.AddRange(parameters);
list.Add(spname, ll);
}
else
{
SPAudit ll = list[spname];
ll.DeleteTable.Add(table);
}
}
}
base.ExplicitVisit(node);
}
}
public class SPAudit
{
public HashSet<string> InsertTable { get; set; }
public HashSet<string> UpdateTable { get; set; }
public HashSet<string> DeleteTable { get; set; }
public List<SPParam> Parameters { get; set; }
public SPAudit()
{
InsertTable = new HashSet<string>();
UpdateTable = new HashSet<string>();
DeleteTable = new HashSet<string>();
Parameters = new List<SPParam>();
}
}
public class SPParam
{
public string Name { get; set; }
public string DefaultValue { get; set; }
public string DataType { get; set; }
}
The SMO model exposes elements of the syntax tree. So instead of assuming a token by position, as in
UpdateSpecification?.Children?.FirstOrDefault();
look up the corresponding property in the documentation. For the update clause, the target table (or updatable view) can occur in different positions. Take this syntax:
UPDATE tablename SET column=value WHERE conditions
which is represented as
var targettable = ins?.UpdateSpecification?.Target?.ScriptTokenStream?.FirstOrDefault()?.Text;
in the SMO model. Whereas, a syntax unique to tsql,
UPDATE t SET t.columnname=value FROM tablename t WHERE conditions
will have its list of tables in the FROM clause.
Regarding the other two DML statements you mentioned: DELETE is the same because they share a common base class, DeleteInsertSpecification (Target).
For INSERT, there is the Target as well, and if its InsertSource is of type SelectInsertSource, this may be based on any number of tables and views too.
You can use following SQL Query:
SELECT *
FROM sys.dm_sql_referenced_entities ('dbo.APSP_MySP', 'OBJECT');
It gives you all the tables, views, SPs impacted in the stored procedure.
is_selected or is_select_all are set to 1 for selected references
is_updated is set to 1 for updated references
As query is reading from pre-defined system tables, it runs fast
If you need information about the referred object use the referenced_id column value to find details
You can use it in 2 ways:
Call the above query in parallel for each stored procedure
Create another query/SP which will loop and run it for every stored procedure
Change Proc_1 to your procedure name
Refine PATINDEX matching to cater for the different possibilites
Modify to look at all procedures
Does not cater for tables in dynamic sql or passed as parameters
Look out for any issues with dm_sql_referenced_entities
SELECT
e.TableName,
p.name,
PATINDEX('%DELETE '+e.TableName+'%', p.definition) AS is_delete,
PATINDEX('%INSERT INTO '+e.TableName+'%', p.definition) AS is_insert,
PATINDEX('%UPDATE '+e.TableName+'%', p.definition) AS is_update
FROM
(
SELECT distinct referenced_entity_name AS TableName
FROM sys.dm_sql_referenced_entities ('dbo.Proc_1', 'OBJECT')
) e,
(
SELECT o.name, m.object_id, definition
FROM sys.objects o, sys.sql_modules m
WHERE o.name = 'Proc_1'
AND o.type='P'
AND m.object_id = o.object_id
) p
I would recommend you querying the syscomments SQL view. The performance will be much better.
select text from sys.syscomments where text like '%DELETE%'
You can work with the results in the SQL Query or fetch all the results and filter the data in C#.

Add Remove Update List of class in C#

I'm working on this for more than a week and quit stressed currently,
Hope you guys can put me out of my misery.
I welcome If you can suggest overall different approach too. Okay here we go,
I'm on a learning curve and creating a small chat app using SignalR, MVC, JSON, jquery.
I have Chatter class which contain list of ChatMsg class (Msgs). As GetData() method shows below, I'm getting my classes populated from database to a list. As you can see list of Chatter contain some variables including list of ChatMsg. This will get any changes to Table ( new chat messages).
Up to here, this is working fine. [Add part]
[Serializable]
public class Chatter
{
public string Name { get; set; }
public bool Open { get; set; }
public DateTime LastMsg { get; set; }
public IEnumerable<ChatMsg> Msgs { get; set; }
}
[Serializable]
public class ChatMsg
{
public DateTime MsgCreated { get; set; }
public string MsgType { get; set; }
public string MsgBody { get; set; }
}
public List<Chatter> GetData()
{
Dictionary<string, List<ChatMsg>> dcm = new Dictionary<string, List<ChatMsg>>();
List<Chatter> lcm = new List<Chatter>();
using (var connection = new SqlConnection(_connString))
{
connection.Open();
using (var command = new SqlCommand(#"SELECT [Sender], [Receiver], [Body], [MessageCreated] FROM [dbo].[Chat] WHERE [Receiver] = #Name AND [Seen] = #Seen", connection))
{
command.Parameters.Add(new SqlParameter("#Name", "Fan"));//Test val
command.Parameters.Add(new SqlParameter("#Seen", "0"));//Test val
command.Notification = null;
var dependency = new SqlDependency(command);
dependency.OnChange += new OnChangeEventHandler(dependency_OnChange);
if (connection.State == ConnectionState.Closed)
connection.Open();
var reader = command.ExecuteReader();
while (reader.Read())
{
List<ChatMsg> cm = new List<ChatMsg>();
cm.Add(item: new ChatMsg { MsgCreated = Convert.ToDateTime(reader["MessageCreated"]), MsgType = "from", MsgBody = (string)reader["Body"] });
if (dcm.ContainsKey((string)reader["Sender"]))
{ dcm[(string)reader["Sender"]].Add(item: new ChatMsg { MsgCreated = Convert.ToDateTime(reader["MessageCreated"]), MsgType = "from", MsgBody = (string)reader["Body"] }); }
else { dcm.Add((string)reader["Sender"], cm); }
}
}
}
foreach (KeyValuePair<string, List<ChatMsg>> pair in dcm)
{
lcm.Add(item: new Chatter { Name = pair.Key, Open = true, LastMsg = DateTime.UtcNow, Msgs = pair.Value });
}
// Updateting [Seen] = 1 here
return lcm;
}
Now if this is a new instance I'm putting this list of Chatters to Session.
Each time when getData() gets new data I'd like to check my Session["ChatHistory"] and if Parent.Name exist I'd like to update Parent and Addrange to Msgs, if not ad new parent from getData() session list.
I'm strugling on following code.
public string receiveMessages()
{
if (Session["ChatHistory"] == null) Session["ChatHistory"] = new List<Chatter>();
List<Chatter> lc = (List<Chatter>)Session["ChatHistory"];
ChatRepository chatRepository = new ChatRepository();
List<Chatter> c = (List<Chatter>)chatRepository.getData();
//havent tested below
foreach (Chatter e in c)
{
var temp_lc = lc.Find(n => n.Name == e.Name);// Can we avoid linq?
if (temp_lc == null)
{
lc.Add(e);
}
else
{
// How to Addrange to Msgs?
}
}
var serializer = new JavaScriptSerializer();
var t = serializer.Serialize(lc);
return t;
}
How to Update list of class in list of class?
How to remove an item from list of class?
Thank you so much!
Consider using variable names like chatters and chatterHistory instead of c and lc. It makes it much easier to read.
Try rewriting your foreach in receiveMessages() like so:
foreach (Chatter e in c)
{
var temp_lc = lc.Where(x => x.Name == e.Name).SingleOrDefault();
if (temp_lc == null)
{
lc.Add(e);
}
else
{
temp_lc.Msgs = temp_lc.Msgs.Concat(e.Msgs).ToList();
}
}
If temp_lc exists, temp_lc.Msgs.Concat(e.Msgs).ToList() will concatenate the Msgs property with e.Msgs. ToList() converts it into a List<ChatMsg>, and then we can assign the whole thing back to temp_lc.Msgs.
That last step is important because Concat() does not mutate (change) the object it is called on - instead, it returns a new object that we then can assign back to temp_lc.Msgs.

Error while converting List of objects to Datatable

Hello I have the following method.
public static DataTable ConvertToDataTable<T>(List<T> lstData)
{
PropertyDescriptorCollection objPropertiesCollection = TypeDescriptor.GetProperties(typeof(T));
DataTable dtResult = new DataTable();
foreach (PropertyDescriptor objProperty in objPropertiesCollection)
{
dtResult.Columns.Add(objProperty.Name, Nullable.GetUnderlyingType(objProperty.PropertyType) ?? objProperty.PropertyType);
}
foreach (T item in lstData)
{
DataRow dr = dtResult.NewRow();
foreach (PropertyDescriptor objProperty in objPropertiesCollection)
{
dr[objProperty.Name] = objProperty.GetValue(item) ?? DBNull.Value;
}
dtResult.Rows.Add(dr);
}
return dtResult;
}
and I have the following Class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace ClientesPagos.Clases
{
[Serializable]
public class Servicio
{
public int IdServicio;
public string Nombre;
public string Descripcion;
public string Imagen;
public decimal Precio;
public int Cantidad;
public decimal Total;
public Servicio()
{
}
public Servicio(int id, string nombre, string descripcion, string img, decimal precio, int cantidad)
{
IdServicio = id;
Nombre = nombre;
Descripcion = descripcion;
Imagen = img;
Precio = precio;
Cantidad = cantidad;
Total = cantidad * precio;
}
}
}
now the problem is on the first line of the method
PropertyDescriptorCollection objPropertiesCollection =
TypeDescriptor.GetProperties(typeof(T));
It does not fill objPropertiesCollection and therefore the following instructions don't work.
Is there something wrong on the class, that it may not be working on the first Method?
You are trying to get properties an you should change your fields to properties.
Those are properties:
public int IdServicio {get; set;}
public string Nombre { get; set; }
public string Descripcion { get; set; }
public string Imagen { get; set; }
public decimal Precio { get; set; }
public int Cantidad { get; set; }
public decimal Total { get; set; }
So you want to convert "everything" to a DataTable? Then i would use the CopyToDataTable approach on MSDN with it's ObjectShredder class.
How to: Implement CopyToDataTable Where the Generic Type T Is Not a DataRow
public class ObjectShredder<T> {
private System.Reflection.FieldInfo[] _fi;
private System.Reflection.PropertyInfo[] _pi;
private System.Collections.Generic.Dictionary<string, int> _ordinalMap;
private System.Type _type;
// ObjectShredder constructor.
public ObjectShredder() {
_type = typeof(T);
_fi = _type.GetFields();
_pi = _type.GetProperties();
_ordinalMap = new Dictionary<string, int>();
}
/// <summary>
/// Loads a DataTable from a sequence of objects.
/// </summary>
/// <param name="source">The sequence of objects to load into the DataTable.</param>
/// <param name="table">The input table. The schema of the table must match that
/// the type T. If the table is null, a new table is created with a schema
/// created from the public properties and fields of the type T.</param>
/// <param name="options">Specifies how values from the source sequence will be applied to
/// existing rows in the table.</param>
/// <returns>A DataTable created from the source sequence.</returns>
public DataTable Shred(IEnumerable<T> source, DataTable table, LoadOption? options) {
// Load the table from the scalar sequence if T is a primitive type.
if (typeof(T).IsPrimitive) {
return ShredPrimitive(source, table, options);
}
// Create a new table if the input table is null.
if (table == null) {
table = new DataTable(typeof(T).Name);
}
// Initialize the ordinal map and extend the table schema based on type T.
table = ExtendTable(table, typeof(T));
// Enumerate the source sequence and load the object values into rows.
table.BeginLoadData();
using (IEnumerator<T> e = source.GetEnumerator()) {
while (e.MoveNext()) {
if (options != null) {
table.LoadDataRow(ShredObject(table, e.Current), (LoadOption)options);
} else {
table.LoadDataRow(ShredObject(table, e.Current), true);
}
}
}
table.EndLoadData();
// Return the table.
return table;
}
public DataTable ShredPrimitive(IEnumerable<T> source, DataTable table, LoadOption? options) {
// Create a new table if the input table is null.
if (table == null) {
table = new DataTable(typeof(T).Name);
}
if (!table.Columns.Contains("Value")) {
table.Columns.Add("Value", typeof(T));
}
// Enumerate the source sequence and load the scalar values into rows.
table.BeginLoadData();
using (IEnumerator<T> e = source.GetEnumerator()) {
Object[] values = new object[table.Columns.Count];
while (e.MoveNext()) {
values[table.Columns["Value"].Ordinal] = e.Current;
if (options != null) {
table.LoadDataRow(values, (LoadOption)options);
} else {
table.LoadDataRow(values, true);
}
}
}
table.EndLoadData();
// Return the table.
return table;
}
public object[] ShredObject(DataTable table, T instance) {
FieldInfo[] fi = _fi;
PropertyInfo[] pi = _pi;
if (instance.GetType() != typeof(T)) {
// If the instance is derived from T, extend the table schema
// and get the properties and fields.
ExtendTable(table, instance.GetType());
fi = instance.GetType().GetFields();
pi = instance.GetType().GetProperties();
}
// Add the property and field values of the instance to an array.
Object[] values = new object[table.Columns.Count];
foreach (FieldInfo f in fi) {
values[_ordinalMap[f.Name]] = f.GetValue(instance);
}
foreach (PropertyInfo p in pi) {
values[_ordinalMap[p.Name]] = p.GetValue(instance, null);
}
// Return the property and field values of the instance.
return values;
}
public DataTable ExtendTable(DataTable table, Type type) {
// Extend the table schema if the input table was null or if the value
// in the sequence is derived from type T.
foreach (FieldInfo f in type.GetFields()) {
if (!_ordinalMap.ContainsKey(f.Name)) {
// Add the field as a column in the table if it doesn't exist
// already.
DataColumn dc = table.Columns.Contains(f.Name) ? table.Columns[f.Name]
: table.Columns.Add(f.Name, f.FieldType);
// Add the field to the ordinal map.
_ordinalMap.Add(f.Name, dc.Ordinal);
}
}
foreach (PropertyInfo p in type.GetProperties()) {
if (!_ordinalMap.ContainsKey(p.Name)) {
// Add the property as a column in the table if it doesn't exist
// already.
DataColumn dc = table.Columns.Contains(p.Name) ? table.Columns[p.Name]
: table.Columns.Add(p.Name, p.PropertyType);
// Add the property to the ordinal map.
_ordinalMap.Add(p.Name, dc.Ordinal);
}
}
// Return the table.
return table;
}
}
Now you can add these extensions:
public static class CustomLINQtoDataSetMethods {
public static DataTable CopyToDataTable<T>(this IEnumerable<T> source) {
return new ObjectShredder<T>().Shred(source, null, null);
}
public static DataTable CopyToDataTable<T>(this IEnumerable<T> source,
DataTable table, LoadOption? options) {
return new ObjectShredder<T>().Shred(source, table, options);
}
}
Now CopyToDataTable works with any kind of IEnumerable<T>(even anonymous types) not only with DataRows.

Categories