Duplicate entries - linq-to-sql

I have created a universalrepository that takes the type passed to it and when I create data entry method, the entity is created fine, but when I create a linked entity to it, i get the base entity created again. Any ideas why?
Details..
I have divided a specification into multiple tables to manage stuff...
Now I have got a person entity, an applicant entity...(in reality applicant and person are the same), a contractor entity. A contractor can only be created by an applicant and therefore an applicant will always be created and therefore a person will always be created.
When I go on creating a person, it creates a person fine, but when I create an applicant it creates a person again. Likewise when I create a contractor it creates a person and multiple applicants for some reason.
Here is my LINQ to SQL. If you notice in anyway I can improve this code, I will appreciate that too.
here is the repository
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Linq;
namespace ParkingPermit.Models
{
public class UniversalManagerRepository<T> :IRepositoryT<T>
where T:class
{
private Table<T> _table;
private readonly DB _db ;//= new DB();
public UniversalManagerRepository()
{
_db = new DB();
_table = _db.GetTable<T>();
}
#region IRepositoryT<T> Members
public T Create(T create)
{
// _table = new DB().GetTable<T>();
//_db.GetTable(typeof(T)).InsertOnSubmit(create);
_table.InsertOnSubmit(create);
Save();
return create;
}
public void Delete(T delete)
{
throw new NotImplementedException();
}
public T Edit(T edit)
{
throw new NotImplementedException();
}
public T GetItem(int id)
{
throw new NotImplementedException();
}
public T Update(T update)
{
throw new NotImplementedException();
}
public IEnumerable<T> List()
{
//IQueryable i = _db.GetTable(typeof(T)).AsQueryable() ;
return _db.GetTable(typeof(T)) as IEnumerable<T>;
//throw new NotImplementedException();
}
public void Save()
{
//_db.SubmitChanges();
_table.Context.SubmitChanges();
//throw new NotImplementedException();
}
#endregion
}
}
I can post an image of the linq to sql designer if that helps, but I cant see the feature here...
Many thanksalt text http://img509.imageshack.us/img509/2072/linq.jpg
the thing is that when applicant is added and an applicant.Person is assigned from the session(in model binder), it creates a new person, which is actually the original person created in the beginning. How can I avoid that.
protected override void OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var personType = (Person)controllerContext.HttpContext.Session[PersonSessionKey];
controllerContext.HttpContext.Session[CurrentApplicantSessionKey] = null;
var av = new ApplicantValidator(new ModelStateWrapper(bindingContext.ModelState));
var newApplicant = bindingContext.Model as Applicant;
if (personType == null)
{
bindingContext.ModelState.AddModelError(bindingContext.ModelName,
"Cannot Update this Instance directly, please restart the application");
// controllerContext.HttpContext.Session[PersonSessionKey] = personType;
}
else if (newApplicant != null)
{
if (newApplicant.Person != null)
{
if (newApplicant.Person.Equals(personType as Person))
{
bindingContext.ModelState.AddModelError(bindingContext.ModelName,
"A person with these details already exists, please restart the application...");
//return
controllerContext.HttpContext.Session[PersonSessionKey] = null;
personType = null;
}
}
else if (av.Validate(newApplicant))
{
if (newApplicant.Person == null)
{
newApplicant.Person = personType as Person;
newApplicant.PersonId = personType.PersonId;
}
}
}
}

I have resolved this part and apparently its now giving issued with update, can anbody find anything unusual.
Answer to my first problem, was that in Model Binders the entity is being manipulated from sessions and the created back to the service layer.
Apparently it seems that because its all happening outside linq orm framework, this entity needs to be recreated as "From clause ...from ..in db." and then linq correctly recognizes it and does the correct job of insertion.
Can anyone help me with the update/edit..please

Related

Adding a Schema Prefix to a Table using XML Mapping - needed to convert a MSSQL Database to a MySQL

