LINQ 2 SQL Method for all tables - c#

My Title may be slightly off but here is what I am trying to do. I have a L2S Method that would be for every table that I would like to write once. This is to set a soft lock column where I will also need a Read and UnLock method. Here is what I have so far:
public static void LockRow(string TableName, int TablePrimaryKey)
{
using (var context = McpDataContext.Create())
{
var tableToLock = (from lockTable in context.tblPlans
where lockTable.PlanID == TablePrimaryKey
select lockTable).Single();
tableToLock.Locked = true;
context.SubmitChanges();
}
}
What I would like to do is replace context.tblPlans with context.TableName. Is this possible in LINQ? How so? I am assumming that I am going about it the wrong way so I'd be grateful for some direction/pointers.
Thanks

Update becuase the first example would not work.
You could do it with a generic method and an interface:
public interface IPlanTable
{
int PlanID { get; set; }
}
public static void LockRow<TEntity>(int TablePrimaryKey) where TEntity : class, IPlanTable
{
using (var context = McpDataContext.Create())
{
var tableToLock = (from lockTable in context.GetTable<TEntity>()
where lockTable.PlanID == TablePrimaryKey
select lockTable).Single();
tableToLock.Locked = true;
context.SubmitChanges();
}
}
You will also have to use the fact that the Linw2SQL tables are created as partial classes to extend them so all the relevent table implement IPlanTable
You would use it like below:
LockRow<tblPlan>(23);
simply replace tblPlan with whatever the name of your table class is.
However this won't allow you to set the table at runtime, LinqToSQL is object orientated and type safe, specifying the table you want to retreive is contrary to how it si designed to work.

Related

EntityFramework, how to decide which dbSet<TEntity> to use with switch/case?

