According to Microsoft
the database context lifetime should be very short and any instantiated
objects should be disabled and removed from the ram as soon as they are no longer needed.
While I was developing an POS WPF application ,which based on caliburn.Micro framework and IOC container was implemented, I separated the project as following:
the entities classes & the context were putted in separated Class
Library project.
I implemented the repository and unit of work
design patterns in the main wpf project and i injected the context in
the unit of work class constructor.
I registered the context and the
unit of work into the ioc container "Autofac Container" with
InstancePerDependency life time for both of them which means a new instance will be created whenever an object of each on of them is
requested.
Finally ,i injected the unit of work interface into the ViewModel classes so i can load the proper entities for each VM in its constructor.
and the problems came here.
When I was trying to add a Product' Category and then I navigate to the Products VM to add a new product under this category ,but the VM didn't recognize that I had added new category.
when I traced the code I found that it happens with any VM. The VMs weren't feeling the changes except when I restart the program. I tried to fix it in many ways but no thing worked with me ,wo i decided to change the lifetime of the context to be "SingleInstance" which means one instance of the context will be created for whole the project.
I know it's a bad practice but there weren't any other choices.
The Questions are:
why did it happen?
what was the proper solution at this situation ?
does it also happen in the other workloads like mvc ,web apis or the Scoped lifetime handles it in the dot net core services container ?
was my separation structure correct and does it work fine for other projects or not?
Update:
This is my product entity:
public class Product
{
public Product()
{
ProductBarcodes = new HashSet<ProductBarcode>();
InventoriesProducts = new HashSet<InventoriesProducts>();
ProductUnits = new HashSet<ProductUnits>();
ProductsVariants = new HashSet<ProductsVariants>();
}
[Key]
public string Productkey { get; set; }
[Required,MaxLength(150), MinLength(3)]
public string ProductName { get; set; }
[MaxLength(500)]
public string Description { get; set; }
public string ImagePath { get; set; }
public decimal? SalesPrice { get; set; }
public decimal? Cost { get; set; }
public bool CanBeSold { get; set; }
public bool CanBePurchased { get; set; }
public ProductType ProductType { get; set; }
public long CategoryId { get; set; }
public virtual ProductCategory Category { get; set; }
[InverseProperty(nameof(ProductBarcode.Product))]
public virtual ICollection<ProductBarcode> ProductBarcodes { get; set; }
public virtual ICollection<InventoriesProducts> InventoriesProducts { get; set; }
public virtual ICollection<ProductUnits> ProductUnits { get; set; }
public virtual ICollection<ProductsVariants> ProductsVariants { get; set; }
}
This is my Category entity :
public class ProductCategory
{
[Key]
public long CategoryId { get; set; }
[Required, MaxLength(150), MinLength(3)]
public string Title { get; set; }
public IList<ProductCategory> SubCategories { get; set; }
public ProductCategory Parent { get; set; }
public IList<Product> Products { get; set; }
}
This project supports english and arabic languages so,i implemented the results messages as following:
I added the ITransactionResult generic interface that describe how any transaction result should be and it's generic to give me the ability to pass the entity type that i'm going to give transaction results about.
The interface has a UserMessageResource property of type Type this property usually was setted to Properties.Resources to search for a registered resource which contains the arabic or english message that will be displayed after the transaction.
Then i added two classes to implement this interface to describe two cases , when the Transaction ends successfully which was presented in SucessTransaction class and other wises were presented in FailedTransaction class.
ITransactionResult :
public interface ITransactionResult<T> where T : class
{
Exception ExceptionHappend { get; }
string UserMessage { get; }
string SourceType { get; }
bool Success { get; }
Type UserMessageResource { get; }
string UserMessageName { get; }
string[] Notes { get; }
}
SucessTransaction:
public class SuccessTransaction<T> : ITransactionResult<T> where T : class
{
public SuccessTransaction(string message,params string[] notes)
{
Success = true;
ExceptionHappend = null;
Notes = notes;
UserMessage = message;
}
public SuccessTransaction(Type UserMessageResource,string UserMessageName, params string[] notes):this(string.Empty,notes)
{
this.UserMessageResource = UserMessageResource;
this.UserMessageName = UserMessageName;
UserMessage = UserMessageResource.GetProperty(UserMessageName).GetValue(UserMessageResource).ToString();
}
public Exception ExceptionHappend { get; }
public string UserMessage { get; }
public bool Success {get;}
public string SourceType { get => nameof(T); }
public Type UserMessageResource { get; }
public string UserMessageName { get; }
public string[] Notes { get; }
}
FailedTransaction :
public class FailedTransaction<T> : ITransactionResult<T> where T : class
{
public FailedTransaction(string message,Exception exception,params string[] notes)
{
Success = false;
UserMessage = message;
ExceptionHappend = exception;
Notes = notes;
}
public FailedTransaction(Type UserMessageResource, string UserMessageName,Exception exception, params string[] notes)
:this(string.Empty,exception,notes)
{
this.UserMessageResource = UserMessageResource;
this.UserMessageName = UserMessageName;
UserMessage = UserMessageResource.GetProperty(UserMessageName).GetValue(UserMessageResource).ToString();
}
public Exception ExceptionHappend { get; }
public string UserMessage { get; }
public bool Success { get; }
public string SourceType { get => nameof(T); }
public Type UserMessageResource { get; }
public string UserMessageName { get; }
public string[] Notes { get; }
}
My Product repo :
public class ProductRepo : IProductRepo
{
private DbContext dbContext;
public Type DisplayResourceType { get; set; }
public ProductRepo(DbContext dbContext)
{
this.dbContext = dbContext;
}
public Product Add(Product entity, out ITransactionResult<Product> transactionResult)
{
try
{
var res = dbContext.Add(entity);
transactionResult = new SuccessTransaction<Product>(DisplayResourceType, "Adding_S");
return res.Entity;
}
catch (Exception ex)
{
transactionResult = new FailedTransaction<Product>(DisplayResourceType, "Adding_F", ex);
return entity;
}
}
public IEnumerable<Product> AddRange(IEnumerable<Product> entites, out ITransactionResult<Product> transactionResult)
{
try
{
dbContext.AddRange(entites);
transactionResult = new SuccessTransaction<Product>(DisplayResourceType, "AddingRang_S");
return entites;
}
catch (Exception ex)
{
transactionResult = new FailedTransaction<Product>(DisplayResourceType, "AddingRange_F", ex);
return entites;
}
}
public IEnumerable<Product> GetAll() => GetAllAsQueryable().ToList();
public IQueryable<Product> GetAllAsQueryable() => dbContext.Set<Product>();
public Product GetById(string id) => dbContext.Find<Product>(id);
public bool IsExist(string id, out Product entity)
{
entity = GetById(id);
return entity != null ? true : false;
}
public void Remove(Product entity, out ITransactionResult<Product> transactionResult)
{
try
{
var res = dbContext.Remove(entity);
transactionResult = new SuccessTransaction<Product>(DisplayResourceType, "Removing_S");
}
catch (Exception ex)
{
transactionResult = new FailedTransaction<Product>(DisplayResourceType, "Removing_F", ex);
}
}
public void RemoveRange(IEnumerable<Product> entities, out ITransactionResult<Product> transactionResult)
{
try
{
dbContext.AddRange(entities);
transactionResult = new SuccessTransaction<Product>(DisplayResourceType, "RemovingRange_S");
}
catch (Exception ex)
{
transactionResult = new FailedTransaction<Product>(DisplayResourceType, "RemovingRange_F", ex);
}
}
public Product Update(Product entity, out ITransactionResult<Product> transactionResult)
{
try
{
var res = dbContext.Update(entity);
transactionResult = new SuccessTransaction<Product>(DisplayResourceType, "Updating_S");
return res.Entity;
}
catch (Exception ex)
{
transactionResult = new FailedTransaction<Product>(DisplayResourceType, "Updating_F", ex);
return entity;
}
}
public IEnumerable<Product> UpdateRange(IEnumerable<Product> entities, out ITransactionResult<Product> transactionResult)
{
try
{
dbContext.UpdateRange(entities);
transactionResult = new SuccessTransaction<Product>(DisplayResourceType, "UpdatingRange_S");
return entities;
}
catch (Exception ex)
{
transactionResult = new FailedTransaction<Product>(DisplayResourceType, "UpdatingRange_F", ex);
return entities;
}
}
}
Category repo :
public class CategoryRepo : ICategoryRepo
{
private DbContext dbContext;
public Type DisplayResourceType { get; set; }
public CategoryRepo(DbContext dbContext)
{
this.dbContext = dbContext;
}
public IEnumerable<ProductCategory> GetAll() => GetAllAsQueryable().ToList();
public IQueryable<ProductCategory> GetAllAsQueryable() => dbContext.Set<ProductCategory>();
public ProductCategory GetById(long id) => dbContext.Find<ProductCategory>(id);
public ProductCategory Add(ProductCategory entity, out ITransactionResult<ProductCategory> transactionResult)
{
try
{
var res = dbContext.Add(entity);
transactionResult = new SuccessTransaction<ProductCategory>(DisplayResourceType, "Adding_S");
return res.Entity;
}
catch (Exception ex)
{
transactionResult = new FailedTransaction<ProductCategory>(DisplayResourceType, "Adding_F", ex);
return entity;
}
}
public IEnumerable<ProductCategory> AddRange(IEnumerable<ProductCategory> entites, out ITransactionResult<ProductCategory> transactionResult)
{
try
{
dbContext.AddRange(entites);
transactionResult = new SuccessTransaction<ProductCategory>(DisplayResourceType, "AddingRang_S");
return entites;
}
catch (Exception ex)
{
transactionResult = new FailedTransaction<ProductCategory>(DisplayResourceType, "AddingRange_F", ex);
return entites;
}
}
public ProductCategory Update(ProductCategory entity, out ITransactionResult<ProductCategory> transactionResult)
{
try
{
var res = dbContext.Update(entity);
transactionResult = new SuccessTransaction<ProductCategory>(DisplayResourceType, "Updating_S");
return res.Entity;
}
catch (Exception ex)
{
transactionResult = new FailedTransaction<ProductCategory>(DisplayResourceType, "Updating_F", ex);
return entity;
}
}
public IEnumerable<ProductCategory> UpdateRange(IEnumerable<ProductCategory> entities, out ITransactionResult<ProductCategory> transactionResult)
{
try
{
dbContext.UpdateRange(entities);
transactionResult = new SuccessTransaction<ProductCategory>(DisplayResourceType, "UpdatingRange_S");
return entities;
}
catch (Exception ex)
{
transactionResult = new FailedTransaction<ProductCategory>(DisplayResourceType, "UpdatingRange_F", ex);
return entities;
}
}
public void Remove(ProductCategory entity, out ITransactionResult<ProductCategory> transactionResult)
{
try
{
var res = dbContext.Remove(entity);
transactionResult = new SuccessTransaction<ProductCategory>(DisplayResourceType, "Removing_S");
}
catch (Exception ex)
{
transactionResult = new FailedTransaction<ProductCategory>(DisplayResourceType, "Removing_F", ex);
}
}
public void RemoveRange(IEnumerable<ProductCategory> entities, out ITransactionResult<ProductCategory> transactionResult)
{
try
{
dbContext.AddRange(entities);
transactionResult = new SuccessTransaction<ProductCategory>(DisplayResourceType, "RemovingRange_S");
}
catch (Exception ex)
{
transactionResult = new FailedTransaction<ProductCategory>(DisplayResourceType, "RemovingRange_F", ex);
}
}
public bool IsExist(long id, out ProductCategory entity)
{
entity = GetById(id);
return entity != null ? true : false;
}
}
I am trying to use Custom Generic class and interface.Im having problems to find a solution for the logic and code of my small Project.
THE BLUEPRINT INTERFACE
public interface IDepartment<T>
{
T CreateAndGetValues(SqlDataReader dataReader);
}
THE POSSIBLE DTO
public class Bank<T> : IDepartment<T>
{
public int LfdNr { get; set; }
public string Kennung { get; set; }
public string Name { get; set; }
public string Straße { get; set; }
public string PLZ { get; set; }
public string Ort { get; set; }
public DateTime TSCREATE { get; set; }
public DateTime TSUPDATE { get; set; }
public T CreateAndGetValues(SqlDataReader dataReader)
{
Bank<T> TheReturnObject = new Bank<T>();
ThereturnObject.LfdNr = Convert.ToInt32(dataReader.GetValue(0));
ThereturnObject.Kennung = dataReader.GetValue(1).ToString();
ThereturnObject.Name = dataReader.GetValue(2).ToString();
ThereturnObject.Straße = dataReader.GetValue(3).ToString();
ThereturnObject.PLZ = dataReader.GetValue(4).ToString();
ThereturnObject.Ort = dataReader.GetValue(5).ToString();
ThereturnObject.TSCREATE = DateTime.ParseExact(dataReader.GetValue(6).ToString().Trim(), "dd.MM.yyyy HH:mm:ss", System.Globalization.CultureInfo.InvariantCulture);
ThereturnObject.TSUPDATE = DateTime.ParseExact(dataReader.GetValue(7).ToString().Trim(), "dd.MM.yyyy HH:mm:ss", System.Globalization.CultureInfo.InvariantCulture);
return TheReturnObject;
}
}
THE GENERAL DATABASE READER.
public class Mssql<T> where T :class, IDepartment<T>, new()
{
public List<T> ReadTable()
{
string sqlcommand = "Select * from Bank; ";
List<T> TheListofObject = new List<T>();
using (SqlConnection cnn = new SqlConnection(connetionString))
{
cnn.Open();
using (SqlCommand command = new SqlCommand(sqlcommand, cnn))
{
SqlDataReader dataReader;
try
{
dataReader = command.ExecuteReader();
while (dataReader.Read())
{
IDepartment<T> Object = new T();
Object.CreateAndGetValues(dataReader);
TheListofObject.Add(Object);
}
return TheListofObject;
}
catch (Exception ex)
{
throw;
}
dataReader.Close();
}
cnn.Close();
}
}
}
THE MAIN PROGRAM
class Program
{
static void Main(string[] args)
{
Mssql<Bank<T>> TEST = new Mssql<Bank<T>>();
List<Bank<T>> TheList = TEST.ReadTable();
Console.ReadLine();
}
}
I am expecting that i can use a interface with a method where classes can inherit and return a new type of itself with the Type T and to pass the Type. So that the classes are not dependent that much to each other. And code can be reusable. I hope that is not confusing. Thanks
For this task I strongly recommend using an ORM. They are popular and mostly free. Stackoveflow runs on one of them.
Wikipedia's List of object-relational mapping software gives 9 .NET examples as of Oct 2019.
Here is an example from Dapper's doco.
public class Dog
{
public int? Age { get; set; }
public Guid Id { get; set; }
public string Name { get; set; }
public float? Weight { get; set; }
public int IgnoredProperty { get { return 1; } }
}
var guid = Guid.NewGuid();
var dog = connection.Query<Dog>("select Age = #Age, Id = #Id", new { Age = (int?)null, Id = guid });
If you add Dapper.Contrib you don't even need to write the query if you want all records.
public class Car
{
public int Id { get; set; } // Works by convention
public string Name { get; set; }
}
(...)
var cars = connection.GetAll<Car>();
Thank You for your time guys.
I have found an answer with out using any tool to my Problem. And I would like to share it.
//THE BLUEPRINT INTERFACE
public interface IDepartment
{
Object GetAndReadValues(SqlDataReader dataReader);
}
// The Possible DTO
public class Bank : IDepartment
{
public int LfdNr { get; set; }
public string Kennung { get; set; }
public string Name { get; set; }
public string Straße { get; set; }
public string PLZ { get; set; }
public string Ort { get; set; }
public DateTime TSCREATE { get; set; }
public DateTime TSUPDATE { get; set; }
public void CreateAndGetValues(SqlDataReader dataReader)
{
}
public Object GetAndReadValues(SqlDataReader dataReader)
{
Bank ThereturnObject = new Bank();
ThereturnObject.LfdNr = Convert.ToInt32(dataReader.GetValue(0));
ThereturnObject.Kennung = dataReader.GetValue(1).ToString();
ThereturnObject.Name = dataReader.GetValue(2).ToString();
ThereturnObject.Straße = dataReader.GetValue(3).ToString();
ThereturnObject.PLZ = dataReader.GetValue(4).ToString();
ThereturnObject.Ort = dataReader.GetValue(5).ToString();
ThereturnObject.TSCREATE = DateTime.ParseExact(dataReader.GetValue(6).ToString().Trim(), "dd.MM.yyyy HH:mm:ss", System.Globalization.CultureInfo.InvariantCulture);
ThereturnObject.TSUPDATE = DateTime.ParseExact(dataReader.GetValue(7).ToString().Trim(), "dd.MM.yyyy HH:mm:ss", System.Globalization.CultureInfo.InvariantCulture);
return ThereturnObject;
}
//THE GENERAL DATABASE READER.
public class Mssql<T> where T :class, IDepartment, new()
{
public List<T> ReadTable()
{
string sqlcommand = "Select * from Bank; ";
List<T> TheListofObject = new List<T>();
using (SqlConnection cnn = new SqlConnection(connetionString))
{
cnn.Open();
using (SqlCommand command = new SqlCommand(sqlcommand, cnn))
{
SqlDataReader dataReader;
try
{
dataReader = command.ExecuteReader();
while (dataReader.Read())
{
IDepartment Object = new T();
object ReturnObject = Object.GetAndReadValues(dataReader);
TheListofObject.Add((T)ReturnObject);
}
return TheListofObject;
}
//Error handling with th DB must be handeld correctly with the specific error
catch (Exception ex)
{
throw;
}
dataReader.Close();
}
cnn.Close();
}
}
}
//THE MAIN PROGRAM
class Program
{
static void Main(string[] args)
{
Mssql<Bank> TEST = new Mssql<Bank>();
List<Bank> TheList = TEST.ReadTable();
foreach (var item in TheList)
{
Console.WriteLine(item.LfdNr);
Console.WriteLine(item.Kennung);
Console.WriteLine(item.Name);
Console.WriteLine(item.Ort);
Console.WriteLine(item.PLZ);
Console.WriteLine(item.Straße);
Console.WriteLine(item.TSCREATE);
Console.WriteLine(item.TSUPDATE);
Console.WriteLine("");
Console.WriteLine("");
Console.WriteLine("");
}
Console.ReadLine();
}
}
I have 50 IMountCmd objects from one or more threads and drop them in a blocking collection. Each is executed and some get results or errors. They are put into a ConcurrentDictionary where I loop for ContainsKey and return the objects. Does this seems thread safe and correct way to process a blocking queue?
public class CmdGetAxisDegrees : IMountCommand
{
public long Id { get; }
public DateTime CreatedUtc { get; private set; }
public bool Successful { get; private set; }
public Exception Exception { get; private set; }
public dynamic Result { get; private set; }
private readonly AxisId _axis;
public CmdGetAxisDegrees(long id, AxisId axis)
{
Id = id;
_axis = axis;
CreatedUtc = HiResDateTime.UtcNow;
Successful = false;
Queues.AddCommand(this);
}
public void Execute(Actions actions)
{
try
{
Result = actions.GetAxisDegrees(_axis);
Successful = true;
}
catch (Exception e)
{
Successful = false;
Exception = e;
}
}
}
private static void ProcessCommandQueue(IMountCommand command)
{
command.Execute(_actions);
if (command.Id > 0)
{
_resultsDictionary.TryAdd(command.Id, command);
}
}
public static IMountCommand GetCommandResult(long id)
{
IMountCommand result;
while (true)
{
if (!_resultsDictionary.ContainsKey(id)) continue;
var success = _resultsDictionary.TryRemove(id, out result);
if (!success)
{
//log
}
break;
}
return result;
}
static Queues()
{
_actions = new Actions();
_resultsDictionary = new ConcurrentDictionary<long, IMountCommand>();
_commandBlockingCollection = new BlockingCollection<IMountCommand>();
Task.Factory.StartNew(() =>
{
foreach (var command in _commandBlockingCollection.GetConsumingEnumerable())
{
ProcessCommandQueue(command);
}
});
}
public interface IMountCommand
{
long Id { get; }
DateTime CreatedUtc { get; }
bool Successful { get; }
Exception Exception { get; }
dynamic Result { get; }
void Execute(Actions actions);
}
I have Entity Framework code which is supposed to be updating an entity called Asset. For some reason, it's not updating as it should. Debugging to see the root cause of this issue is difficult as I don't see any exceptions.
The problem is in the repository class where it always returns an
Asset could not be updated
message even if I pass in a valid Book or Asset object.
Model classes
public class Asset
{
public Guid Id { get; set; }
public string Name { get; set; }
}
public class Book : Asset
{
public string Isbn { get; set; }
public string Edition { get; set; }
public string Publisher { get; set; }
public virtual User User { get; set; }
}
Controller class
[System.Web.Http.Authorize]
public async Task<IHttpActionResult> Put([FromBody] BookViewModel model)
{
string result = null;
try
{
if (model.IsEmpty)
return BadRequest(ModelState);
result = await _assetAssetRepository.Update(Mapper.Map<Book>(model));
return Content(HttpStatusCode.Created, result);
}
catch (Exception ex)
{
return Content(HttpStatusCode.InternalServerError, result);
}
}
Repository class:
public class AssetRepository
{
private readonly GenAppContext _context = new GenAppContext();
public Task<string> Update(Asset asset)
{
try
{
var updateTask = Task<string>.Factory.StartNew(() =>
{
if (!(asset is Book))
return "Please return correct type of asset";
var _asset = _context.Assets.SingleOrDefault(x => x.Id == asset.Id);
if (_asset == null)
throw new ArgumentNullException(nameof(_asset));
_asset.Name = asset.Name;
((Book) _asset).Edition = ((Book) asset).Edition;
((Book) _asset).Publisher = ((Book) asset).Publisher;
((Book) _asset).Isbn = ((Book) asset).Isbn;
_context.Assets.AddOrUpdate(_asset);
return _context.SaveChanges() > 0
? "Asset has been updated successfully"
: "Asset could not be updated";
});
return updateTask;
}
catch (Exception ex)
{
return Task<string>.Factory.StartNew(() => ex.Message + " " + ex.StackTrace);
}
}
}
I have Web service and a method which makes a request to my table 'Customer' with Entity Framework.
I want to return the result of my request :
public List<Customer> MyMethod(int id)
{
Model model = new Model();
List<Customer> customer = new List<Customer>();
try
{
customer = model.Customer.Where(x => x.Id == id).ToList();
}
catch(Exception ex)
{
throw new System.NullReferenceException();
}
return customer;
}
In debug mode I have a list filled but when I call the method via SoapUI I have no answer.
Thank you
My full code :
Class Customer (generated by EF) :
[Table("Customer")]
public partial class Customer
{
public int Id { get; set; }
[StringLength(50)]
public string Name { get; set; }
public int? Adress_id { get; set; }
public virtual Adress Adress { get; set; }
}
Class Adress (generated by EF) :
[Table("Adress")]
public partial class Adress
{
public Adress()
{
Customer = new HashSet<Customer>();
}
public int Id { get; set; }
[Column("Adress")]
[StringLength(255)]
public string Adress1 { get; set; }
[StringLength(50)]
public string Town { get; set; }
public virtual ICollection<Customer> Customer{ get; set; }
}
My Web Method :
[WebMethod]
public Customer MyMethod(int id)
{
Model model = new Model();
Customer customer = new Customer();
try
{
customer = model.Customer.Where(x => x.Name == id).First();
}
catch (Exception ex)
{
throw new System.NullReferenceException(ex.Message, ex.InnerException);
}
return customer;
}
please refer to the following code:
public List<UserInfo> GetAllUserInfoDetailes()
{
List<UserInfo> user = new List<UserInfo>();
using (Model1 pubs = new Model1())
{
var userId = (from p in pubs.TB_UserInfo select p);
foreach (TB_UserInfo singleUser in userId)
{
UserInfo a = new UserInfo();
a.EUserId = singleUser.User_Id;
a.EUser_Name = singleUser.User_Name;
a.EAlias = singleUser.Alias;
a.EPassword = singleUser.Password;
a.EState = singleUser.State;
a.ECurrentPrevious = singleUser.CurrentPrevious;
a.EEmail = singleUser.Email;
a.EReportDutyTime = singleUser.ReportdutyTime;
a.ETeam_Id = singleUser.Team_Id;
a.ELogin_Time = DateTime.Now;
user.Add(a);
}
}
return user;
}
Then it can get the result.
I have a virtual property in my class Customer which corresponds to another table (foreign key). If I remove this virtual property, I have a reponse but I want to return the joint
Add WebMethod attribute to your method:
[WebMethod]
public List<Customer> MyMethod(int id)
{....}