I have NHibernate XML mapping files that work wonderfully in MSSQL Databases. An example of a table is:
<class name="Worm" table="`Worms`" schema="`dbo`">
Now I need to use the exact same mapping file (unchanged) to generate a MariaDB (or MySQL) database. Clearly, such databases do not have schemas. So, what I'm trying to do is to create a naming convention so that the 'schema' becomes the prefix of a table, e.g. 'dbo_Worm'.
I've tried using the
var schemaUpdate = new NHibernate.Tool.hbm2ddl.SchemaUpdate(configuration);
by adding a custom Naming Strategy class into the 'configuration'. For now my custom class does nothing: just throws NotImplementedExceptions():
public class MyCustomNamingStrategy : INamingStrategy
{
public static MyCustomNamingStrategy Instance => new MyCustomNamingStrategy();
public string ClassToTableName(string className)
{
throw new NotImplementedException();
}
public string PropertyToColumnName(string propertyName)
{
throw new NotImplementedException();
}
public string TableName(string tableName)
{
throw new NotImplementedException();
}
public string ColumnName(string columnName)
{
throw new NotImplementedException();
}
public string PropertyToTableName(string className, string propertyName)
{
throw new NotImplementedException();
}
public string LogicalColumnName(string columnName, string propertyName)
{
throw new NotImplementedException();
}
}
The reasons are two:
I've never reached the breakpoints of my MyCustomNamingStrategy
class to begin with, so I don't even know if this is the way to go.
Will it give me any information with regards to the 'schema'? I
don't know...
The code that calls the SchemaUpdate of the tool completely ignores the custom naming strategy and a MySQL Exception is thrown
stating that no 'dbo' database is found (duh....)
Having tried everything and searched everywhere I'm turning to you for assistance.
Can anyone help me
Keep the exact same XML Mapping File, yet
Produce tables prefixed with their schema names ?
Any hints would be greatly appreciated!
Finally found a solution:
public override void RemoveSchemas(NHibernate.Cfg.Configuration configuration)
{
foreach (var clsMapping in configuration.ClassMappings)
{
clsMapping.Table.Schema = null;
if ((clsMapping as NHibernate.Mapping.RootClass) != null) (clsMapping as NHibernate.Mapping.RootClass).CacheRegionName = null;
if (clsMapping.IdentityTable != null)
{
clsMapping.IdentityTable.Schema = null;
var identifier = clsMapping.IdentityTable.IdentifierValue as NHibernate.Mapping.SimpleValue;
if (identifier != null)
{
if(identifier?.IdentifierGeneratorProperties?.ContainsKey("schema") == true)
{
identifier.IdentifierGeneratorProperties["schema"] = null;
}
}
}
}
foreach (var colMapping in configuration.CollectionMappings)
{
colMapping.Table.Schema = null;
if (colMapping.CollectionTable != null) colMapping.CollectionTable.Schema = null;
colMapping.CacheRegionName = null;
}
}

Retrieving data from the same database with the Identity

I connected the identity model of my WebApp to my MySQL Database, and modified it like I want to connect.
My question is the next: If I want to get data from tables not used in the Identity but still from the same database, What is the correct way to do it? Should I open a new connection and get the data with a class? Is there a way to implement it in the model?
I attach the code to connect to the database:
public void Configuration(IAppBuilder app)
{
DbConfiguration.SetConfiguration(new MySqlEFConfiguration());
ConfigureAuth(app);
context = new ApplicationDbContext();
}
And my connectionStrings:
<connectionStrings>
<add name="DefaultConnection" connectionString="server=x.x.x.x;uid=user;password=pwd;database=db;port=3306;charset=utf8" providerName="MySql.Data.MySqlClient" />
</connectionStrings>
(Sorry if the question is too obvious, I'm starting to develop apps in ASP.NET MVC)
Yes, all you need to do is open the connection when it is required.
So, i assume you did something like this with your MySQL Database connections
public class MySqlInitializer : IDatabaseInitializer<ApplicationDbContext>
{
public void InitializeDatabase(ApplicationDbContext context)
{
if (!context.Database.Exists())
{
// if database did not exist before - create it
context.Database.Create();
}
else
{
// query to check if MigrationHistory table is present in the database
var migrationHistoryTableExists = ((IObjectContextAdapter)context).ObjectContext.ExecuteStoreQuery<int>(
string.Format("SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = '{0}' AND table_name = '__MigrationHistory'",
"[Insert your database schema here - such as 'users']"));
// if MigrationHistory table is not there (which is the case first time we run) - create it
if (migrationHistoryTableExists.FirstOrDefault() == 0)
{
context.Database.Delete();
context.Database.Create();
}
}
}}
and your ApplicationDbContext class will be like this
public class ApplicationUser : IdentityUser{}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
static ApplicationDbContext()
{
Database.SetInitializer(new MySqlInitializer());
}
public ApplicationDbContext()
: base("DefaultConnection")
{
}
}
Now, to do some queries from database, you need to create an object of ApplicationDbContext
So, your Next Question "Is there a way to implement it in the model?" Yes there is you need to Create a View Model.
public class ItemProduct
{
public int Id {get; set;};
public string Name {get; set;};
}
So, lets say a Product Table exists in your database and we can query on it.
public class HomeController : Controller
{
ApplicationDbContext context = new ApplicationDbContext();
public ActionResult AnyName()
{
List<ItemProduct> lstProducts = new List<ItemProduct>();
var getProducts = (from p in context.Products
select p).ToList();
foreach(var item in getProducts)
{
ItemProduct pro = new ItemProduct()
{
pro.Id = item.Id,
pro.Name = item.Name
};
lstProducts.Add(pro);
}
// So, you have all your Products in ItemProduct Class list.
// Now, return lstProducts to your View to show it on front end
}
}
Hope this answer your question :)