I'm a beginner with EntityFramework and WCF, so I don't yet know how everything works, so I'll try to explain my situation as best as I can..
I have WCF service that uses a database with EntityFramework and I have managed to get it to work just fine for example like this:
using (var entities = new databaseEntities())
{
// Check if there is 0 rows, then just add the new row.
int count = entities.Table1.Where(i => i.Name == name).Count();
if (count < 1)
{
var newEntry = new Table1
{
Name = name,
InsertDate = DateTime.Now,
CreatedBy = createdBy,
Comment = comment,
Active = true
};
entities.Table1.Add(newEntry);
entities.SaveChanges();
}
}
The problem arrives when I have more than one table and I want to decide which one to use. The tables are basically the same and thus would use the same operations, so I would like to use one function for all of them (that way I can avoid duplicate code). But I can't seem to understand how I can change the table at runtime for example via a switch/case.
For Example:
// A function that gets the type of the table I want to access
void WriteToSomeTable(int type)
{
switch (type)
{
case 0:
//The table to update is Table1
break;
case 1:
//The table to update is Table2
break;
}
}
If I want to get the count of all the entries with a given name with
int count = entities.Table1.Where(i => i.Name == "somename").Count();
how can I make the "entities.Table1" determined at runtime?
I can make variables:
System.Data.Entity.DbSet<Table1> firstTable = entities.Table1;
System.Data.Entity.DbSet<Table2> secondTable = entities.Table2;
So I thought that with a list i could set an int index; to a different value with the switch/case and then just use
int count = list[index].Where(i => i.Name == "somename").Count();
but I can't add them to a List, since they are different types
// entity.Table1 is
System.Data.Entity.DbSet<Table1>
// and entity.Table2 is
System.Data.Entity.DbSet<Table2>
ArrayList won't cut it either, since there are no ".Where" function if i try to use the objects inside the ArrayList.
I also tried with just the System.Data.Entity.Dbset, but to use the ".Where" function, I would need to use .Cast() function, but I cant store the needed "TEntity" to a variable (or can I?). For Example:
System.Data.Entity.DbSet firstTable = entity.Table1
Type t = firstTable.GetType();
int count = firstTable.Cast<t>().Where(i => i.Name == "somename").Count();//doesn't work
//This, however works:
int count = firstTable.Cast<Table1>().Where(i => i.Name == "somename").Count();
I hope I made some sense on what my problem here is :) Hopefully someone has an idea, how to solve this, since I have battled with this for ages now, and the only solution I have come up with is to have a separate function call in each switch/case with the exact same code, except for the "entity.Table" part. And having to write the same set of code multiple times isn't a very good solution :(
Make generic function...
public void MyAddFunction<T>(T newItem, databaseEntities db,
Expression<Func<T, bool>> predicate) where T : class
{
var table = db.Set<T>();
int count = table.Count(predicate);
if(count < 1)
{
table.Add(newItem);
}
db.SaveChanges();
}
and according to your cases call your function(You want to add table1 for example)
using(var entities = new databaseEntities())
{
MyAddFunction<Table1>(new Table1(), entities , x => x.Name == "name");
}
You don't say so, but since you are working with Entity Framework, I assume that your class databaseEntities is the DbContext that contains all your Entities as DbSet<TEntity> properties.
You write that your problem is that you know the type of the entities (in my exaple TEntity), but you don't know how to get the corresponding DbSet. Your proposal is to use a switch statement for this.
Luckily this is not needed. DbContext.Set(Type) does this for you. You provide the Type, Dbcontext returns the DbSet of this type.
public class SchoolContext : DbContext
{
public DbSet<Student> Students {get; set;}
public DbSet<Teacher> Teachers {get; set;}
public DbSet<ClassRoom> ClassRooms {get; set;}
...
}
If you know the type already at compile time, use DbContext.Set<TEntity>, if TEntity is only known at runtime, use DbContext.Set(Type entityType)`
Type entityType = ...;
DbSet mySet = DbContext.Set(entityType);
The problem is of course, that at compile time you don't know the entityType, and thus don't know what functions you can call and what properties your entityType has.
If you are certain that your entities has certain properties, like in your example property Name, consider deriving all your entities from a common interface. Like this:
interface ICommonSchoolProperties
{
public int Id {get;} // primary key
public string Name {get;}
}
public class Teacher : ICommonSchoolProperties {...}
public class ClassRoom : ICommonSchoolProperties {...}
etc.
Now you are certain, that whenever you ask the SchoolContext for an item of any type, you are certain that the items you get have at least an Id and a Name. And thus you can do the following:
Type entityType = ...
IEnumerable<ICommonSchoolProperties> schoolItems = mySchoolContext
.Set(entityType)
.Cast<ICommonSchoolProperties>)();
And you can call the functions of which you are certain that your SchoolProperty has.
var nrOfItems = schoolItems
.Where(schoolItem => schoolItem.Name = ...)
.Count();
var allKnownNames = schoolItems
.Select(schoolItem => schoolItem.Name)
.Distinct();
The problem remains if you want to call functions that Teachers have, but ClassRooms have not.
This is a fairly rare case, and if you have an object of which you don't know what functions it has, you should reconsider your design and think about creating functions that handle these objects, instead of giving the type, decoding what functions this object has and then use them
Instead of:
private void WriteToSomeTable(Type type)
{
if type is a teacher
call teacher functions
else if type is classroom
call classroomfunctions
else
DoSomeThingcommon(...)
}
Consider:
public void WriteTeacherTable()
{
call teacher functions();
DoSomethingCommon();
}
private void WriteClassRoomtable()
{
call classroom functions;
DoSomethingCommon();
}
Note that the number of lines hardly increase.
Somewhere inside your program there is a place where you know that you are dealing with Teachers instead of ClassRooms. The only reason in a proper OO design where you would mix Teachers and ClassRooms as if they were something similar would be if you know you only want to call the functions they have in common. If that is the case, go back to the interface function where you know which common functions you can call.

Insert again elements from one list to the prior list

Here is my situation. I have 2 list of the same type. Imagine the names like these. FullList and ElementsRemoved. So in order to avoid the database roundtrip, anytime I delete an element from the fulllist I added to the list of ElementsRemoved in case of regret's user so he can revert the deletion.
I was thinking to loop inside my ElementsRemoved to insert them again into the FullList from where initially were removed.
There is any way to do this as simple with List Methods.
Something like
FullList.Insert, Add, ..... (x =>
in order to reduce line code and optimized?
Instead of deleting the item from your database consider using a flag in the table.
For example consider this entities table (written in TSQL):
CREATE TABLE Entity
(
Id INT IDENTITY PRIMARY KEY
,Name NVARCHAR(20) NOT NULL
,IsDelete BIT NOT NULL DEFAULT 0
);
This way you can set the IsDelete bit when the user deletes the entity which will prevent the data from being lost. The data can be pruned on a job in the off hours.
The would lead to only needing one list instead of keeping track of two lists.
public class Entity
{
public int Id { get; set; }
public string Name { get; set; }
public bool IsDelete { get; set; }
}
public static void UndoDelete(IEnumerable<Entity> fullList, int[] removedIds)
{
foreach(var entity in fullList.Where(e => removedIds.Contains(e.Id)))
{
entity.IsDelete = false;
}
}
In case you cannot modify your application.
You can simply add the entities back in.
See List(T).AddRange
var entitiesToAdd = new[] { 2, 3, 4 };
var entitiesToInsert = ElementsRemoved.Where(e => entitiesToAdd.Contains(e.Id));
FullList.AddRange(entitiesToInsert);
In your front end make a class that holds a bool and your object:
public class DelPair<T>{
public bool IsDeleted{get;set;}
public T Item{get;set;}
}
Now instead of using a list of objects use a list of DelPair<YourClass> and set IsDeleted=true when deleting.
This pattern will also allow you to track other things, such as IsModified if it comes to that.
Based on OP comment that he's using an ENTITY class and needs it to function as such:
One option is to make your DelPair class inherit ENTITY. Another may be to put implicit casting operator:
...
// not exactly sure about the signature, trial/error should do :)
public static implicit operator T(DelPair<T> pair)
{
return pair.Item;
}
Suppose you have an element having a field id which uniquely identifies it.
class Element{public int id;}
In that case you can do this
FullList.Add(ElementsRemoved.FirstOrDefault(e=>e.id==id));
In case you want to add all elements use AddRange
FullList.AddRange(ElementsRemoved);
You can use the AddRange method
FullList.AddRange(ElementsRemoved);
But consider doing this
public class YourClass
{
public string AnyValue{get;set;}
public bool IsDeleted{get;set;}
}
And you have list like this List < YourClass> FullList. Now whenever user removes any item you just set the
IsDeleted = true
of the item that is removed. This will help you in keeping just one list and adding removing from the list

LINQ against two different data contexts using interfaces

This is the continuation of my question asked here.
Brief summary: I have two different databases with minimal changes (one table and foreign keys to it are missing in one table) and I want my import utility that uses Linq-To-Sql to be able to populate both databases with data without duplication of the logic.
My first approach was to use dynamic storing two different database contexts in a single variable, but this approach did not work and I was recommended to use interfaces for this purpose.
Now I bumped into the following problem:
I correctly extracted interfaces from my db context class:
public interface IDataContext
{
System.Data.Linq.Table<IFieldCollection> FieldCollections { get; }
System.Data.Linq.Table<IField> Fields { get; }
}
... but in order to be able to implement the interface by two different database context classes I had to substitute the actual LINQ classes (FieldCollection, Field) with interfaces as well.
Now I have problems with returning Table<IFieldCollection> or Table<IField> in my class implementation. The auto-generated code for the db context class is the following:
public System.Data.Linq.Table<FieldCollection> FieldCollections
{
get
{
return this.GetTable<FieldCollection>();
}
}
So, in order to implement IDataContext here I need to change the returning value to Table<IFieldCollection>. How can I then cast Table<FieldCollection> to Table<IFieldCollection> in my property getter without retrieving the complete table from the database?
In my testapp the following seems to work.
Define your interface like this:
public interface IDataContext {
IQueryable<IFieldCollection> FieldCollections { get; }
IQueryable<IField> Fields { get; }
}
Update your datacontexts with the following implementation:
public partial class DataContext1: IDataContext {
IQueryable<IFieldCollection> IDataContext.FieldCollections {
get { return FieldCollections; }
}
IQueryable<IField> IDataContext.Fields {
get { return Fields; }
}
}
Now, if you query for example, using a .Where(<predicate>).FirstOrDefault() then the query is compiled correctly, with a corresponding WHERE in SQL.
using (IDataContext context = MyDataContextFactory.GetInstance() /* or something else */) {
var fieldsCount = context.Fields.Where(x => x.Id == 1).Count();
// The predicate Id == 1 and Count() are translated to sql
}

Extend Entity Object to include a calculated property

Let's say I have an Entity Object 'Jewels' that has the properties 'Name' and 'Birthdate'.
I want to implement a LINQ query that returns an object that has 'Name', 'Birthdate' and 'Birthstone'. So I extend 'Jewels' like this:
public partial class JewelStones : Jewels
string Birthstone = null;
public void JewelsWithStone()
{
this.Birthstone = "diamond";
//(we figure out what stone applies to the month here)
}
I can get this far, and I THINK I'm on the right track, but I don't know how to write a LINQ query and get back an object that includes Birthstone, so I can bind that object to a grid that will show Birthstone, which I'm not storing anywhere, as it's always calculated (this is pretend data, sorry if it's not logical).
List<Jewel> jewels = new List<Jewel>;
using (jewelentities db = new jewelentities())
{
jewels = (from j in db.Jewels select j).ToList();
}
How do I fill up my JewelStone object with Name, Birthdate, and Birthstone?
If I'm not following best practice here, please let me know!
EDIT
I've tried adding a partial class to the Entity partial class. When I reference the Jewel class now, it 'sees' the Birthstone property, but it is null. I don't know why? Here is the partial class:
public partial class Jewel
{
private string _birthstone;
public string Birthstone
{
get { return _birthstone; }
set
{
JewelBusiness jewelBusiness = new JewelBusiness();
_birthstone = jewelBusiness.RequestBirthstone(birthmonth);
}
}
}
If I use LINQ to query the entity to get a list of Jewel records, I get all the info from the Entity, Jewel.Birthstone is there, but it is null. However if I do a foreach on the results ---
foreach (Jewel j in jewels)
{
string stone = jewelBusiness.RequestBirthstone(j.Birthmonth);
}
stone will equal the expected result (birthstone for that month).
Why doesn't my partial class return the birthstone??
I'm not sure I understand your requirement correctly. But if you don't want to store Birthstone but calculate it on the fly, just change your code to
public partial class Jewel
{
private string _birthstone;
public string Birthstone
{
get
{
if (_birthstone == null)
{
JewelBusiness jewelBusiness = new JewelBusiness();
_birthstone = jewelBusiness.RequestBirthstone(birthmonth);
}
return _birthstone;
}
}
}
Isn't your Jewels EntityObject in a partial class too? You can most likely just add a Jewels partial class to "extend" it and add the wanted property there.
For me, it depends on where the logic for the calculated column resides.
If it resides in database, then you must do join query in the Linq. I assume in this case, you has a table named BirthStoneTable, with the month as the relation. I don't suggest to add a ternary operation inside linq query, such as select j.BirthDate.Month == 1 ? "Diamond" : //etc etc. It is hard to debug and to track (moreover for code coverage reason).
If it resides in UI specific (only to improve the display), I usually add a type-casted class, such as:
public class JewelUI{
public explicit operator JewelUI(Jewel jewel){
JewelUI jewelUI = new JewelUI();
// assign birthdate and name
jewelUI.BirthStone = GetBirthStone(jewel.BirthDate.Month);
}
public string BirthStone{get;set;};
public string GetBirthStone(int month){
if(month == 1) return "Diamond";
//etc etc
}
}
If the calculated column is used in the business logic, usually I handle the calculation in service / business logic. All of it to ensure the good Separation of Concern.
NB: I may misunderstand your requirement though

cast Table<T> to something

I have a datacontext, and it has Authors table.
public partial Author:IProductTag{}
I want to cast Table<Authors> object to Table<IProductTag>, but that appears to be impossible. I am trying to do that because I want my method to be able to work with different tables which come as input parameters. To be more specific, I need to execute OrderBy and Select methods of the table. I have few other tables, entities of which implement IProductTag . Also, I tried to write a function like:
public static void MyF<t>(){
Table<t> t0 = (Table<t>)DataContext.GetMyTableUsingReflection();
}
But it fails at compile-time. And if I cast the table to something like ITable or IQueriable, then the OrderBy and Select functions simply don't work. So how do you deal with it?
I suspect you want to make your method generic - so instead of
void DoSomethingWithTable(Table<IProductTag> table)
you should have
void DoSomethingWithTable<T>(Table<T> table) where T : class, IProductTag
That should work fine, assuming you only need to read entities (and apply query operators etc). If that doesn't work for you, please give more details about what your method needs to do.
(You say that your attempt to use reflection failed, but you haven't said in what way it failed. Could you give more details?)
I have no idea what a ProductTag is so I've used different types to show my solution to this problem. Yes there doesn't seem to be a way to get a Table<T>, but you can get IQueryable<T> which works just as well (at least for my situation).
I have a simple analytics database, where each website has its own table containing both generic and specific items. I wanted to use an interface for the shared data.
public interface ISession
{
public DateTime CreateDt {get; set; }
public string HostAddress {get; set; }
public int SessionDuration {get; set; }
}
public static IQueryable<ISession> GetQueryableTable(MyDataContext db, string site)
{
Type itemType;
switch (item)
{
case "stackoverflow.com":
itemType = typeof(Analytics_StackOverflow);
break;
case "serverfault.com":
itemType = typeof(Analytics_ServerFault);
break;
default: throw Exception();
}
return db.GetTable(itemType).Cast<ISession>();
}
You can then do a query like this :
var table = GetQueryableTable(db, "stackoverflow.com");
var mySessions = table.Where(s => s.HostAddress == MY_IP);
To create a new row you can use reflection :
var rowType = typeof(Analytics_ServerFault);
var newRow = (ISession) rowType.GetConstructor(new Type[0]).Invoke(new object[0]);
(I have a function to get GetRowType - which is not shown here).
Then to insert into the table I have a separate helper function:
public static void Insert(MyDataContext db, ISession item)
{
// GetTable is defined by Linq2Sql
db.GetTable(GetRowType(domain)).InsertOnSubmit(item);
}

Categories