Why is this code unable to persist data? - ado.net

public class Customer : BaseClass<Customer>
{
public string Name { get; set; }
public DateTime? DateOfBirth { get; set; }
public string TelephoneNumber { get; set; }
public string InsuranceProvider { get; set; }
public int? PolicyNumber { get; set; }
public byte [] Photo { get; set; }
}
This message shows up:
Or, this:
How to modify this code to be able to persist data?
CustomerDAO.cs
class CustomerDAO
{
.....
public int Save(ITransactionManager tm, Customer item)
{
int count = -1;
try
{
ISqlQueryExecutor<Customer> queryExecutor = new SqlQueryExecutor<Customer>(tm);
count =
queryExecutor.
ExecuteNonQuery(#"INSERT INTO Customer(
ID
,Name
,TelephoneNumber
,DateOfBirth,
InsuranceProvider,
PolicyNumber)
VALUES(
#ID
,#Name
,#TelephoneNumber
,#DateOfBirth,
#InsuranceProvider,
#PolicyNumber)",
item.ID,
item.Name,
item.TelephoneNumber,
item.DateOfBirth,
item.InsuranceProvider,
item.PolicyNumber,item.Photo
);
//new DbParameter(item.ID, DbType.Int32),
//new DbParameter(item.Name, DbType.String),
//new DbParameter(item.TelephoneNumber, DbType.String),
//new DbParameter(item.DateOfBirth, DbType.DateTime),
//new DbParameter(item.InsuranceProvider, DbType.String),
//new DbParameter(item.PolicyNumber, DbType.Int32)
//new DbParameter(item.Photo, DbType.Binary)
//);
string str = string.Empty;
}
catch (Exception ex)
{
throw ex;
}
return count;
}
.... ....
}
CustomerBLL.cs
class CustomerBLL
{
... ... ...
public int Save(Customer item)
{
int newId = 0;
ITransactionManager tm = ApplicationContext.Get(DBNameConst.ActiveConnStringName);
try
{
tm.BeginTransaction();
item.ID = newId = PivotTable.GetNextID(tm, "Customer").Value;
customerDao.Save(tm, item);
PivotTable.UpdateNextIdField(tm, "Customer", newId);
tm.CommitTransaction();
}
catch (Exception ex)
{
tm.RollbackTransaction();
throw ex;
}
return newId;
}
... ... ...
}
ASqlQueryExecutor.cs
public abstract class ASqlQueryExecutor<T> : ISqlQueryExecutor<T>
{
public virtual int ExecuteNonQuery(string queryString, params object[] parameters)
{
int count = -1;
try
{
Command = ParameterAttacher.AttachSaveParameters(TransactionManager, queryString, parameters);
Command.CommandText = queryString;
count = Command.ExecuteNonQuery();
}
catch (Exception ex)
{
throw ex;
}
return count;
}
ParameterAttacher.cs
class ParameterAttacher
{
public static IDbCommand AttachSaveParameters(ITransactionManager tm, string queryString, params object [] argumentsList)
{
IDbCommand command = new DbObjectInstantiator(tm.ProviderName).CreateCommand();
command.Connection = tm.Connection;
command.Transaction = tm.Transaction;
IList<string> parameterNamesList = new List<string>(ParameterParser.Parse(queryString));
if (parameterNamesList.Count > 0 && argumentsList.Length == argumentsList.Length)
{
int i = 0;
foreach (string paramName in parameterNamesList)
{
Attach(command, paramName, argumentsList[i]);
++i;
}
}
return command;
}
public static void Attach(IDbCommand command, string paramName, object dbParam)
{
IDbDataParameter param = command.CreateParameter();
param.ParameterName = paramName;
param.Value = (dbParam==null) ? ((object)DBNull.Value) : dbParam;
//param.DbType = dbParam.DbType;
command.Parameters.Add(param);
}
}

string or binary data would be truncated. the statement has been terminated
This could occur because you're trying to store too much data into a column. For example, if you have an nvarchar(5) column in a database and you try to store "this is a string" in there, you might get that error because 5 characters can't hold all of "this is a string".
You can avoid this problem by limiting the fields in your UI to the same length as those in the database. Or, you can perform a check in the validation methods.
Implicit conversion from data type nvarchar to binary is not allowed.
This seems fairly obvious: you're trying to store a character value in a binary column. You haven't provided your database schema, so I can't tell for sure where this could be; but, if you have a column or sproc parameter in the database set as binary but your C# details it as DbType.String you might get this error.
UPDATE:
You never set your DbType in ParameterAttacher.Attach. This means the Parameter will default to DbType.AnsiString for the parameter type. If you pass it a byte[] it may convert that to an ansi string, but when the parameter is given to ADO, it will see DbType.AnsiString and compare that to varbinary(50) (or money, or datetime, or int, etc.) and throw an exception detailing that it doesn't know how to convert to binary (e.g. an implicit conversion).
Also, do yourself a favour and get rid of:
catch(Exception ex)
{
throw ex;
}
That will just force you to loose the real location of the exception and cause you to waste time trying to figure out where the real problem is.
When you must catch (e.g. when you want to rollback the transaction, just throw, don't throw ex. Just throw won't lose the stack information and you can track down the location of the exception. For example:
catch (Exception ex)
{
tm.RollbackTransaction();
throw;
}

Related

Building GroupBy Expression Tree - IEnumerable parameter not defined error

I want to build an expression for IQueryable GroupBy. While at the moment I'm just simplifying the problem to try and get it working, the eventual final implementation will involve the creation of quite complex expression trees so I want to build a complete expression that can then be integrated into other expressions.
I specifically want to build an expression of this overload:
public static System.Linq.IQueryable<TResult> GroupBy<TSource,TKey,TResult> (
this System.Linq.IQueryable<TSource> source,
System.Linq.Expressions.Expression<Func<TSource,TKey>> keySelector,
System.Linq.Expressions.Expression<Func<TKey,System.Collections.Generic.IEnumerable<TSource>,TResult>> resultSelector);
... my problem is in the implementation of the resultSelector and and the IEnumerable<TSource>.
I have a table of Customers (just dummy data for the purposes of working out this problem). This is stored in an SQL DB and I specifically want to use IQueryable to access the data.
public class Customer
{
public int Id { get; set; }
public string? FirstName { get; set; }
public string? LastName { get; set; }
public int Age { get; set; }
}
I also have a GroupResult class used to hold the results of the GroupBy (I have different constructors which I've been using in my testing to work out where my problem is occurring)
internal class GroupResult
{
public string? Name { get; set; }
public int NumRecords { get; set; }
public decimal AverageAge { get; set; }
public int TotalAge { get; set; }
public GroupResult() { }
public GroupResult(string name)
{
Name = name;
}
public GroupResult(IEnumerable<Customer> customers)
{
Name = Guid.NewGuid().ToString();
NumRecords = customers.Count();
}
public GroupResult(string name, IEnumerable<Customer> customers)
{
Name = name;
NumRecords = customers.Count();
}
}
The main static class that displays prompts to select column to group on, creates the relevant expression tree and executes it
internal static class SimpleGroupByCustomer
{
internal static DataContext db;
internal static void Execute()
{
using (db = new DataContext())
{
//get input
Console.WriteLine();
Console.WriteLine("Simple Customer GroupBy");
Console.WriteLine("=======================");
Console.WriteLine("Simple GroupBy on the Customer Table");
Console.WriteLine();
Console.WriteLine("Select the property that you want to group by.");
Console.WriteLine();
var dbSet = db.Set<Customer>();
var query = dbSet.AsQueryable();
//for this example we're just prompting for a column in the customer table
//GetColumnName is a helper function that lists the available columns and allows
//one to be selected
string colName = Wrapper.GetColumnName("Customer");
MethodInfo? method = typeof(SimpleGroupByCustomer).GetMethod("GetGroupBy",
BindingFlags.Static | BindingFlags.NonPublic);
if (method != null)
{
method = method.MakeGenericMethod(new Type[] { typeof(String), query.ElementType });
method.Invoke(null, new object[] { query, colName });
}
}
}
internal static void GetGroupBy<T, TTable>(IQueryable query, string colName)
{
Type TTmp = typeof(TTable);
var param = Expression.Parameter(TTmp, "c");
var prop = Expression.PropertyOrField(param, colName);
LambdaExpression keySelector = Expression.Lambda<Func<TTable, T>>(prop, param);
var param1 = Expression.Parameter(typeof(T), "Key");
var param2 = Expression.Parameter(typeof(IEnumerable<TTable>), "Customers");
var ci = typeof(GroupResult).GetConstructor(new[] { typeof(T), typeof(IEnumerable<TTable>) });
//var ci = typeof(GroupResult).GetConstructor(new[] { typeof(T) });
//var ci = typeof(GroupResult).GetConstructor(new[] { typeof(IEnumerable<TTable>) });
if (ci == null)
return;
var pExp = new ParameterExpression[] { param1, param2 };
var methodExpression = Expression.Lambda<Func<T, IEnumerable<TTable>, GroupResult>>(
Expression.New(ci, new Expression[] { param1, param2 }), //<--- ERROR HERE
pExp
);
Type[] typeArgs = new Type[] { typeof(TTable), typeof(T), typeof(GroupResult) };
Expression[] methodParams = new Expression[] { query.Expression, keySelector, methodExpression };
var resultExpression = Expression.Call(typeof(Queryable), "GroupBy", typeArgs, methodParams);
IQueryable dbQuery = query.Provider.CreateQuery(resultExpression);
if (dbQuery is IQueryable<GroupResult> results)
{
foreach (var result in results)
{
Console.WriteLine("{0,-15}\t{1}", result.Name, result.NumRecords.ToString());
}
}
}
}
When I run this and try and iterate through the results I get the following exception:
System.InvalidOperationException: 'variable 'Customers' of type 'System.Collections.Generic.IEnumerable`1[ExpressionTrees3.Data.Customer]' referenced from scope '', but it is not defined'
which is being caused by the param2 ParameterExpression marked above.
If I use the GroupResult constructor that just takes the key value
var ci = typeof(GroupResult).GetConstructor(new[] { typeof(T) });
and omit the param2 from the Lambda body definition the code works as expected and I get a collection of GroupResult records containing the distinct key values in the Name field (but obviously no summary value).
I've tried everything I can think of and just can't get past this error - it's as though the GroupBy is not actually producing the IEnumerable grouping of Customers for each key.
I suspect I'm missing something really obvious here, but just can't see it. Any help would really very much appreciated.
Please note that I am after answers to this specific issue, I'm not looking for alternative ways of doing a GroupBy (unless there's a fundamental reason why this shouldn't work) - this will be rolled into a much larger solution for building queries and I want to use the same process throughout.
Thanks Svyatoslav - as I thought, it was me being especially dumb!
Your comments, as well as a discussion with a friend who has a lot SQL knowledge pointed me in the right direction.
I had been thinking that the GroupBy expression was going to return an Enumerable for each key value and was trying to pass that into a function ... it always felt wrong, but I just ignored that and kept going.
It's obvious now that I need to tell the GroupBy what to calculate and return (i.e. your comment about aggregation).
So for this easy example, the solution is very simple:
var pExp = new ParameterExpression[] { param1, param2 };
var countTypes = new Type[] { typeof(TTable) };
var countParams = new Expression[] { param2 };
var countExp = Expression.Call(typeof(Enumerable), "Count", countTypes, countParams);
var methodExpression = Expression.Lambda<Func<T, IEnumerable<TTable>, GroupResult>>(
Expression.New(ci, new Expression[] { param1, countExp }),
pExp
);
Just by adding the 'Count' expression into the GroupBy method call it works!
.. and adding a new ctor for GroupResult:
public GroupResult(string name, int count)
{
Name = name;
NumRecords = count;
}
(yep, I feel a bit stupid!)

Getting error code from MongoWriteException, duplicate key error

I'm trying to detect duplicate a key insertion error in my code written by means of C# and MongoDB.Driver.
Is it correct error handling for this case? (there is a unique index on EntityId column)
public class Entity
{
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
public string EntityId { get; set; }
}
...
public async Task<string> CreateEntityAsync(string entityId)
{
var entity = new Entity
{
EntityId = entityId,
};
try
{
await Collection.InsertOneAsync(peer);
}
//according to https://docs.mongodb.com/manual/core/index-unique error 11000 should be raised.
catch (MongoWriteException ex) when (GetErrorCode(ex) == 11000)
{
//custom error handling
}
return entity.Id;
}
private int GetErrorCode(MongoWriteException ex)
{
return (ex.InnerException as MongoBulkWriteException)?.WriteErrors.FirstOrDefault()?.Code ?? 0;
}

Return collection with flag for if items have children

I am trying to return a collection of customers, which works but I would like to make my call efficient so that each of the records brought back should have a Boolean return to see if that customer has children (addresses).
whats the most efficient call that will return me all the customers including a flag for each to determine if children are present
below is my current code for just retrieving customers (relationship between customer and address is through customerID on address table)
using entity framework 6.1
public static List<Customer> GetCustomers()
{
try
{
using (var context = new MyContext())
{
return (from c in context.Customers
select c).ToList();
}
}
catch (Exception ex)
{
throw new CustomerException(MethodBase.GetCurrentMethod().Name, ex);
}
}
I would perhaps do something like this.
public class CustomerDto
{
public string Name { get; set; }
public int AddressCount { get; set; }
}
var result = from w in context.Customers select new { Name = w.Name, AddressCount = w.Addresses.Count };
var ret = new List<CustomerDto>();
foreach (var customer in result)
{
var newCustomerDto = new CustomerDto();
newCustomerDto.Name = customer.Name;
newCustomerDto.AddressCount = customer.AddressCount;
}
return ret;
This is a simple example, and should serve as a starting point for you.
I don't have VS open, so just check the code, but it looks good to me. I forgot to mention that if it is only a flag that you are after, instead of .Count you can use .Any
Use this code
public static List<Customer> GetCustomers()
{
try
{
using (var context = new MyContext())
{
return (from c in context.Customers
where c.Address.Any()
select c).ToList();
}
}
catch (Exception ex)
{
throw new CustomerException(MethodBase.GetCurrentMethod().Name, ex);
}
}

EntityValidationException is thrown when saving my entity

I got this message when debugged in catch (Exception e). When User fill in all the information, Address and Payment View will get the SalesOrderID and redirect to Complete View. But it didn't show the Complete when it done.
[HttpPost]
public ActionResult AddressAndPayment(SalesOrderHeader order,Customer customer, Address address ,FormCollection values)
{
ViewBag.PersonType = new SelectList(new[] { "EM", "SC", "VC", "IN" } // work
.Select(x => new { value = x, text = x }),
"value", "text");
try
{
if (string.Equals(values["PromoCode"], PromoCode, StringComparison.OrdinalIgnoreCase) == false)
{
return View(order);
}
else
{
order.AccountNumber = User.Identity.Name;
order.OrderDate = DateTime.Now;
address.ModifiedDate = DateTime.Now; // maybe this error
order.Address.PostalCode = "12345";
//Save Order
BikeDBs.SalesOrderHeaders.Add(order);
try
{
BikeDBs.SaveChanges();
}
catch (DbEntityValidationException e)
{
foreach (var entityValidationErrors in e.EntityValidationErrors)
{
foreach (var validationError in entityValidationErrors.ValidationErrors)
{
Console.WriteLine("Properties: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage);
}
}
}
//Process Order
var cart = ShoppingCart.GetCart(this.HttpContext);
cart.CreateOrder(order);
//cart.CreateOrder(order1);
return RedirectToAction("Complete", new { id = order.SalesOrderID });
}
}
catch (Exception exception)
{
//Invalid - redisplay with errors
return View(order);
}
All I want is when the Order is saved, it will redirect to Complete. But in this case, it's not. And here is Address model:
public partial class Address
{
public Address()
{
this.SalesOrderHeaders = new HashSet<SalesOrderHeader>();
this.SalesOrderHeaders1 = new HashSet<SalesOrderHeader>();
}
public int AddressID { get; set; }
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public string City { get; set; }
public int StateProvinceID { get; set; }
public string PostalCode { get; set; }
public System.Guid rowguid { get; set; }
[Required()]
public Nullable<System.DateTime> ModifiedDate { get; set; }
public virtual StateProvince StateProvince { get; set; }
public virtual ICollection<SalesOrderHeader> SalesOrderHeaders { get; set; }
public virtual ICollection<SalesOrderHeader> SalesOrderHeaders1 { get; set; }
}
What's a solution and how to fix it?
You can do this pretty easily by using the ModelState, it should catch it. If it doesn't I added code into your catch block to catch it and display the page again with the errors using ModelState.AddModelError.
[HttpPost]
public ActionResult AddressAndPayment(SalesOrderHeader order,Customer customer, Address address ,FormCollection values)
{
ViewBag.PersonType = new SelectList(new[] { "EM", "SC", "VC", "IN" } // work
.Select(x => new { value = x, text = x }),
"value", "text");
if(ModelState.IsValid)
{
try
{
if (string.Equals(values["PromoCode"], PromoCode, StringComparison.OrdinalIgnoreCase) == false)
{
return View(order);
}
else
{
order.AccountNumber = User.Identity.Name;
order.OrderDate = DateTime.Now;
order.Address.PostalCode = values["PostalCode"];
//Save Order
BikeDBs.SalesOrderHeaders.Add(order);
try
{
BikeDBs.SaveChanges();
}
catch (DbEntityValidationException e)
{
foreach (var entityValidationErrors in e.EntityValidationErrors)
{
foreach (var validationError in entityValidationErrors.ValidationErrors)
{
// If this far add errors to model errors and show view again.
ModelState.AddModelError(validationError.PropertyName, validationError.ErrorMessage);
Console.WriteLine("Properties: {0} Error: {1}", validationError.PropertyName, validationError.ErrorMessage);
}
}
return View(order);
}
//Process Order
var cart = ShoppingCart.GetCart(this.HttpContext);
cart.CreateOrder(order);
//cart.CreateOrder(order1);
return RedirectToAction("Complete", new { id = order.SalesOrderID });
}
}
catch (Exception exception)
{
//Invalid - redisplay with errors
return View(order);
}
}
return View(order);
}
For my answer I assume that the properties PostalCode and PersonType are of type string and are defined as not nullable.
I think the error messages you get clearly say what the problem is. The properties PostalCode and PersonType are required, that means they need to have a value other than null.
So when you do not set the properties to a value other than null and you try to save your entity you will get the error messages.
To fix it you will net to set the properties to some values (maybe a default value) or you have to change your EntityModel to specify that these properties are nullable

ADO.NET distinct data bases

What programmers needed was a way to generalize different data systems
in a standard, consistent, and powerful way. In the world of .NET
application development, Microsoft ADO.NET meets that need. Instead of
worrying about the minutiae associated with the different database
systems, programmers using ADO.NET focus on the data content itself.
From the book "ADO.NET 4 Step by Step"
Always thinking the next structure.
ADO.NET can be sepearated on two parts:
1. Classic ADO.NET (DataSets, DataTables, etc.). In classic variant there are distinct DB connection providers, every which translates DB internal data to the .NET. For example MS SQL Server holds data in one way and Oracle in another. So we can change the data base with changing provider.
Seems as magic pill, however all ADO.NET statements are hard coded.
For MS SQL top 10 select statement is SELECT TOP 10 ROWS FROM TABLE, and for Oracle SELECT ROWS FROM TABELE WHERE ROWNUM <= 10. It seems that changing DB and provider will not help, isnt' it?
2. Entity Framework. This framework has internal independent language statements that is transformed to the choosen DB statesment, which looks as truely magic pill:
LINQ -> Internal EF statement -> MS SQL DB,
LINQ -> Internal EF statement -> Oracle DB.
So can one simply change the DB and be almost independent from DB in classic ADO.NET?
So can one simply change the DB and be almost independent from DB in classic ADO.NET?
Of course you can, but to be able to do that we have to debunk this statement.
Seems as magic pill, however all ADO.NET statements are hard coded.
That's not a byproduct of ADO.NET - that's a byproduct of your architecture. You're building the SQL statements in the wrong place. You need concrete, provider specific models, that are capable of building statements that differ between providers. This isn't as bad as it sounds - most statements can be auto-generated leveraging reflection - it's just the special cases.
For example, let's say I had a model like this:
public class Employee
{
public int ID { get; set; }
public string Name { get; set; }
public DateTime DateOfBirth { get; set; }
}
and let's say I wanted to generate a SELECT statement out of that. Well, I'm going to first need a couple attributes to tell me what property is the PK and what properties are data fields:
[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
internal sealed class DataFieldAttribute : Attribute
{
public DataFieldAttribute()
{
}
}
[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
sealed class PrimaryKeyAttribute : Attribute
{
public PrimaryKeyAttribute()
{
}
}
and now I need to decorate that class:
public class Employee
{
[PrimaryKey]
public int ID { get; set; }
[DataField]
public string Name { get; set; }
[DataField]
public DateTime DateOfBirth { get; set; }
}
and now I just need a simple process to create a SELECT statement, so first let's build a base data model class:
public abstract class DataModelBase
{
protected string _primaryKeyField;
protected List<string> _props = new List<string>();
public DataModelBase()
{
PropertyInfo pkProp = this.GetType().GetProperties().Where(p => p.GetCustomAttributes(typeof(PrimaryKeyAttribute), false).Length > 0).FirstOrDefault();
if (pkProp != null)
{
_primaryKeyField = pkProp.Name;
}
foreach (PropertyInfo prop in this.GetType().GetProperties().Where(p => p.GetCustomAttributes(typeof(DataFieldAttribute), false).Length > 0))
{
_props.Add(prop.Name);
}
}
public virtual string TableName { get { return this.GetType().Name; } }
public virtual string InsertStatement
{
get
{
return string.Format("INSERT INTO [{0}] ({1}) VALUES ({2})",
this.TableName,
GetDelimitedSafeFieldList(", "),
GetDelimitedSafeParamList(", "));
}
}
public virtual string UpdateStatement
{
get
{
return string.Format("UPDATE [{0}] SET {1} WHERE [{2}] = #{2}",
this.TableName,
GetDelimitedSafeSetList(", "),
_primaryKeyField);
}
}
public virtual string DeleteStatement
{
get
{
return string.Format("DELETE [{0}] WHERE [{1}] = #{1}",
this.TableName,
_primaryKeyField);
}
}
public virtual string SelectStatement
{
get
{
return string.Format("SELECT [{0}], {1} FROM [{2}]",
_primaryKeyField,
GetDelimitedSafeFieldList(", "),
this.TableName);
}
}
protected string GetDelimitedSafeParamList(string delimiter)
{
return string.Join(delimiter, _props.Select(k => string.Format("#{0}", k)));
}
protected string GetDelimitedSafeFieldList(string delimiter)
{
return string.Join(delimiter, _props.Select(k => string.Format("[{0}]", k)));
}
protected string GetDelimitedSafeSetList(string delimiter)
{
return string.Join(delimiter, _props.Select(k => string.Format("[{0}] = #{0}", k)));
}
}
and now let's inherit from that data model:
public class Employee : DataModelBase
and boom, now I can get those statements any time I need them, and those statements work for any concrete provider right now.
And then I'll use Dapper to get the data because it leverages the IDbConnection interface like what you need and it's ridiculously fast - and there you go - a provider independent solution that would easily be extended to build an Oracle version of Employee if necessary.
This framework has internal independent language statements that is transformed to the choosen DB statesment, which looks as truely magic pill
Sure, it may look like a magic pill, but it's really a curse in a lot of ways. You have no flexibility (at least it's not easy) to build statements that are optimized for your needs to support high transaction and volume databases. You truly are subject to the master here. The .NET Entity Framework builds those statements for you, and I can't even count how many questions on StackOverflow have been over how can I change the SQL that's being generated by this LINQ statement leveraging the .NET Entity Framework.
was staring to play with this. I know this is a old post. thanks to the above example herewith little modified. Still lots to be done
using System.Collections.Generic;
using System.Reflection;
using Dapper;
using System.Linq;
using AppAttributes;
using System.ComponentModel.DataAnnotations;
using System;
public abstract class DataModelBase
{
protected string _primaryKeyField;
protected List<string> _props = new List<string>();
protected List<BuildClass> _class = new List<BuildClass>();
public DataModelBase()
{
PropertyInfo pkProp = this.GetType().GetProperties().Where(p => p.GetCustomAttributes(typeof(KeyAttribute), false).Length > 0).FirstOrDefault();
if (pkProp != null)
{
_primaryKeyField = pkProp.Name;
}
foreach (PropertyInfo prop in this.GetType().GetProperties().Where(p => p.GetCustomAttributes(typeof(DataFieldAttribute), false).Length > 0))
{
_props.Add(prop.Name);
}
foreach(PropertyInfo prop in this.GetType().GetProperties())
{
if(prop.GetCustomAttributes<ExcludeAttribute>().Count<ExcludeAttribute>() > 0) continue;
MaxLengthAttribute maxLength = prop.GetCustomAttribute<MaxLengthAttribute>();
MinLengthAttribute minLength = prop.GetCustomAttribute< MinLengthAttribute>();
StringLengthAttribute stringLength = prop.GetCustomAttribute< StringLengthAttribute>();
RequiredAttribute required = prop.GetCustomAttribute<RequiredAttribute>();
RangeAttribute range = prop.GetCustomAttribute<RangeAttribute>();
DataTypeAttribute dataType = prop.GetCustomAttribute<DataTypeAttribute>();
KeyAttribute key = prop.GetCustomAttribute<KeyAttribute>();
var kyk = prop.PropertyType;
//var sss = kyk.FullName.;
var cl = new BuildClass
{
Name = prop.Name,
MaxLength = maxLength != null
? (int?)maxLength.Length
: stringLength != null
? (int?)stringLength.MaximumLength : null,
MinLength = minLength != null
? (int?)minLength.Length
: stringLength != null
? (int?)stringLength.MinimumLength : null,
PrimaryKey = key != null ? true : false,
Type = prop.PropertyType.Name.ToString()
};
_class.Add(cl);
}
}
[Exclude]
public virtual string TableName { get { return this.GetType().Name; } }
[Exclude]
public virtual string InsertStatement
{
get {
return string.Format("INSERT INTO [{0}] ({1}) VALUES ({2})",
this.TableName,
GetDelimitedSafeFieldList(", "),
GetDelimitedSafeParamList(", "));
}
}
[Exclude]
public virtual string UpdateStatement
{
get {
return string.Format("UPDATE [{0}] SET {1} WHERE [{2}] = #{2}",
this.TableName,
GetDelimitedSafeSetList(", "),
_primaryKeyField);
}
}
[Exclude]
public virtual string DeleteStatement
{
get {
return string.Format("DELETE [{0}] WHERE [{1}] = #{1}",
this.TableName,
_primaryKeyField);
}
}
[Exclude]
public virtual string SelectStatement
{
get {
return string.Format("SELECT [{0}], {1} FROM [{2}]",
_primaryKeyField,
GetDelimitedSafeFieldList(", "),
this.TableName);
}
}
[Exclude]
public virtual string CreateStatement
{
get {
return "CREATE TABLE " + TableName+" (" + GetDelimetedCreateParamList(",")
+ ", CONSTRAINT PK_"
+ _class.Where(c=>c.PrimaryKey).FirstOrDefault().Name
+ " PRIMARY KEY("
+ string.Join(",", _class.Where(c=>c.PrimaryKey).Select(c=>c.Name)) + ") )";
}
}
protected string GetDelimetedCreateParamList(string delimeter)
{
return string.Join(delimeter, _class.Select(k => string.Format(" {0} {1} ({2}) {3}" + Environment.NewLine,
k.Name,
GetSqlType(k.Type),
k.MaxLength,
k.NotNull == true || k.PrimaryKey == true ? "NOT NULL " : ""
//k.PrimaryKey == true ? "PRIMARY KEY" : ""
).Replace("()", ""))
);
}
protected string GetSqlType(string type)
{
switch(type.ToUpper())
{
case "INT16":
return "smallint";
case "INT16?":
return "smallint";
case "INT32":
return "int";
case "INT32?":
return "int";
case "INT64":
return "bigint";
case "INT64?":
return "bigint";
case "STRING":
return "NVARCHAR";
case "XML":
return "Xml";
case "BYTE":
return "binary";
case "BYTE?":
return "binary";
case "BYTE[]":
return "varbinary";
case "GUID":
return "uniqueidentifier";
case "GUID?":
return "uniqueidentifier";
case "TIMESPAN":
return "time";
case "TIMESPAN?":
return "time";
case "DECIMAL":
return "money";
case "DECIMAL?":
return "money";
case "bool":
return "bit";
case "bool?":
return "but";
case "DateTime":
return "datetime";
case "datetime?":
return "datetime";
case "double":
return "float";
case "double?":
return "float";
case "char[]":
return "nchar";
}
return "UNKNOWN";
}
private string CreateField(BuildClass column)
{
return " " + column.Name + " " + column.Type + " (" + column.MaxLength + ") ";
}
protected string GetDelimitedSafeParamList(string delimiter)
{
return string.Join(delimiter, _props.Select(k => string.Format("#{0}", k)));
}
protected string GetDelimitedSafeFieldList(string delimiter)
{
return string.Join(delimiter, _props.Select(k => string.Format("[{0}]", k)));
}
protected string GetDelimitedSafeSetList(string delimiter)
{
return string.Join(delimiter, _props.Select(k => string.Format("[{0}] = #{0}", k)));
}
}
public class BuildClass
{
public string Name { get; set; }
public string Type { get; set; }
public bool PrimaryKey { get; set; }
//public bool ForeignKey { get; set; }
public int? MinLength { get; set; }
public int? MaxLength { get; set; }
public bool NotNull { get; set; } = false;
}