ASP.NET MVC LINQ-2-SQL as model - how to update?

Is it possible to use LINQ2SQL as MVC model and bind? - Since L2S "attachement" problems are really showstopping.
[HttpPost]
public ActionResult Save(ItemCart edCart)
{
using (DataContext DB = new DataContext())
{
DB.Carts.Attach(edCart);
DB.Carts.Context.Refresh(RefreshMode.KeepChanges, edCart);
DB.Carts.Context.SubmitChanges();
DB.SubmitChanges();
}
return RedirectToAction("Index");
}
That does not work. :S
What does your Save View look like?
You can't just attach a new item to the EntitySet like that. -> Attaching requires a lot of checks and it is a real pain to implement. I tried it myself and didn't like it at all.
In your [HttpPost] method you'll need to update the model before you can save it:
[HttpPost]
public ActionResult Save(int id, ItemCart edCart) {
DataContext DB = new DataContext(); // I'm doing this without a using keyword for cleanliness
var originalCart = DB.Carts.SingleOrDefault(c => c.ID == id); // First you need to get the old database entry
if (ModelState.IsValid & TryUpdateModel(edCart, "Cart")) { // This is where the magic happens.
// Save New Instance
DB.SubmitChanges.
return RedirectToAction("Details", new { id = originalCart.ID });
} else {
// Invalid - redisplay with errors
return View(edCart);
}
}
It tries to update the model from the controllers valueprovider using they "Cart" prefix.

Model binding issue in LINQ to SQL

I am just now starting to work with LINQ, and am pretty familiar with MVC. I have a strongly typed view that is updating a record. I have successfully done a creation:
This works fine, and creates a record in the database:
public ActionResult Create(TABLEMODEL tableModel)
{
DBDataContext db = new DBDataContext();
if (ModelState.IsValid)
{
db.TABLEMODEL.InsertOnSubmit(tableModel);
db.SubmitChanges();
}
}
But when trying to update:
public ActionResult Manage(TABLEMODEL tableModel)
{
DBDataContext db = new DBDataContext();
if (ModelState.IsValid)
{
db.SubmitChanges();
}
}
This fails, in the sense that it does not update the record in the database. No actual error/exception occurs, and I can step through it just fine.
I am sure I am missing something, but cannot find what. I appreciate any help on this matter.
UPDATE
I did notice that if I get a record using the DataContext:
DBDataContext db = new DBDataContext();
var m = db.TABLEMODELs.Single(m => m.ID == 1);
m.Name = "UpdatedName";
db.SubmitChanges();
This does update, so I assume I am somehow not binding from my model to the LINQ context.
My Solution
I found that you need to retrieve the object and then update that with the form. Simple enough.
[HttpPost]
public ActionResult Manage(int ID, FormCollection form)
{
DBSDataContext db = new DBSDataContext();
var t= db.TABLEMODELs.Single(b => b.ID == ID);
UpdateModel(t);
if (ModelState.IsValid)
{
db.SubmitChanges();
}
return View(t);
}
You should re-query the original tableModel, map the updated row and then update.
Perhaps something like this (example only, not knowing anything about your schema):
var originalTableModel = db.GetById( tableModel.Id);
originalTableModel.FirstName = tableModel.FirstName;
db.SubmitChanges();

LINQ to SQL validate all fields, not just stop at first failed field

