I have a project that has a login screen. When the user logs in the Controller calls another Class that I am using as a data Access class. This Class called the stored procedure to verify if the user is valid and pulls back all the user information and loads it into a UserModel. This works as I just have the data access method return a bool. So then I am logged in. But the information is not staying or is never stored in my model. I have been researching this and working off and on for this for weeks. I am hoping another set of eyes will help show me what I am missing. Thanks.
Here is my Model
public class UserModel
{
/// <summary>
/// Holds the current username.
/// </summary>
public string UserName {get;set;}
/// <summary>
/// Holds the First Name of the user.
/// </summary>
public string FirstName { get; set; }
/// <summary>
/// Holds the last name of the user.
/// </summary>
public string LastName { get; set; }
/// <summary>
/// Holds the users Date of Birth.
/// </summary>
public string UserDOB { get; set; }
/// <summary>
/// Holds the users Email Address
/// that was provided.
/// </summary>
public string Email { get; set; }
/// <summary>
/// Holds the current Password of the user.
/// </summary>
public string Password { get; set; }
/// <summary>
/// Holds the Date the account was created.
/// </summary>
public DateTime CreationDate { get; set; }
/// <summary>
/// Holds the PK of the type of user,
/// Admin, Standard, Promotional
/// </summary>
public int UserType { get; set; }
}
Here is my Data Access
public bool UserLogin(string userName, string Password)
{
bool userFound = false;
const string Query = #"GetUserLogin";
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
SqlCommand command = new SqlCommand(Query, connection)
{
CommandType = CommandType.StoredProcedure
};
command.Parameters.AddWithValue("#UserName", userName);
command.Parameters.AddWithValue("#Password", Password);
using (SqlDataReader reader = command.ExecuteReader())
{
reader.Read();
userModel.UserName = reader["UserName"].ToString();
userModel.FirstName = reader["FirstName"].ToString();
userModel.LastName = reader["LastName"].ToString();
userModel.Email = reader["Email"].ToString();
userModel.UserDOB = reader["UserDOB"].ToString();
if (reader == null)
{
userFound = false;
}
else
{
userFound = true;
}
}
}
return userFound;
}
This is my User Controller
public class UserController : Controller
{
private UserLoginDataAccess dataAccess = new UserLoginDataAccess();
// GET: /Login/
[HttpGet]
public IActionResult Login()
{
return View();
}
[HttpPost, ValidateAntiForgeryToken]
public IActionResult Login(UserModel model)
{
bool isLoggedIn;
isLoggedIn = dataAccess.UserLogin(model.UserName, model.Password);
if (isLoggedIn)
{
return View("Dashboard");
}
else
{
return View();
}
}
}
Firstly, this is never not null:
if (reader == null)
Secondly: where / how is userModel defined? If that is a field on the controller, then note that it jsn't persisted anywhere between requests. You need to use other approaches here. Usually an auth cookie of some kind.
Thirdly: I'm horrified to know whether you're doing a flat password compare in the DB; please let it be hashed...
Fourthly: writing your own security code is almost never a good idea. There are login providers that get it right. I recommend using them.
Fifthly: you should test whether Read() returned true or not - although frankly I think I'd recommend offloading all of this DB code to something like Dapper.
Related
This is my setup for my .NET Core application:
public class Program
{
public static void Main(string[] args)
{
BuildWebHost(args).Run();
}
public static IWebHost BuildWebHost(string[] args)
{
return WebHost.CreateDefaultBuilder(args)
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.ConfigureAppConfiguration((builderContext, config) =>
{
var entryAssemblyFolder = new FileInfo(Assembly.GetEntryAssembly().Location).DirectoryName;
IHostingEnvironment env = builderContext.HostingEnvironment;
config
.SetBasePath(entryAssemblyFolder)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
})
.UseStartup<Startup>()
.Build();
}
}
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public static IConfiguration Configuration { get; set; }
}
All of the above runs when the program starts. At a later point in time during the execution of the program, I'd like to be able to add additional configuration based on data which isn't known at startup time. The following is pseudo-code since there's no parameter to the ConfigurationBuilder constructor:
public class Helper
{
public void Add(string key, string value)
{
//pseudo-code:
var builder = new ConfigurationBuilder(Startup.Configuration);
builder.AddInMemoryCollection(new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>(key, value)
});
Startup.Configuration = builder.Build();
}
}
How can I add to the existing configuration, while keeping what's already there (including the reloadOnChange: true) ?
Thanks,
I did some more digging into the source of MemoryConfigurationProvider. The provider copies the initial data into it's own Data property.
So even if you update your original dictionary, the provider will still only have the original values, when it was initialized.
Haven't tried the GetReloadToken approach which might work, but here is an alternative more direct approach.
Consider this be your configuration:
public class MyConfiguration
{
public static Dictionary<string,string> InMemoryCollection =
new Dictionary<string, string>
{
{"InMemoryCollection:Option1", "value1"},
{"InMemoryCollection:Option2", "value2"}
};
}
Initialize your configuration:
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration(builder => {
builder.AddInMemoryCollection(MyConfiguration.InMemoryCollection);
})
.UseStartup<Startup>();
Assume you have a controller to read and enter new values:
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
private IConfigurationRoot configuration;
/// <summary>
/// Initializes a new instance of the <see cref="ValuesController"/> class.
/// Value injected through DI.
/// </summary>
/// <param name="configuration">The configuration.</param>
public ValuesController(IConfiguration configuration)
{
this.configuration = (IConfigurationRoot)configuration;
}
[HttpGet]
/// <summary>
/// Get in memory values.
/// </summary>
/// <returns></returns>
public IDictionary<string,string> Get()
{
var result = new Dictionary<string, string>();
this.configuration.GetSection("InMemoryCollection").Bind(result);
return result;
}
[HttpPost]
/// <summary>
/// Enter a new value.
/// </summary>
/// <param name="value">The value.</param>
public void Post([FromBody] string value)
{
//get the provider instance from the configuration root.
MemoryConfigurationProvider memoryProvider =
(MemoryConfigurationProvider)this.configuration.Providers
.First(p =>
p.GetType() == typeof(MemoryConfigurationProvider));
//add the new option into the providers data collection.
var nextKey = MyConfiguration.InMemoryCollection.Count + 1;
memoryProvider.Add(
$"InMemoryCollection:Option{nextKey}",
value);
}
}
Note: I don't know the details/requirements for configuration options that are added dynamically, but using the AddInMemoryCollection approach doesn't look like it's the best of choices. Unless of course, you deliberately don't want to persist them.
I am fairly certain though that there are other approaches/solutions to resolve the issue, without necessarily using the Microsoft.Extensions.Configuration APIs.
Why on gods earth will the APFile.Id not deserialize correct ???
I have a simple class APFile - which I try to deserialize - for some reason it will not deserialize the Id - and I cannot see where its going wrong :(
using System;
using Newtonsoft.Json;
namespace ActionShared
{
[Serializable]
public class APFile
{
public APFile()
{
}
/// <summary>
/// File id
/// </summary>
[JsonProperty("id")]
public int Id { get; set; }
/// <summary>
/// The id of entity that the file is attached to.
/// </summary>
// [JsonProperty("type")]
[JsonIgnore]
public int Type { get; set; }
/// <summary>
/// The file name including the extension.
/// </summary>
[JsonProperty("filename")]
public string Filename { get; set; }
/// <summary>
/// The mime type of the file.
/// </summary>
[JsonIgnore]
public string Mimetype { get; set; }
/// <summary>
/// The size of the file in bytes.
/// </summary>
//[JsonProperty("size")]
[JsonIgnore]
public int Size { get; set; }
/// <summary>
/// The unique id of the file creator's profile.
/// </summary>
//[JsonProperty("creator_id")]
[JsonIgnore]
public int Creator { get; set; }
/// <summary>
/// The unique id of the file creator's profile.
/// </summary>
//[JsonProperty("creator_name")]
[JsonIgnore]
public string CreatorName { get; set; }
/// <summary>
/// The date and time when the file was uploaded.
/// </summary>
// [JsonProperty("created_at")]
[JsonIgnore]
public DateTime CreatedAt { get; set; }
/// <summary>
/// The date and time when the file was last updated.
/// </summary>
// [JsonProperty("updated_at")]
[JsonIgnore]
public DateTime UpdatedAt { get; set; }
/// <summary>
/// A link to download the file.
/// </summary>
//[JsonProperty("url")]
[JsonIgnore]
public string Url { get; set; }
}
}
public void Do()
{
string a = "{\"id\":910,\"filename\":\"image.jpg\"}";
var b = JsonConvert.DeserializeObject<APFile>(a);
// b.Id = 0 and b.Filename = "image.jpg"
}
plz copy your json responce and convert into C# class file to http://json2csharp.com/
public home()
{
InitializeComponent();
NavigationPage.SetHasNavigationBar(this, false);
PostRequest(this, "http://propertyworld.in/app/property.php?");
}
public async static void PostRequest(home instance, string URL)
{
var formContent = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("action", "property_list"),
});
var myHttpClient = new HttpClient();
var response = await myHttpClient.PostAsync(URL, formContent);
//json variable is over responce
var json = await response.Content.ReadAsStringAsync();
MyJsonConvertedclass record = JsonConvert.DeserializeObject<MyJsonConvertedclass>(json);
ObservableCollection<ModelClass.infoClass> ls = new ObservableCollection<ModelClass.infoClass> { };
foreach (var point in record.records)
{
ls.Add(new ModelClass.infoClass
{
info1 = Stringclass.rs + " " + point.property.price,
info2 = point.property.title.ToUpper(),
url = record.image_url + point.property.image
});
}
instance.newProp.ItemsSource = ls;
//instance.yourControllname which is declares in your xaml like
}
I have following JSON string which is received from an external party.
{
"team":[
{
"v1":"",
"attributes":{
"eighty_min_score":"",
"home_or_away":"home",
"score":"22",
"team_id":"500"
}
},
{
"v1":"",
"attributes":{
"eighty_min_score":"",
"home_or_away":"away",
"score":"30",
"team_id":"600"
}
}
]
}
My mapping classes:
public class Attributes
{
public string eighty_min_score { get; set; }
public string home_or_away { get; set; }
public string score { get; set; }
public string team_id { get; set; }
}
public class Team
{
public string v1 { get; set; }
public Attributes attributes { get; set; }
}
public class RootObject
{
public List<Team> team { get; set; }
}
The question is that I don't like the Attributes class name and the attributes field names in the Team class. Instead, I want it to be named TeamScore and also to remove _ from the field names and give proper names.
JsonConvert.DeserializeObject<RootObject>(jsonText);
I can rename Attributes to TeamScore, but if I change the field name (attributes in the Team class), it won't deserialize properly and gives me null. How can I overcome this?
Json.NET - Newtonsoft has a JsonPropertyAttribute which allows you to specify the name of a JSON property, so your code should be:
public class TeamScore
{
[JsonProperty("eighty_min_score")]
public string EightyMinScore { get; set; }
[JsonProperty("home_or_away")]
public string HomeOrAway { get; set; }
[JsonProperty("score ")]
public string Score { get; set; }
[JsonProperty("team_id")]
public string TeamId { get; set; }
}
public class Team
{
public string v1 { get; set; }
[JsonProperty("attributes")]
public TeamScore TeamScores { get; set; }
}
public class RootObject
{
public List<Team> Team { get; set; }
}
Documentation: Serialization Attributes
If you'd like to use dynamic mapping, and don't want to clutter up your model with attributes, this approach worked for me
Usage:
var settings = new JsonSerializerSettings();
settings.DateFormatString = "YYYY-MM-DD";
settings.ContractResolver = new CustomContractResolver();
this.DataContext = JsonConvert.DeserializeObject<CountResponse>(jsonString, settings);
Logic:
public class CustomContractResolver : DefaultContractResolver
{
private Dictionary<string, string> PropertyMappings { get; set; }
public CustomContractResolver()
{
this.PropertyMappings = new Dictionary<string, string>
{
{"Meta", "meta"},
{"LastUpdated", "last_updated"},
{"Disclaimer", "disclaimer"},
{"License", "license"},
{"CountResults", "results"},
{"Term", "term"},
{"Count", "count"},
};
}
protected override string ResolvePropertyName(string propertyName)
{
string resolvedName = null;
var resolved = this.PropertyMappings.TryGetValue(propertyName, out resolvedName);
return (resolved) ? resolvedName : base.ResolvePropertyName(propertyName);
}
}
Adding to Jacks solution. I need to Deserialize using the JsonProperty and Serialize while ignoring the JsonProperty (or vice versa). ReflectionHelper and Attribute Helper are just helper classes that get a list of properties or attributes for a property. I can include if anyone actually cares. Using the example below you can serialize the viewmodel and get "Amount" even though the JsonProperty is "RecurringPrice".
/// <summary>
/// Ignore the Json Property attribute. This is usefule when you want to serialize or deserialize differently and not
/// let the JsonProperty control everything.
/// </summary>
/// <typeparam name="T"></typeparam>
public class IgnoreJsonPropertyResolver<T> : DefaultContractResolver
{
private Dictionary<string, string> PropertyMappings { get; set; }
public IgnoreJsonPropertyResolver()
{
this.PropertyMappings = new Dictionary<string, string>();
var properties = ReflectionHelper<T>.GetGetProperties(false)();
foreach (var propertyInfo in properties)
{
var jsonProperty = AttributeHelper.GetAttribute<JsonPropertyAttribute>(propertyInfo);
if (jsonProperty != null)
{
PropertyMappings.Add(jsonProperty.PropertyName, propertyInfo.Name);
}
}
}
protected override string ResolvePropertyName(string propertyName)
{
string resolvedName = null;
var resolved = this.PropertyMappings.TryGetValue(propertyName, out resolvedName);
return (resolved) ? resolvedName : base.ResolvePropertyName(propertyName);
}
}
Usage:
var settings = new JsonSerializerSettings();
settings.DateFormatString = "YYYY-MM-DD";
settings.ContractResolver = new IgnoreJsonPropertyResolver<PlanViewModel>();
var model = new PlanViewModel() {Amount = 100};
var strModel = JsonConvert.SerializeObject(model,settings);
Model:
public class PlanViewModel
{
/// <summary>
/// The customer is charged an amount over an interval for the subscription.
/// </summary>
[JsonProperty(PropertyName = "RecurringPrice")]
public double Amount { get; set; }
/// <summary>
/// Indicates the number of intervals between each billing. If interval=2, the customer would be billed every two
/// months or years depending on the value for interval_unit.
/// </summary>
public int Interval { get; set; } = 1;
/// <summary>
/// Number of free trial days that can be granted when a customer is subscribed to this plan.
/// </summary>
public int TrialPeriod { get; set; } = 30;
/// <summary>
/// This indicates a one-time fee charged upfront while creating a subscription for this plan.
/// </summary>
[JsonProperty(PropertyName = "SetupFee")]
public double SetupAmount { get; set; } = 0;
/// <summary>
/// String representing the type id, usually a lookup value, for the record.
/// </summary>
[JsonProperty(PropertyName = "TypeId")]
public string Type { get; set; }
/// <summary>
/// Billing Frequency
/// </summary>
[JsonProperty(PropertyName = "BillingFrequency")]
public string Period { get; set; }
/// <summary>
/// String representing the type id, usually a lookup value, for the record.
/// </summary>
[JsonProperty(PropertyName = "PlanUseType")]
public string Purpose { get; set; }
}
Expanding Rentering.com's answer, in scenarios where a whole graph of many types is to be taken care of, and you're looking for a strongly typed solution, this class can help, see usage (fluent) below. It operates as either a black-list or white-list per type. A type cannot be both (Gist - also contains global ignore list).
public class PropertyFilterResolver : DefaultContractResolver
{
const string _Err = "A type can be either in the include list or the ignore list.";
Dictionary<Type, IEnumerable<string>> _IgnorePropertiesMap = new Dictionary<Type, IEnumerable<string>>();
Dictionary<Type, IEnumerable<string>> _IncludePropertiesMap = new Dictionary<Type, IEnumerable<string>>();
public PropertyFilterResolver SetIgnoredProperties<T>(params Expression<Func<T, object>>[] propertyAccessors)
{
if (propertyAccessors == null) return this;
if (_IncludePropertiesMap.ContainsKey(typeof(T))) throw new ArgumentException(_Err);
var properties = propertyAccessors.Select(GetPropertyName);
_IgnorePropertiesMap[typeof(T)] = properties.ToArray();
return this;
}
public PropertyFilterResolver SetIncludedProperties<T>(params Expression<Func<T, object>>[] propertyAccessors)
{
if (propertyAccessors == null)
return this;
if (_IgnorePropertiesMap.ContainsKey(typeof(T))) throw new ArgumentException(_Err);
var properties = propertyAccessors.Select(GetPropertyName);
_IncludePropertiesMap[typeof(T)] = properties.ToArray();
return this;
}
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var properties = base.CreateProperties(type, memberSerialization);
var isIgnoreList = _IgnorePropertiesMap.TryGetValue(type, out IEnumerable<string> map);
if (!isIgnoreList && !_IncludePropertiesMap.TryGetValue(type, out map))
return properties;
Func<JsonProperty, bool> predicate = jp => map.Contains(jp.PropertyName) == !isIgnoreList;
return properties.Where(predicate).ToArray();
}
string GetPropertyName<TSource, TProperty>(
Expression<Func<TSource, TProperty>> propertyLambda)
{
if (!(propertyLambda.Body is MemberExpression member))
throw new ArgumentException($"Expression '{propertyLambda}' refers to a method, not a property.");
if (!(member.Member is PropertyInfo propInfo))
throw new ArgumentException($"Expression '{propertyLambda}' refers to a field, not a property.");
var type = typeof(TSource);
if (!type.GetTypeInfo().IsAssignableFrom(propInfo.DeclaringType.GetTypeInfo()))
throw new ArgumentException($"Expresion '{propertyLambda}' refers to a property that is not from type '{type}'.");
return propInfo.Name;
}
}
Usage:
var resolver = new PropertyFilterResolver()
.SetIncludedProperties<User>(
u => u.Id,
u => u.UnitId)
.SetIgnoredProperties<Person>(
r => r.Responders)
.SetIncludedProperties<Blog>(
b => b.Id)
.Ignore(nameof(IChangeTracking.IsChanged)); //see gist
I am using JsonProperty attributes when serializing but ignoring them when deserializing using this ContractResolver:
public class IgnoreJsonPropertyContractResolver: DefaultContractResolver
{
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var properties = base.CreateProperties(type, memberSerialization);
foreach (var p in properties) { p.PropertyName = p.UnderlyingName; }
return properties;
}
}
The ContractResolver just sets every property back to the class property name (simplified from Shimmy's solution). Usage:
var airplane= JsonConvert.DeserializeObject<Airplane>(json,
new JsonSerializerSettings { ContractResolver = new IgnoreJsonPropertyContractResolver() });
Also if you want to ignore something use this
[JsonIgnore]
public int Id { get; set; }
[JsonProperty("id")]
Public string real_id { get; set; }
I am using entity framework code first with dotConnect for MySQL, and for some reason I get an error about one of my models mapping.
I've searched this error and it is usually caused by bad xml file or entity framework specific files. I am not using any of these, only a .cs code file with the models.
Exception Details:
System.Data.MappingException: The number of members in the conceptual
type 'SiteModels.TournamentTable' does not match with the number of
members on the object side type 'SiteModels.TournamentTable'. Make
sure the number of members are the same.
I have no idea why I am getting this error, since I don't have any designer,
only one file containing the code.
Here is the problematic class:
public class TournamentTable
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public List<TournamentColumn> Columns { get; set; }
public TournamentTable()
{
Columns = new List<TournamentColumn>();
}
public void AddColumn(int index, TournamentColumn column)
{
Columns.Insert(index, column);
}
public void RemoveColumn(int index)
{
Columns.RemoveAt(index);
}
/// <summary>
/// Add a tournament cell at the top of the column.
/// </summary>
/// <param name="column"></param>
public void AddColumn(TournamentColumn column)
{
Columns.Add(column);
}
/// <summary>
/// Returns the tournament column at the index specified.
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public TournamentColumn this[int index]
{
get { return Columns[index]; }
}
/// <summary>
/// Returns the tournament cell at the index specified.
/// </summary>
/// <param name="columnIndex"></param>
/// <param name="cellIndex"></param>
/// <returns></returns>
public TournamentCell this[int columnIndex, int cellIndex]
{
get { return Columns[columnIndex][cellIndex]; }
set
{
Columns[columnIndex][cellIndex] = value;
}
}
}
Context configuration:
public class EntitiesContext : DbContext
{
public EntitiesContext()
: base()
{
System.Data.Entity.Database.SetInitializer<EntitiesContext>(new DropCreateDatabaseIfModelChanges<EntitiesContext>());
}
public EntitiesContext(DbConnection connection)
: base(connection, true)
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions
.Remove<System.Data.Entity.ModelConfiguration.Conventions
.ColumnTypeCasingConvention>();
}
public DbSet<User> Users { get; set; }
public DbSet<Player> Players { get; set; }
public DbSet<Game> Games { get; set; }
public DbSet<TournamentTable> TournamentTables { get; set; }
}
Thanks for the help, I don't have any clue.
Try commenting out the indexers and see if it helps. If it does you may need to put NotMapped attribute on them or reimplement them as methods.
Is there anyway simple ways to add a property to Linq to Sql generated entity to reference its DataContext?
For example:
var myContext = new DataContext();
var product = context.Products.Where(p => p.Id == productId).SingleOrDefault();
and product entity has a property called "Context" (product.Context) that has a reference to the myContext, datacontext.
I know how to customize generated entities. My question is how to customize (i think DataContext) to set 'Context' property of each instance that it creates to itself.
I'm not sure I'm doing the right thing or not. I want to write a business encapsulation model that has best performance with less code.
As i googled around I have find out that the DataContext object is very lightweight and there for I thought it would be a good way to add an instance of DataContext to each object. This will reduce the need to attach, detached objects again to a new instance of datacontext every time I want to update or delete them.
If you have any other solution I will really appreciate it.
Thanks
Is there a simple way to add a property to Linq to Sql generated entity to reference its DataContext?
There is no simple way to achieve this.
In doing this, you defeat part of the purpose of LINQ-to-SQL. One of the purposes is to allow to you work with the objects that you have, and not have to change your design based on database considerations.
In attaching the DataContext to the database, you are coupling your representation in code with the means to persist it, which is generally a bad design idea.
If you feel you must do this though, you can always derive from the class and then implement an interface on that class which exposes the DataContext.
I recommend implementing the interface, since you can't indicate a base class to the LINQ-to-SQL designer, and you want to be able for all of your entities to have a shared implementation.
See here: Determine the source DataContext for a Linq to Sql query
I asked more or less the same question. You can get the context from the IQueryable that a linq to sql query returns, but not from the entity itself as far as I know.
Actually, I agree to casperOne. If you really have to need this, I remembered that the classes that linq-to-sql generates are partial. So you can write a partial class to any class you want and add extended functionalities to it.
Pass the data context as a ref parameter to a custom method on your partial Product object:
public partial class Product
{
public string GetSomethingElse(ref DataContext dbase)
{
return dbase.OtherTableWhatever.Count.ToString(); // whatever
}
}
Inside your aspx.cs:
var context = new DataContext();
var product = context.Products.SingleOrDefault(x => x.id == 1);
var s = product.GetSomethingElse(ref context);
Here is a custom wrapper I made for System.Data.Linq. It contains a find method, so instead of your code:
var myContext = new DataContext();
var product = context.Products.Where(p => p.Id == productId).SingleOrDefault();
you can do this
var myContext = new DataContext();
var product = context.Products.Find(productId); //(assuming productId is your primary key)
You can grab the code below and do any custom modifications you wish to set product.Context, but this is an example of modifying the DataContext.
I also made save and delete methods. You'll notice I go out a regrab the record even though it is being passed in. I do this because the record might get detached from the context and not update. If anyone would like the full code I can post the github link.
public abstract class DbContext : IDisposable
{
#region Properties
private string _connectionString { get; set; }
private DataContext _context { get; set; }
#endregion
#region Constructor
public DbContext(string connectionString)
{
_connectionString = connectionString;
_context = new DataContext(_connectionString);
Initialized(_context);
}
public DbContext(string server, string database, string userID, string password)
{
_connectionString = string.Format(
"Server={0};Database={1};User Id={2};Password={3};MultipleActiveResultSets=true",
server,
database,
userID,
password);
_context = new DataContext(_connectionString);
Initialized(_context);
}
#endregion
#region Methods
/// <summary>
/// Is used to get the contents of a Sql Server Table.
/// </summary>
/// <typeparam name="TEntity">Type</typeparam>
/// <returns>Table</returns>
public Table<TEntity> GetTable<TEntity, TPKType>()
where TEntity : DbTableEquatable<IDbTableEquatable<TPKType>>
where TPKType : struct
{
return _context.GetTable<TEntity>();
}
/// <summary>
/// Is used to get the contents of a Sql Server Table.
/// </summary>
/// <typeparam name="TEntity">Type</typeparam>
/// <returns>Table</returns>
public Table<TEntity> GetTable<TEntity>()
where TEntity : DbTableEquatable<IDbTableEquatable<long>>
{
return GetTable<TEntity, long>();
}
protected virtual void Initialized(DataContext context) { }
/// <summary>
/// Saves the changes to the database. In order to save the table must inherit from DbTableEquatable
/// and be a type of IDbTableEquatable and the Primary Key Type must be a C# Structure
/// </summary>
/// <typeparam name="TEntity">Record Type</typeparam>
/// <typeparam name="TPKType">Primary Key Type</typeparam>
/// <param name="entity">Record</param>
public virtual void SaveChanges<TEntity, TPKType>(TEntity entity)
where TEntity : DbTableEquatable<IDbTableEquatable<TPKType>>
where TPKType : struct
{
var changedList = _context.GetTable<TEntity>();
var ID = entity.GetType().GetProperty("ID").GetValue(entity);
_preprocessSave<TEntity, TPKType>(entity);
// Save changes
if (Convert.ToInt64(ID) == 0)
{
// Insert
// If No ID we need to insert on submit
_context.GetTable<TEntity>().InsertOnSubmit((TEntity)entity);
_context.SubmitChanges();
}
else
{
// Update
var item = changedList.Where(w => w.Equals(entity)).FirstOrDefault();
ReflectionManager.SetValuesWithSkip(entity, item, "ID");
_context.SubmitChanges();
}
Refresh();
}
/// <summary>
/// Saves the changes to the database. In order to save the Table the Record is from must inherit from DbTableEquatable
/// and be a type of IDbTableEquatable and the Primary Key Type must be a C# Structure
/// </summary>
/// <typeparam name="TEntity">Record Type</typeparam>
/// <param name="entity">Record</param>
public virtual void SaveChanges<TEntity>(TEntity entity)
where TEntity : DbTableEquatable<IDbTableEquatable<long>>
{
SaveChanges<TEntity, long>(entity);
}
/// <summary>
/// Saves any non committed changes to the database
/// </summary>
public void SaveChanges()
{
_context.SubmitChanges();
Refresh();
}
/// <summary>
/// Marks the record as delete and will be deleted when saved
/// </summary>
/// <typeparam name="TEntity">Record Type</typeparam>
/// <typeparam name="TPKType">Primary Key Type</typeparam>
/// <param name="entity">Record</param>
public virtual void DeleteOnSave<TEntity, TPKType>(TEntity entity)
where TEntity : DbTableEquatable<IDbTableEquatable<TPKType>>
where TPKType : struct
{
var item = _context.GetTable<TEntity>().Where(w => w.Equals(entity)).FirstOrDefault();
_context.GetTable<TEntity>().DeleteOnSubmit((TEntity)item);
}
/// <summary>
/// Marks the record as delete and will be deleted when saved
/// </summary>
/// <typeparam name="TEntity">Record Type</typeparam>
/// <param name="entity">Record</param>
public virtual void DeleteOnSave<TEntity>(TEntity entity)
where TEntity : DbTableEquatable<IDbTableEquatable<long>>
{
DeleteOnSave<TEntity, long>(entity);
}
protected virtual void _preprocessSave<TEntity, TPKType>(TEntity entity)
where TEntity : DbTableEquatable<IDbTableEquatable<TPKType>>
where TPKType : struct
{
}
protected virtual void _preprocessSave<TEntity>(TEntity entity)
where TEntity : DbTableEquatable<IDbTableEquatable<long>>
{
_preprocessSave<TEntity, long>(entity);
}
public void Dispose()
{
_connectionString = "";
_context.Dispose();
_context = null;
}
public virtual void Refresh<TEntity>(RefreshMode mode, TEntity entity) where TEntity : class
{
_context.Refresh(RefreshMode.OverwriteCurrentValues, entity);
}
public virtual void Refresh()
{
_context = new DataContext(_connectionString);
}
public virtual void Refresh<TEntity>(RefreshMode mode, params TEntity[] entities) where TEntity : class
{
_context.Refresh(RefreshMode.OverwriteCurrentValues, entities);
}
public virtual void Refresh<TEntity>(RefreshMode mode, IEnumerable<TEntity> entities) where TEntity : class
{
_context.Refresh(RefreshMode.OverwriteCurrentValues, entities);
}
#endregion
}
Extenstions
public static class Extension
{
public static TEntity Find<TEntity, TPKType>(this Table<TEntity> table, TPKType ID, string pkName = "ID")
where TEntity : DbTableEquatable<IDbTableEquatable<TPKType>>
where TPKType : struct
{
TEntity copy = Activator.CreateInstance<TEntity>();
// set value through reflection
copy.GetType().GetProperty(pkName).SetValue(copy, ID, null);
return (TEntity)table.Where(w => w.Equals(copy)).FirstOrDefault();
}
public static TEntity Find<TEntity>(this Table<TEntity> table, long ID, string pkName = "ID")
where TEntity : DbTableEquatable<IDbTableEquatable<long>>
{
TEntity copy = Activator.CreateInstance<TEntity>();
// set value through reflection
copy.GetType().GetProperty(pkName).SetValue(copy, ID, null);
return (TEntity)table.Where(w => w.Equals(copy)).FirstOrDefault();
}
}
}
Interface/Abstraction
/// <summary>
/// This Class Assumes the type T has a property called ID. MUST be
/// used with IDbEquatable
/// </summary>
/// <typeparam name="T">Class Type</typeparam>
public abstract class DbTableEquatable<T> : IEquatable<T> where T : class
{
public bool Equals(T other)
{
//Check whether the compared object is null.
if (Object.ReferenceEquals(other, null))
{
return false;
}
//Check whether the compared object references the same data.
if (Object.ReferenceEquals(this, other))
{
return true;
}
return ((dynamic)other).ID == ((dynamic)this).ID;
}
}
/// <summary>
/// Needs to be inherited from in order for Ion.Data.Linq functions to work
/// </summary>
/// <typeparam name="T">Primary Key Type</typeparam>
public interface IDbTableEquatable<T>
{
T ID { get; set; }
}
here is a table implementation
[Table(Name = "Crews")]
public class Crew : DbTableEquatable<IDbTableEquatable<long>>, IDbTableEquatable<long>
{
[Column(IsPrimaryKey = true, DbType = "Bigint NOT NULL IDENTITY", AutoSync = AutoSync.OnInsert, IsDbGenerated = true)]
public long ID { get; set; }
[Column]
public string Alias { get; set; }
[Column]
public string DefaultForeground { get; set; }
[Column]
public string DefaultBackground { get; set; }
[Column]
public string AccountEmailAddress { get; set; }
[Column]
public long? CrewLeaderEmployeeID { get; set; }
[Column]
public string Comments { get; set; }
[Column]
public string Password { get; set; }
[Column]
public string PrivateICSLink { get; set; }
[Column]
public string PrivateXMLLink { get; set; }
}