I just started using LINQ to SQL classes, and really like how this helps me write readable code.
In the documentation, typical examples state that to do custom validation, you create a partial class as so::
partial class Customer
{
partial void OnCustomerIDChanging(string value)
{
if (value=="BADVALUE") throw new NotImplementedException("CustomerID Invalid");
}
}
And similarly for other fields...
And then in the codebehind, i put something like this to display the error message and keep the user on same page so to correct the mistake.
public void CustomerListView_OnItemInserted(object sender, ListViewInsertedEventArgs e)
{
string errorString = "";
if (e.Exception != null)
{
e.KeepInInsertMode = true;
errorString += e.Exception.Message;
e.ExceptionHandled = true;
}
else errorString += "Successfully inserted Customer Data" + "\n";
errorMessage.Text = errorString;
}
Okay, that's easy, but then it stops validating the rest of the fields as soon as the first Exception is thrown!! Mean if the user made mode than one mistake, she/he/it will only be notified of the first error.
Is there another way to check all the input and show the errors in each ?
Any suggestions appreciated, thanks.
This looks like a job for the Enterprise Library Validation Application Block (VAB). VAB has been designed to return all errors. Besides this, it doesn't thrown an exception, so you can simply ask it to validate the type for you.
When you decide to use the VAB, I advise you to -not- use the OnXXXChanging and OnValidate methods of LINQ to SQL. It's best to override the SubmitChange(ConflictMode) method on the DataContext class to call into VAB's validation API. This keeps your validation logic out of your business entities, which keeps your entities clean.
Look at the following example:
public partial class NorthwindDataContext
{
public ValidationResult[] Validate()
{
return invalidResults = (
from entity in this.GetChangedEntities()
let type = entity.GetType()
let validator = ValidationFactory.CreateValidator(type)
let results = validator.Validate(entity)
where !results.IsValid
from result in results
select result).ToArray();
}
public override void SubmitChanges(ConflictMode failureMode)
{
ValidationResult[] this.Validate();
if (invalidResults.Length > 0)
{
// You should define this exception type
throw new ValidationException(invalidResults);
}
base.SubmitChanges(failureMode);
}
private IEnumerable<object> GetChangedEntities()
{
ChangeSet changes = this.GetChangeSet();
return changes.Inserts.Concat(changes.Updates);
}
}
[Serializable]
public class ValidationException : Exception
{
public ValidationException(IEnumerable<ValidationResult> results)
: base("There are validation errors.")
{
this.Results = new ReadOnlyCollection<ValidationResult>(
results.ToArray());
}
public ReadOnlyCollection<ValidationResult> Results
{
get; private set;
}
}
Calling the Validate() method will return a collection of all errors, but rather than calling Validate(), I'd simply call SubmitChanges() when you're ready to persist. SubmitChanges() will now check for errors and throw an exception when one of the entities is invalid. Because the list of errors is sent to the ValidationException, you can iterate over the errors higher up the call stack, and present them to the user, as follows:
try
{
db.SubmitChanges();
}
catch (ValidationException vex)
{
ShowErrors(vex.ValidationErrors);
}
private static void ShowErrors(IEnumerable<ValidationResult> errors)
{
foreach(var error in errors)
{
Console.WriteLine("{0}: {1}", error.Key, error.message);
}
}
When you use this approach you make sure that your entities are always validated before saving them to the database
Here is a good article that explains how to integrate VAB with LINQ to SQL. You should definitely read it if you want to use VAB with LINQ to SQL.
Not with LINQ. Presumably you would validate the input before giving it to LINQ.
What you're seeing is natural behaviour with exceptions.
I figured it out. Instead of throwing an exception at first failed validation, i store an error message in a class with static variable. to do this, i extend the DataContext class like this::
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
/// <summary>
/// Summary description for SalesClassesDataContext
/// </summary>
public partial class SalesClassesDataContext
{
public class ErrorBox
{
private static List<string> Messages = new List<string>();
public void addMessage(string message)
{
Messages.Add(message);
}
public List<string> getMessages()
{
return Messages;
}
}
}
in the classes corresponding to each table, i would inherit the newly defined class like this::
public partial class Customer : SalesClassesDataContext.ErrorBox
only in the function OnValidate i would throw an exception in case the number of errors is not 0. Hence not attempting to insert, and keeping the user on same input page, without loosing the data they entered.